shlab
作者:互联网
深入理解计算机系统ch8---shlab
感觉不熟悉异常控制流中章节中的内容时,写起来比较费劲,代码量不大但是要理清楚什么时候该阻塞信号,什么时候该取消阻塞。
# eval函数主要参照书中的方式
void eval(char *cmdline)
{
char *argv[MAXARGS];
char buf[MAXLINE]; //这个buf感觉可以不需要
int bg;
pid_t pid;
sigset_t mask_all, mask_one, prev;
sigfillset(&mask_all);
sigemptyset(&mask_one);
sigaddset(&mask_one, SIGCHLD);
strcpy(buf, cmdline);
bg = parseline(buf, argv);
if (argv[0] == NULL)
return;
if (!builtin_cmd(argv)) { //如果不是内置命令
sigprocmask(SIG_BLOCK, &mask_one, &prev);
if ((pid = fork()) < 0)
unix_error("fork error");
if (pid == 0) { /* child process run the jobs */
sigprocmask(SIG_SETMASK, &prev, NULL);
setpgid(0, 0); //这里将每个Job设置成单独一个组,且组ID等于其PID,后面发送信号的时候就比较方便
if (execve(argv[0], argv, environ) < 0) {
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}
sigprocmask(SIG_BLOCK, &mask_all, NULL);
/* father process wait for foreground job to terminate */
if (!bg) {
addjob(jobs, pid, FG, buf);
sigprocmask(SIG_SETMASK, &prev, NULL); //这里解除阻塞,我的想法是防止waitfg函数执行时,因为信号的阻塞导致循环的条件一直成立从而
/* wait */
waitfg(pid);
} else {
addjob(jobs, pid, BG, buf);
int jid = pid2jid(pid);
printf("[%d] (%d) %s", jid, pid, buf);
sigprocmask(SIG_SETMASK, &prev, NULL);
}
}
return;
}
int builtin_cmd(char **argv)
{
sigset_t mask_all, prev;
sigfillset(&mask_all);
if (!strcmp(argv[0], "quit")) {
exit(0);
}
if (!strcmp(argv[0], "jobs")) {
sigprocmask(SIG_BLOCK, &mask_all, &prev);
listjobs(jobs); //涉及到全局变量的读取,阻塞信号
sigprocmask(SIG_SETMASK, &prev, NULL);
return 1;
}
if (!strcmp(argv[0], "fg") || !strcmp(argv[0], "bg")) {
do_bgfg(argv);
return 1;
}
return 0; /* not a builtin command */
}
void do_bgfg(char **argv)
{
char *jobstr = argv[1];
if (jobstr == NULL) {
printf("%s command requires PID or %%jid argument\n", argv[0]);
return;
}
int isjid = 0, jid; //isjid用来判断fg,bg命令之后跟的是%jid还是PID
pid_t pid;
if (jobstr[0] == '%')
isjid = 1;
jobstr += isjid;
if (!(strspn(jobstr, "0123456789") == strlen(jobstr))) {
printf("%s: argument must be a PID or %%jid\n", jobstr);
return;
}
sigset_t mask, prev;
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, &prev);
if (isjid) {
jid = atoi(jobstr);
}
else {
pid = atoi(jobstr);
jid = pid2jid(pid);
}
struct job_t *job = getjobjid(jobs, jid);
if (job == NULL) {
if (isjid) {
printf("%%%d: No such Job\n", jid);
sigprocmask(SIG_SETMASK, &prev, NULL);
return;
}
printf("%d: No such Process\n", pid);
sigprocmask(SIG_SETMASK, &prev, NULL);
return;
}
if (argv[0][0] == 'b') {
if (job->state == ST) {
job->state = BG;
kill(-(job->pid), SIGCONT);
printf("[%d] (%d) %s", job->jid, job->pid, job->cmdline);
sigprocmask(SIG_SETMASK, &prev, NULL);
}
}
if (argv[0][0] == 'f') {
if (job->state == ST) {
job->state = FG;
kill(-(job->pid), SIGCONT);
sigprocmask(SIG_SETMASK, &prev, NULL); //同上
waitfg(job->pid);
}
else if (job->state == BG) {
job->state = FG;
sigprocmask(SIG_SETMASK, &prev, NULL); //理由和上面相同
waitfg(job->pid);
}
}
return;
}
# waitfg的写法,作业给的提示中说是使用忙等待,也就是循环
# 使用sigsuspend的话,需要额外增加一个全局变量
# 参考了其他博客之后,感觉使用循环更好
void waitfg(pid_t pid)
{
/*
sigset_t mask;
sigemptyset(&mask);
fg_pid = 0;
while (!fg_pid)
sigsuspend(&mask);
*/
sigset_t mask, prev;
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, &prev);
struct job_t *job = getjobpid(jobs, pid); //每次要从全局变量jobs中读取内容是都要阻塞信号
sigprocmask(SIG_SETMASK, &prev, NULL);
while (job != NULL && job->state == FG) { //判断pid对应的job是否为空,为空则不用再等待,如果工作非空,但是其状态不再为FG时,也退出等待
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, &prev);
job = getjobpid(jobs, pid);
sigprocmask(SIG_SETMASK, &prev, NULL);
}
return;
}
# 关键在于识别好发送SIGCHLD信号的原因,也就是进程是停止(stopped),终止(terminated)还是自然退出的(exited)
# waitpid(-1, &status, WNOHANG | WUNTRACED)如果子进程中有任何的进程停止,终止或者自然退出,都会立即返回。然后根据state来判断返回的pid对应的进程是因为什么原因导致SIGCHLD信号产生的
void sigchld_handler(int sig)
{
int olderrno = errno;
pid_t pid;
int status;
while (((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)) {
sigset_t mask_all, prev;
sigfillset(&mask_all);
sigprocmask(SIG_BLOCK, &mask_all, &prev);
if (WIFSIGNALED(status)) { //因为ctrl + c导致的进程停止
pid_t curr_fg_pid = fgpid(jobs);
if (pid == curr_fg_pid) {
int jid = pid2jid(pid);
printf("Job [%d] (%d) Terminated by signal %d\n", jid, pid, WTERMSIG(status));
deletejob(jobs, pid);
}
}
else if (WIFEXITED(status)) { /* 处理正常终止的进程 */
deletejob(jobs, pid);
}
else if (WIFSTOPPED(status)) { /* ctrl + z导致的进程停止 */
struct job_t *job = getjobpid(jobs, pid);
job->state = ST;
int jid = pid2jid(pid);
printf("Job [%d] (%d) Stopped by signal %d\n", jid, pid, WSTOPSIG(status));
}
sigprocmask(SIG_SETMASK, &prev, NULL);
}
errno = olderrno;
return;
}
void sigint_handler(int sig)
{ /* remove FG job and terminate the process */
int olderrno = errno;
sigset_t mask_all, prev;
sigfillset(&mask_all);
sigprocmask(SIG_BLOCK, &mask_all, &prev);
pid_t pid = fgpid(jobs);
if (pid) { //判断一下是不是前台进程,不是的话就不生效
kill(-pid, sig); //这里是-pid,与之前的将每个工作单独放到一个进程组对应起来
}
sigprocmask(SIG_SETMASK, &prev, NULL);
errno = olderrno;
return;
}
# sigtstp_hadler类似
void sigtstp_handler(int sig)
{
int olderrno = errno;
sigset_t mask_all, prev;
sigprocmask(SIG_BLOCK, &mask_all, &prev);
pid_t pid = fgpid(jobs);
if (pid) {
kill(-pid, sig);
}
sigprocmask(SIG_SETMASK, &prev, NULL);
errno = olderrno;
return;
}
参考博客链接
https://www.cnblogs.com/jjppp/p/14686584.html
https://blog.csdn.net/xiaolian_hust/article/details/80087376
标签:sigprocmask,mask,pid,job,shlab,SIG,prev 来源: https://www.cnblogs.com/mujueke/p/15612329.html