进程信号

深入理解Linux信号机制

进程信号

一、信号的基本概念

生活中的信号:红绿灯

信号更多的是通知事件的发生,信号产生以后第一时间也不是直接处理而是先存储下来,处理信号。

这节课学习的信号实际是一个软中断。

linux下62种信号(宏),使用kill -l查看。

信号分为两类:1-31不可靠信号(非实时信号)  34-64可靠信号(实时信号)

信号的产生->信号的注册->信号的注销->信号的处理。

二、产生信号

1.通过终端按键产生信号(硬件中断)ctrl + c

2.调用系统函数向进程发信号(程序异常)《unix环境高级编程》

3.由软件条件产生信号

int kill(pid_t pid, int sig); 向指定进程发送指定信号

kill (getpid(), SIGINT);

int raise(int sig);向自身发送信号

raise(SIGTREM);

void abort(void);向自身发送SIGABRT信号

unsigned int alarm(unsigned int seconds);在n秒之后向进程发送SIGALRM信号

alarm(3);设置一个定时器,取消上一个定时,并且返回上一个定时器剩余时间。

     int sigqueue(pid_t pid, int sig, const union sigval value);

核心转储(core dump;

保存当前程序运行的数据以及调用栈信息,用于错误原因定位调试。

如果程序运行出现错误,可以直接通过core文件来gdb调试(有些错误是偶然发生的)

coredump默认关闭:隐私安全/资源占用

coredump打开:ulimit

三、信号注册

信号的注册/给一个进程发送信号,就是修改这个进程pcb关于信号的pending位图,将相应的信号置为1。

 

四、信号阻塞

暂时不处理信号(阻止信号递达),并不是不接收信号。

信号的递达:信号的处理

要阻塞一个信号,那么就是将pcb中关于信号的block位图,将相应的信号置为1,这个位图就像一个备注,说明若接收到信号暂时不处理。

信号未决:这是一种状态,信号从注册成功到信号递达之间的一种状态。

信号阻塞的实现:

阻塞这个集合中的信号:sigset_t  new_block;

用于保存原来阻塞集合中的信号,防止后续要将阻塞集合还原回去:

sigset_t old_block;

int sigemptyset(sigset_t *set);清空一个信号集合

int sigfillset(sigset_t *set);将所有的信号都添加到set集合中

int sigaddset(sigset_t *set, int sigunm);添加指定的单个信号到set集合中

int sigdelset(sigset_t *set, int signum);从集合中一处一个指定的信号

int sigismember(const sigset_t *set, int signum);判断一个信号是否在一个集合中

int sigprocmask(int how, sigset_t *set, sigset_t *oldset);阻塞信号/接触阻塞

阻塞信号/解除阻塞

how:

SIG_BLOCK 阻塞集合中的信号

SIG_UNBLOCK 将集合中的信号解除阻塞

SIG_SETMASK 将集合中的信号设置到阻塞集合中

set:要阻塞/解除阻塞的集合

oldset:保存原先阻塞集合中的信号

有两个信号是不会被阻塞的:SIGKILL, SIGSTOP

sigpending  获取未决信号

五、注销:

就是从pending集合中将即将要处理的信号相应位置0(从pcb的pending集合中移除)

非可靠信号注册就是将相应pending位图置1,然后添加一个sigqueue结构到链表中,之后如果有相同信号到来,一看位图已经置1了,就不做操作了,意味着后来的信号在前一个信号未处理之前不会重复注册,代表丢了。

可靠信号不管有没有注册都要置1,并且添加节点到链表,所以不会丢信号。

非可靠信号注销就是删除链表节点,相应位图置为0

可靠信号注销时删除节点,判断是否有相同的信号节点,如果没有位图置为0,有就不置为0.

六、处理

默认操作----按照操作系统中对信号事件的既定处理方式

忽略操作----直接将信号丢掉

自定义处理:用户自己定义事件的处理方式

sighandler_t signal(int signum, sighandler_t handler);

signum:信号的编号

handler:处理方式(函数指针数组)

SIG_IGN:忽略                                                                                                                                                                                                                                                                                                                                                                                                                                                           

SIG_DFL:默认

信号的捕捉流程:主要是针对信号的自定义处理方式

信号并不是立即处理的。而是选择一个合适的时机去处理,合适的时机就是当前程序从内核态切换到用户态的时候。

程序如何从用户态切换到内核态?程序异常、中断、系统调用

当我们发起系统调用/程序异常/中断当前程序从用户态运行切换到内核态,去处理这些事情,处理完毕后,要从内核态返回用户态,但是在返回之前会看一下是否有信号需要被处理,如果有就处理(切换到用户态执行信号的自定义处理方式),处理完成后再次返回内核态,判断如果没有信号要处理就调用sys_sigreturn返回用户态(我们程序之前运行的位置)。

signal这个接口有linux版本的差异性,一般用sigaction这个接口替代signal。它的功能也是自定义信号处理方式,signal函数内部也是通过sigaction实现的

struct sigaction {

void (*sa_handler)(int);    处理函数

void (*sa_ sigaction)(int, siginfo_t *,void *);  处理函数

sigset_t  sa_mask;  在处理信号的时候可以通过这个mask暂时阻塞一些信号,处理完毕之后会还原回去

int  sa_ flags; 决定了我们使用哪个回调接口,并且还有一些其他的选项信息

};

重新定义处理方式的信号是:SIGINT  SIGQUIT

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

signum:信号编号

act: 新的处理方式

oldact:保存原有的处理方式

这个操作是我们一般性的更改一个信号的处理方式,这种处理方式sa_flags=0代表我们使用的回调接口shisa_handler;

sigemptyset(&new_act.sa_mask);

new_act.sa_flags = 0;

new_act.sa_handler = sigcb;

sigaction(SIGINT, &new_act, &old_act);

 

这个操作是用于传递信号同时携带参数的情况,sa_flags需要被指定为:SA_SIGINFO,并且这时候回调需要使用sa_sigaction

sigemptyset(&new_act.sa_mask);

new_act.sa_flags = SA_SIGINFO;

new_act.sa_sigaction = sigcb1;

sigaction(SIGOUIT, &new_act, &old_act);

int sigqueue(pid_t, int sig, union sigval)这个函数不仅可以发送信号,还可以顺便携带一个参数

pid:进程ID

sig:信号编号

sigval:参数

七、可重入/不可重入函数

不可重入函数:如果函数在不同的地方/时序进行调用,会对函数的功能逻辑造成影响

可重入函数:不管怎么调用,都不会对函数内部功能/程序逻辑造成影响

不可重入函数的要点:

函数内部包含有对全局性变量的修改操作

函数传参的参数跟其他地方共同使用同一变量

因为这些对全局变量的操作不是原子性的,因此这些修改操作有可能在同时在不同地方进行修改

一个函数是否可重入:

1.是否对全局性的数据进行修改

2.这个操作是否是原子的

八、volatile关键字:修饰变量

保持变量内存可见性

一个变量数据被cpu进行处理,每次都要重新从内存中获取变量的数据

如果写的高性能程序需要对程序进行编译优化,那么我们就必须对一些敏感的变量进行内存可见性的修饰

九、僵尸进程的避免

僵尸进程是子进程先于父进程推出,操作系统通知父进程,但父进程不作为。

操作系统通过信号通知父进程(17号信号)

SIGCHLD

以前为避免僵尸进程,只能让父进程一直等待子进程的推出,浪费了父进程资源。

现在信号可以这样做:

自定义信号:SIGCHLD的处理方式,相当于提前告诉进程,当接收到这个信号的时候使用waitpid,这样就不用一直等待。

使用非阻塞的循环来处理SIGCHLD信号,因为SIGCHLD信号不是可靠信号,有可能丢失,因此就有可能漏掉僵尸子进程没有处理,所以一旦接收到信号就处理到不能处理为止。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值