【Linux】进程间通信---信号

1.信号

  • 信号是一个中断软件
  • 信号的种类
    1~31:非可靠信号
    34~64:可靠信号

     

2.信号的产生方式

硬件产生
  ctrl + c:2号信号,导致前台进程终止
  ctrl + z :20号信号(SIGTSTP),导致前台进程暂停
  ctrl + |:3号信号(SIGQUIT)
 
软件产生
  kill函数 kill(pid_t pid, int signo)
  kill -[信号][pid] —>eg:kill -9 12121
  abort(pid_t pid):—>6号信号(SIGABORT)—>(double free)
  解引用空指针+内存访问越界— >进程收到11号信号(SIGSEGV)
 

3.信号的注册

【Linux】进程间通信---信号

  • 在使用sig数组的时候不是按照数组的方式去使用的,而是按照比特位的方式去使用的
  • 对于sig位图当中的bit位,每一个比特位有与之对应的信号,直到将我们当前操作系统的信号表示完毕
     

3.信号的注册

  • 当进程收到一个非可靠信号

  • 第一件事情:将非可靠信号对应的比特位更改为1

  • 第二件事情:添加sigqueuej节点到sigqueue队列当中

  • 注意:队列当中已经有了该 信号的sigqueue节点,则不添加
     

  • 如果进程收到一个可靠信号

  • 第一件事情:在sig位图当中更改该信号对应的比特位为1

  • 第二件事情:不论之前sigqueue队列当中是否存在该信号的sigqueue节点,都再次添加sigqueue节点到sigqueue队列当中;
     

4.信号的注销

  • 非可靠信号的注册
  • 该信号的sigqueue节点从sigqueue队列当中的出队操作
  • 信号在sig位图当中对应的比特位从1置为0
     
  • 可靠信号的注销
  • 将该信号的sigqueue节点从sigqueue队列当中进行出队操作
  • 需要判断sigqueue队列当中是否还有相同的sigqueue节点
    没有:信号在sig位图当中对应的比特位从1置为0
    有:不会更改sig位图当中对应的比特位
     

5.信号的处理

  • SIG_DFL:默认处理方式
  • SIG_IGN:忽略处理(SIGCHLD)
  • 自定义信号处理方式
    signal函数
    typedef void (*sighandler_t )(int);
    sighanler_t signal(int signum, sighhandler_t handler);
     该函数可以更改信号的处理方式
    【Linux】进程间通信---信号
     

6.sigaction函数

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)

  • signum:待更改的信号的值
  • struct sigaction
    struct sigaction{
       void (*sa_handler)(int):函数指针,保存内核对信号的处理方式
       void (*sa_sigaction)(int, siginfo_t , void)
       sigset_t sa_mask:保存的是当前进程在处理信号的时候,收到的信号
       int sa_flags:SA_SIGINFO,操作系统在处理信号的时候,调用的就是sa_sigaction函数指针当中保存的值0,在处理信号的时候,调用sa_handler保存的函数
       void (*sa_restorer)(void) :预留信息
    };
    act:将信号处理函数改变act
    oldact:信号之前的处理方式

int sigempty(sigset_t *set); // 将位图的所有比特位设置为0
 

7.信号的捕捉流程

【Linux】进程间通信---信号

  • 什么时候进入到内核空间:调用系统调用函数的时候,或者调用库函数的时候(库函数底层大多数都是封装系统调用函数的)
     

8.信号的阻塞

【Linux】进程间通信---信号

  • 信号的阻塞并不会干扰信号的注册,只不过当前的进程不能立即处理
     
  • 当我们将block位图当中对应信号的比特位置为1,表示当前今后才能阻塞该信号
  • 当进程收到一个该信号的时候,进程还是一如既往的对该信号进行注册
  • 当进程进入到内核空间,准备返回用户空间的时候,调用do_signal函数,这会儿不会立即去处理该信号了
  • 这里面的不会立即处理,一定不是之后处理
     
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • how:告诉sigprocmask函数,应该进行什么操作
    SIG_BLOCK:设置每个信号为阻塞
    SIG_UNBLOCK:解除对某个信号的阻塞
    SIG_SETMASK:替换阻塞位图
  • set:用来设置阻塞位图
    SIG_BLOCK:设置某个信号为阻塞
      block(new) = block(old) | set
    SIG_UNBLOCK:解除对某个信号的阻塞
      block(new) = block(old) & (~set)
    SIG_SETMASK:替换阻塞位图
       block(new) = set
  • oldset:原来的阻塞位图