Linux 进程信号

目录

1. 信号的基本认识

2. Linux下信号的种类

3. 信号的产生

3.1 通过硬件产生

3.2 通过软件产生

3.3 core dumped 核心转储

4. 信号在进程中注册

4.1 非可靠信号

4.2 可靠信号

5. 信号在进程中注销

5.1 非可靠信号

5.2 可靠信号

6. 信号的处理

6.1 处理方式

6.2 捕捉信号

7. 阻塞信号

7.1 信号的阻塞过程

7.2 使用接口

8. 竞态条件

9. volatile关键字

10. SIGCHLD信号


 

1. 信号的基本认识

  • 本质
    • 软件中断
  • 作用
    • 向进程通知事件的发生(打断当前的操作,去处理事件)
  • 生命周期
    • 产生 -> 注册 -> 注销 -> 处理

2. Linux下信号的种类

信号有不同的种类,每个信号都对应不同的事件

  • 查看信号种类:kill -l
  • 62种信号
    • 1-31号:非可靠信号 / 非实时信号  ->  每个都有各自对应的事件,可丢失(可以不处理;可以不立即处理)
    • 34-64号:可靠信号 / 实时信号  ->  后续添加的信号,不可丢失(发出多少次信号,处理多少次;必须立即处理)

3. 信号的产生

  • 3.1 通过硬件产生

    • ctrl + c  ->  中断信号  ->  操作系统解析为一个软件信号SIGIN,发送给前台进程。
    • ctrl + |  ->  退出信号  ->  SIGQUIT
    • ctrl + z  ->  停止信号  ->  SIGTERM
  • 3.2 通过软件产生

    • kill -signo pid命令
    • int kill(pid_t pid,int sig);       
      • 给指定进程发送指定信号
    • int raise(int sig);       
      • 给调用进程/线程发送指定信号
    • void abort(void);       
      • 给调用进程发送SIGABRT
    • unsigned int alarm(unsigned int seconds);       
      • seconds秒之后,给调用进程发送SIGALRM信号
      • seconds == 0,表示取消定时器
      • 返回值
        • 上一个定时器剩余的时间或0
    • 程序异常
      • 被动结束
  • 3.3 core dumped 核心转储

    • 程序异常退出时,保存程序运行信息到core.pid文件中,用于事后调试
    • 默认是关闭的 -> 占磁盘资源,安全性考虑
    • ulimit -a
      • 查看core dump是否开启
    • ulimit -c 1024
      • 设置核心转储文件大小,开启核心转储
    • gdb ./file 进入gdb后  ->  core-file core.pid 加载core-file文件  ->  bt 查看函数调用栈

4. 信号在进程中注册

  • 给一个进程注册信号,实际上就是进程是否在pcb中的sigset_t 结构体做了标记。
    • sigset_t 结构体,存放数组,数组大小1024个比特位。
    • sigpending 结构体,存放双向链表。
    • sigqueue 结构体
  • 4.1 非可靠信号

    • 判断pending未决信号集合位图相应位是否为1
      • 若为0,为信号组织sigqueue节点,添加到链表中,并且pending位图置1
      • 若为1(信号已经注册过还未被处理),不处理(相当于丢弃信号)
  • 4.2 可靠信号

    • 不管位图是否为1,都组织节点添加到链表中,并且位图置1。
    • 信号不会被丢弃

5. 信号在进程中注销

  • 5.1 非可靠信号

    • ∵ 非可靠信号的信号节点只有一个
      • ∴ 删除节点,位图直接置0
  • 5.2 可靠信号

    • ∵ 可靠信号的信号节点可能会有多个
      • 若还有相同的信号节点,位图依然置1
      • 否则,置0

