异常部分代码赏析
8章异常部分代码赏析
本节内容可以通过三个程序综合起来帮助理解。
第一个是帮助理解创建进程和回收进程的shellex.c。
第二个是帮助理解信号阻塞的signal.c
第三个是帮助理解并发导致的竞争的procmask.c
下面分别讲一下其中的业务逻辑组块,帮助自己更好地理解代码
shellex.c
业务逻辑
实现获取用户输入命令并执行的shell程序
- 要获取输入命令,然后执行命令
- 要获取输入命令,就需要在标准输入中读取输入用户输入的命令字符串
cmdline
。 - 要执行命令,就需要先将空格分隔的字符串
cmdline
字符串转换成参数列表argv
,然后根据参数执行相应的操作。
执行操作API:
- 判断命令是否为内置命令
- 是内置命令,则在当前进程执行即可
- 如果是外部命令,则需要创建子进程并执行外部指令。
- 判断是否在后台执行命令
- 如果后台执行命令,则简单返回后台执行命令的PID和命令字符串
- 如果前台执行命令,则需要父进程挂起当前进程等待子进程结束
代码组块
这部分是通过将代码继续分块,来帮助理解的。
-
创建参数字符串
char *delim; buf[strlen(buf) - 1] = ' '; // 替换最后一个字符为空格 while(*buf && (*buf == ' ')) { buf++; } while(delim = strchr(buf, ' ')) { argv[argc++] = buf; *delim = '\0'; buf = delim + 1; while(*buf && (*buf == ' ')) { buf++; } } argv[argc] = NULL;
-
判断是否为内置命令
if (!builtin_command(argv)) if (strcmp(argv[0], "quit") == 0)
-
创建子进程并加载执行新程序
if ((pid = Fork()) == 0) { if (execve(argv[0], argv, environ) < 0) { printf("%s: Command not found.\n", argv[0]); exit(0); } }
-
前后台执行
if(!bg) { int status; if (waitpid(pid, &status, 0) < 0) { unix_eror("waitfg: waitpid error"); } } else { printf("%d %s", pid, cmdline); }
-
参数为空处理:
在eval,如果参数为空,则不做处理。
if (argv[0] == NULL) { return; }
在parseline中,如果参数为空,则直接返回
if (argc == 0) { return 1; }
signal.c
业务逻辑
父进程设置一个SIGCHLD处理程序,然后创建三个输出后立即返回的子进程,随后等待用户输入。用户输入完毕,则进入无限循环,等待处理程序回收子进程。
- 子进程执行完毕后立刻返回
- 父进程等待回收子进程
要想避免信号丢失,就需要获取到第一个子进程结束信号的时候,就需要尽可能多地回收子进程。
代码组块
-
安全的信号处理
int olderrno = errno; ... errno = olderrno;
-
尽可能多地回收子进程
while (waitpid(-1, NULL, 0) > 0) { Sio_puts("Handler heap child\n"); } if (errno != ECHILD) { Sio_error("waitpid error"); }
-
设置信号处理程序
if (signal(SIGCHLD, handler2) == SIG_ERR) { unix_error("signal error"); }
-
创建三个立即结束的子进程
for (int i = 0; i < 3; i++) { if (Fork() == 0) { printf("Hello from child %d\n", (int)getpid()); exit(0); } }
procmask2.c
避免父进程和子进程对于全局资源的竞争
-
安全的信号处理
int olderrno = errno; ... errno = olderrno;
-
删除job的原子化
Sigfillset(&mask_all); while ((pid = waitpid(-1, NULL, 0)) > 0) { Sigprocmask(SIG_BLOCK, &mask_all, &prev_all); deletejob(pid); Sigprocmask(SIG_SETMASK, &prev_all, NULL); } if (errno != ECHILD) { Sio_error("waitpid error"); }
-
信号初始化
sigset_t mask_all, mask_one, prev_one; Sigfillset(&mask_all); Sigemptyset(&mask_one); Sigaddset(&mask_one, SIGCHLD); Signal(SIGCHLD, handler);
-
添加job的原子化
while(1) { Sigprocmask(SIG_BLOCK, &mask_one, &prev_one); if ((pid = Fork()) == 0) { Sigprocmask(SIG_SETMASK, &prev_one, NULL); execve(....); } Sigprocmask(SIG_BLOCK, &mask_all, NULL); addjob(pid); Sigprocmask(SIG_SETMASK, &prev_one, NULL); }