跳转至

显式地等待信号

  • 有时, 之程序需要显式地等待某个信号处理程序运行: 当shell创建一个前台作业之前, 在接收下一条用户命令之前, 它必须等待作业终止, SIGCHLD处理程序回收
  • 实例代码:
    • 父进程设置SIGINTSIGCHLD的处理程序, 然后进入一个无限循环
      #include "csapp.h"
      
      volatile sig_atomic_t pid;
      
      void sigchld_handler(int s){
          int olderrno = errno;
          pid = waitpid(-1, NULL, 0);
          errno = olderrno;
      }
      
      void sigint_handler(int s){
      
      }
      
      int main(int argc, char **argv){
          sigset_t mask, prev;
      
          Signal(SIGCHLD, sigchld_handler);
          Signal(SIGINT, sigint_handler);
          Sigemptyset(&mask);
          Sigaddset(&mask, SIGCHLD);
      
          while(1){
              Sigprocmask(SIG_BLOCK, &mask, &prev);
              if (Fork() == 0){
                  exit(0);
              }
      
              pid = 0;
              Sigprocmask(SIG_SETMASK, &prev, NULL);
              while(!pid)
                  // pause();
                  // sleep();
      
              printf(".");
          }
          exit(0);
      }
      
  • while(!pid)
    • 一些存在的问题:
      • 循环体为空: 浪费资源
      • pause(): 如果在while后和pause之前收到了SIGCHLD信号, pause将会永远睡眠
      • sleep(1): 低效
    • 合适的解决方法: sigsuspend: 暂时使用mask替换当前的阻塞集合, 然后挂起当前进程, 直到收到一个信号, 其行为要么是运行一个处理程序, 要么是终止该进程
      • 终止: 进程不从sigsuspend返回就直接终止
      • 运行一个处理程序: sigsuspend从处理程序处返回, 恢复调用sigsuspend时原有的阻塞集合

        [!note] sigsuspend 等价于以下代码的原子调用

        sigprocmask(SIG_SETMASK, &mask, &prev);
        pause();
        sigprocmask(SIG_SETMASK, &prev, NULL);
        
        #include "csapp.h"
        
        volatile sig_atomic_t pid;
        
        void sigchld_handler(int s){
            int olderrno = errno;
            pid = waitpid(-1, NULL, 0);
            errno = olderrno;
        }
        
        void sigint_handler(int s){
        
        }
        
        int main(int argc, char **argv){
            sigset_t mask, prev;
        
            Signal(SIGCHLD, sigchld_handler);
            Signal(SIGINT, sigint_handler);
            Sigemptyset(&mask);
            Sigaddset(&mask, SIGCHLD);
        
            while(1){
                Sigprocmask(SIG_BLOCK, &mask, &prev);
                if (Fork() == 0){
                    exit(0);
                }
        
                pid = 0;
                Sigprocmask(SIG_SETMASK, &prev, NULL);
                while(!pid)
                    sigsuspend(&prev);
        
                printf(".");
            }
            exit(0);
        }