进程信号>>>种类、注册、捕捉

信号的基本概念

信号是一个软件中断,打断当前正在运行的进程,让该进程去处理信号的事件。当前的进程需要处理信号所带来的操作。

信号的种类

Linux操作系统当中,总共有62种信号

1~31 不可靠信号,信号有可能会丢失,非实时信号

34~64 可靠信号,信号是不可能被丢失的,实时信号

 

信号的产生

硬件的产生:

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号

Ctrl+c 给前台进程发送一个SIGNT,中断当前的进程

Ctrl+z 给前台进程发送一个SIGTSTP信号,暂停进程

Ctrl+| 给当前进程发送一个SIGQUIT信号,使进程崩溃,产生coredump文件

软件的产生

kill [pid] 给前台进程发送一个SIGTERM信号

kill -[signalno] [pid] 给指定进程发送指定的信号

调用函数的产生

kill 函数 kill(pid_t pid,int signo);

abort函数 封装kill函数,谁调用给谁发送SIGABRT信号

alarm定时器函数 alarm(int sec) SIGALRM


信号的注册

前提:

  • 在内核当中的task_struct结构体当中,保存了一个struct sigpending的对象pending,struct sigpending 这个结构体当中保存了两个元素。第一个元素是struct list_head list第二个元素是sigset_t signal

               sigset_t类型也是一个结构体,struct sigset_t,保存了一个unsigned long sig[ ]

               sig这个数组是按照比特位来使用的,我们称之为位图

  • 内核当中还维护了一个sigqueue队列。队列当中的每一个元素对应信号的一个处理节点。

图解:

 

非可靠信号 1~31

更改sig位图当中对应的比特位为1             在sigqueue队列当中也增加对应信号所对应的节点

当多次收到同一个信号的时候,只添加一次节点,也就是意味着,第二次收到的同样的信号被丢弃掉了

可靠信号 34~64

更改sig位图当中对应比特位为1,并且在sigqueue队列当中增加对应信号所对应的节点

当多次收到同样的一个信号的时候,先判断sig位图当中的比特位是否为1,并且在sigqueue队列当中增加对应信号所对应的节点。

 

 

 

信号的注销过程

非可靠信号的注销

将sig位图当中对应信号的比特位置为0,并且将sigqueue队列当中的节点去除掉(即操作系统收到相应信号,去做相应处理)

可靠信号的注销

将待处理的信号在sigqueue队列当中对应的节点进行盘算,当前处理的对应信号的节点是否在sigqueue队列当中还有相同类型的

  • 如果有,不改变sig位图当中该信号对应的比特位的值 
  • 如果没有,直接将sig位图中对应的比特位值置为0

 

 

信号的捕捉处理

  • 默认处理方式:SIG_DEL 执行一个函数
  • 忽略 SIG_IGN 不做任何事情

僵尸进程产生的原因:子进程退出的时候,会给父进程发送一个SIGCHID信号,而操作系统对SIGCHID信号的处理方式为忽略处理,导致父进程不去处理信号,从而子进程变成僵尸进程

  • 自定义:程序员自己定义信号的处理函数

sighandler_t signal(int signum,sighandler_t handler);

signum:需要自定义哪一个信号

handler:函数指针类型的参数

        typedef void(*sighandler_t)(int)

         void,返回值类型

        int:参数,操作系统传递的信号值

 

signal函数的这种方式称之为向操作系统注册一个函数,当发生某种事件的时候,会回调刚才注册的函数,我们称这个函数为回调函数。并不是我们调用signal函数的时候,回调函数就会被操作系统调用执行,而是当我们收到自定义信号的时候,操作系统才会帮我们调用该回调函数,进行处理信号

 

我们自己的程序启动起来,有自己的执行流,回调函数的执行并不是我们程序的执行流执行的,

而是操作系统的执行流调用执行的

 

 

自定义信号的处理过程

前提

1.在task_struct结构体当中,有一个指向sighand_struct的结构体指针,在该结构体当中有一个action数组,数组当中的每一个元素都是一个struct k_sigaction结构体,数组当中每一个元素对应一个信号的处理逻辑

2.在struct k_sigaction结构体当中有一个元素是struct sigaction sa,struct sigaction结构体中还有一个sighandler_t类型元素,这个sighandler_t是一个函数指针类型,typedef void(*sighandler_t)(int) , 保存信号默认执行的函数。

图解:

 

操作系统默认对信号的处理

