we choose to go to the moon!�|

上山砍大树

园龄:5年3个月粉丝:13关注:3

异常部分代码赏析

8章异常部分代码赏析

本节内容可以通过三个程序综合起来帮助理解。

第一个是帮助理解创建进程和回收进程的shellex.c。

第二个是帮助理解信号阻塞的signal.c

第三个是帮助理解并发导致的竞争的procmask.c

下面分别讲一下其中的业务逻辑组块,帮助自己更好地理解代码

shellex.c

业务逻辑

实现获取用户输入命令并执行的shell程序

  1. 要获取输入命令,然后执行命令
  2. 要获取输入命令,就需要在标准输入中读取输入用户输入的命令字符串cmdline
  3. 要执行命令,就需要先将空格分隔的字符串cmdline字符串转换成参数列表argv,然后根据参数执行相应的操作。

执行操作API:

  • 判断命令是否为内置命令
    • 是内置命令,则在当前进程执行即可
    • 如果是外部命令,则需要创建子进程并执行外部指令。
  • 判断是否在后台执行命令
    • 如果后台执行命令,则简单返回后台执行命令的PID和命令字符串
    • 如果前台执行命令,则需要父进程挂起当前进程等待子进程结束

代码组块

这部分是通过将代码继续分块,来帮助理解的。

  1. 创建参数字符串

    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;
  2. 判断是否为内置命令

    if (!builtin_command(argv))
    if (strcmp(argv[0], "quit") == 0)
  3. 创建子进程并加载执行新程序

    if ((pid = Fork()) == 0) {
    if (execve(argv[0], argv, environ) < 0) {
    printf("%s: Command not found.\n", argv[0]);
    exit(0);
    }
    }
  4. 前后台执行

    if(!bg) {
    int status;
    if (waitpid(pid, &status, 0) < 0) {
    unix_eror("waitfg: waitpid error");
    }
    } else {
    printf("%d %s", pid, cmdline);
    }
  5. 参数为空处理:

    在eval,如果参数为空,则不做处理。

    if (argv[0] == NULL) {
    return;
    }

    在parseline中,如果参数为空,则直接返回

    if (argc == 0) {
    return 1;
    }

signal.c

业务逻辑

父进程设置一个SIGCHLD处理程序,然后创建三个输出后立即返回的子进程,随后等待用户输入。用户输入完毕,则进入无限循环,等待处理程序回收子进程。

  • 子进程执行完毕后立刻返回
  • 父进程等待回收子进程

要想避免信号丢失,就需要获取到第一个子进程结束信号的时候,就需要尽可能多地回收子进程。

代码组块

  1. 安全的信号处理

    int olderrno = errno;
    ...
    errno = olderrno;
  2. 尽可能多地回收子进程

    while (waitpid(-1, NULL, 0) > 0) {
    Sio_puts("Handler heap child\n");
    }
    if (errno != ECHILD) {
    Sio_error("waitpid error");
    }
  3. 设置信号处理程序

    if (signal(SIGCHLD, handler2) == SIG_ERR) {
    unix_error("signal error");
    }
  4. 创建三个立即结束的子进程

    for (int i = 0; i < 3; i++) {
    if (Fork() == 0) {
    printf("Hello from child %d\n", (int)getpid());
    exit(0);
    }
    }

procmask2.c

避免父进程和子进程对于全局资源的竞争

  1. 安全的信号处理

    int olderrno = errno;
    ...
    errno = olderrno;
  2. 删除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");
    }
  3. 信号初始化

    sigset_t mask_all, mask_one, prev_one;
    Sigfillset(&mask_all);
    Sigemptyset(&mask_one);
    Sigaddset(&mask_one, SIGCHLD);
    Signal(SIGCHLD, handler);
  4. 添加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 中国大陆许可协议进行许可。

posted @   上山砍大树  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起