6. 信号的处理

  • 6.1 处理方式

    • 默认处理   ->   SIG_DFL
    • 忽略处理   ->   SIG_IGN    默认处理方式
    • 自定义处理
  • 6.2 捕捉信号

    • 6.2.1 修改信号的处理方式
      • 1.
        • typedef void (*sighandler_t)(int); 
        • sighandler_t signal(int signum, sighandler_t handler);.
          • signum  信号编号
          • handler  函数指针数组,存放每个信号的处理方式;使用handler函数替换signum信号的处理方式
            • 有两个宏
              • SIG_IGN   信号的忽略处理
              • SIG_DFL   信号的默认处理
      • 2.
        • struct sigaction {
                         void     (*sa_handler)(int);
                         void     (*sa_sigaction)(int, siginfo_t *, void *);
                         sigset_t   sa_mask;
                         int        sa_flags;
                         void     (*sa_restorer)(void);
                     };
        • int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
          • act   用户自定义动作,替换原有的处理动作
          • oldact    保存原有的处理动作
        • int sigemptyset(sigset_t *set);
          • 清空信号集合set
        • int sigfillset(sigset_t *set);
          • 将所有信号添加到set中
        • int sigaddset(sigset_t *set, int signum);
          • 将指定信号signum添加到set中
        • int sigdelset(sigset_t *set, int signum);
          • 从集合中移除指定信号
    • 6.2.2 信号自定义处理方式的捕捉流程

  • 注意
    • 若是另外两种处理方式,直接在内核态直接处理完毕。

7. 阻塞信号

暂时阻止信号被递达  ->  信号依然可以注册,只是暂时不处理,解除阻塞之后再处理。

  • 信号的递达
    • 一个动作  ->  信号的处理
  • 信号的未决
    • 一种状态  ->  信号从产生到处理之前所处的状态
  • 7.1 信号的阻塞过程

    • 在pcb的blocked(信号阻塞集合)中 标记暂不处理的信号(将blocked位图集合中对应的位置置1,表示阻塞这个信号)
    • 进程返回用户态前,发现有信号需要处理 
      • 查看该信号在blocked中的标记
        • 若标记为1 -> 阻塞,暂不处理
        • 若标记为0 -> 处理
    • 注意
      • 9号信号SIGKILL和19号信号SIGSTOP无法被阻塞、无法自定义、无法被忽略
  • 7.2 使用接口

    • int  sigprocmask(int  how,  const sigset_t *set, sigset_t *old‐set);
      • 阻塞set集合中的信号,将原来阻塞的信号保存到oldset中
      • how
        • SIG_BLOCK           阻塞set中的信号    mask = mask | set
        • SIG_UNBLOCK      对set中的信号解除阻塞    mask = mask &(~set)
        • SIG_SETMASK      将set中的信号替换原有信号    mask = set
      • 返回值
        • 成功 -> 0
        • 失败 -> -1
    • int sigpending(sigset_t *set);
      • 获取当前进程的未决信号,置于set
    • int sigismember(const sigset_t *set, int signum);
      • 判断信号signum是否在信号集合set当中
    • int sigsuspend(const sigset_t *mask);
      • 临时使用传入的mask替换block阻塞集合,陷入休眠。阻塞的就是mask,被唤醒之后,block阻塞集合还原。

8. 竞态条件

运行时序造成的数据竞争,导致数据二义性。

  • 函数的可重入与不可重入:一个函数是否可以在多个运行时序中重复调用而不会出现问题
    • 不可重入函数 (malloc / free)
      • 如果一个函数中操作了全局数据,并且这个操作既不是原子性操作,又不受保护  
      • 不能在多个时序的运行中重复调用,重复调用有可能造成数据二义性。
    • 可重入函数
      • 在多个时序的运行中重复调用,不会造成异常影响(数据二义性问题)。

​​​​​​​9. volatile关键字

保持内存可见性,每次操作变量都需要重新从内存中获取,防止编译器过度优化

  • 优化:将经常使用的变量放入寄存器中,就算在内存中更改了变量的值,也不会重新加载

    • 缺点:会造成逻辑紊乱

    • 解决:每次对变量访问都要从内存重新获取数据

10. SIGCHLD信号

子进程退出,操作系统通知父进程的信号。

  • 自定义SIGCHLD信号处理方式sigcb,当子进程退出,操作系统发送信号给父进程,直接出发信号回调sigcb,用户主要在sigcb中去调用 wait / waitpid 就可以处理子进程的退出。
  • SIGCHLD是一个非可靠信号,假如有多个子进程同时退出,则有可能导致事件丢失,导致sigcb只被回调一次,只处理一个子进程。
    • 因此,在sigcb中用户需要循环非阻塞处理子进程退出,直到没有退出的子进程。
      • while(waitpid(-1,NULL,WONOHANG) > 0)(>0表示有子进程退出)
      • 必须使用非阻塞,否则没有子进程退出的时候,waitpid将阻塞导致程序无法回到主控流程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值