异常部分代码赏析
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); }
本文作者:上山砍大树
本文链接:https://www.cnblogs.com/shangshankandashu/p/18617927
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步