当sig位图当中收到一个信号的时候,意味着sig位图当中的某一个比特位被置为1,操作系统处理该信号的时候,就会从PCB当中去寻找sighand_struct这个结构的指针,从而找到sa_handler,进而操作系统内核去调用sa_handler保存的函数地址,完成信号的功能。

 

 

自定义信号处理函数

sighandler_t signal(int signum,sighandler_t handler)

signal函数:相当于改掉了sa_handler保存的函数地址,意味着,当收到自定义信号的时候,操作系统内核去调用sa_handler保存的新的函数地址,而这个函数是程序员定义的,也就达到了更改信号处理函数的目的

 

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

signum:需要更改的信号

act:表示把signum信号修改成act处理方式

oldact:表示操作系统之前被signum所定义的处理方式。


struct sigaction类型

struct sigaction {

void (*sa_handler)(int);

void (*sa_sigaction)(int, siginfo_t *, void *);

sigset_t sa_mask;

int sa_flags;

void (*sa_restorer)(void);

};

void (*sa_handler)(int);操作系统为每一个信号定义的默认调用函数

void (*sa_sigaction)(int, siginfo_t *, void *);函数指针,这个函数指针一般是预留的,配合sa_flags使用,当sa_flags为SA_SIGINFO的时候,操作系统就会调用该函数指针当中保存的函数地址。

sigset_t sa_mask;当前进程在处理信号的时候,也有可能收到新的信号,将新的信号放到sa_mask里面

int sa_flags;配合sa_sigaction使用

void (*sa_restorer)(void);预留信息


 

struct sigaction相当于action[ ]数组当中的元素类型(k_sigaction )

sigaction函数:相当于action数组当中的元素,也意味这,直接更改结构体,达到修改信号处理函数地址的目的

signal函数调用sigaction函数实现

信号的捕捉流程

图解:

什么时候会进入内核状态

  • 调用系统调用
  • 程序员调用库函数的时候,库函数如果底层调用可函数调用
  • 程序异常
  1. 访问空指针 11号信号
  2. 内存访问越界 11号信号
  3. double free(同一个指针free两次) 6号信号

 

 

信号的阻塞:

前提:在task_struct结构体当中保存了一个block位图

信号阻塞,不会影响信号注册、信号更改pengding位图和增加sigqueue节点

 

操作系统处理信号的逻辑

当程序从用户态切换到内核态之后,处理do_signal函数的时候,发现收到某个信号,想要处理这个信号之前,先判读block位图当中对应信号的比特位是否为1

block当中对应的比特位为1:则不处理该信号,sigqueue当中对应的信号节点还是存在

block当中对应比特位为0:    处理该信号

 

int         sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

更改sigset_t位图当中比特位的值

how:

SIG_BLOCK 设置某个信号为阻塞状态,  用修改位图达到目的 block(new)=block(old)| set

SIG_UNBLOCK 设置某个信号为非阻塞状态                                 block(new)=block(old)& (~ set)

SIG_SETMASK 设置新的阻塞的sigset_t位图                               block(new)=set

set:要设置的新的阻塞位图

oldset:之前程序当中阻塞的位图,出参

注:

9号信号不能被阻塞


竞态条件:

多个执行流访问同一个资源的情况下,会对程序结果造成二义性,将这种现象称之为竞态条件

重入:多个执行流可以访问到同一资源,称之为重入

可重入:多个执行流可以访问到同一个资源,不会对程序结果产生影响

不可重入:多个执行流可以访问到同一个资源,但是会对程序结果产生影响,意味着程序会有多个结果表现

 

volitile关键字:

使变量保持内存可见性,每次操作变量都需要重新从内存中获取,而摒弃编译器优化。

-O0 -O1 -O2 -O3 编译选项,优化程度依次增高

僵尸进程的解决方法:

SIGCHID:当子进程退出,就会给父进程发送一个SIGCHID信号,配合signal处理机制将进程等待放到SIGCHID函数的自定义处理函数当中,解决父进程一直在等待的局面。

 

更改SIGCHID信号的处理方式,自定义一个处理函数,当子进程退出的时候,内核回去调用用户自定义的函数(回调函数),在回调函数当中去调用wait或者waitpid接口,让父进程来等待子进程

注:在自定义信号处理函数的时候,不要将17号信号之外的函数定义为调用wait或者waitpid的函数,否则当进程收到一个其他信号的时候,触发内核调用用户自定义函数的时候,由于没有一个子进程退出,可能导致内核执行流卡死

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值