【linux】:信号

什么是linux中的信号呢?
举个栗子:
其实就类似于现实中肚子叫了发出一个信号表示你饿了,而这个时候你正在工作,那么你可以选择忽略它,或者暂停你手中的工作去吃点东西来处理这个信号。

信号本质上就是软件层次上对中断机制的模拟

用kill -l命令可以查看linux下的信号列表
这里写图片描述
信号是异步的,一个进程不必通过任何操作来等待信号的到达。

产生信号的方式:
1,用户在终端按下某些键时,终端驱动程序会发送信号给前台进程。例如Ctrl + C产生SIGINT信号,Ctrl +\产生SIGTSIP信号(可使前台进程停止)
2,硬件异常产生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。
3,一个进程调用kill函数可以发送信号给另一个进程,可以用kill命令发送信号给某个进程。raise函数可以给当前进程发送指定的信号(自己给自己发信号)。
4,软件条件产生。

进程对信号的响应:
1,忽略信号:大部分信号可被忽略。除SIGSTOP和SIGKILL信号外(这是超级用户杀掉或停掉任意进程的手段)
2,捕获信号:注册信号处理函数。要求内核在处理该信号时切换到用户态执行。
3,让信号默认动作起作用

几种常见信号:
SIGABRT:调用abort函数时产生该信号表示进程异常终止。
SIGALRM:当调用alarm函数设置的定时器超时时产生该信号,若由setitimer(2)函数设置的间隔时间已经超时时也产生该信号。
SIGCHLD:在一个进程终止或停止时,SIGCHLD信号被送给其父进程。按系统默认将忽略此信号。如果父进程希望被告知其子进程的这种状态改变,则应捕捉此信号。信号捕捉函数中通常要用wait函数以取得子进程ID和其终止状态。

信号屏蔽字:
每个进程都有一个信号屏蔽字,它规定了当前要阻塞递送到该进程的信号集。对于每种可能的信号,该屏蔽字中都有一位与之对应。对于某种信号,若其对应位已设置,则它当前是被阻塞的,进程可以调用sigprocmask来检测和更改当前信号屏蔽字。

相关概念:
(1)实际执行信号的处理动作称为信号递达
(2)信号从产生到递达之间的动作称为未决
(3)进程可以选择阻塞某个信号
(4)被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作
(5)注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种动作。

进程的task_struct中有管理信号的部分。信号在内核中的表示如下 :
内核维持三张表:block表(阻塞)、pending表(未决)、handler表(信号处理)
这里写图片描述
这三张表用位图表示,当进程中有某种状态的信号时,就将表中相应的位置置1,直到信号递达才清除该标志。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

参数:
how:用于指定信号修改的方式,可能选择有三种:
SIG_BLOCK //加入信号到进程屏蔽。
SIG_UNBLOCK //从进程屏蔽里将信号删除。
SIG_SETMASK //将set的值设定为新的进程屏蔽。
set:为指向信号集的指针,在此专指新设的信号集,如果仅想读取现在的屏蔽值,可将其置为NULL。
oldset:也是指向信号集的指针,在此存放原来的信号集。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handler(int sig)
{
   printf("Deal SIGINT");  //SIGINT信号处理函数
}

int main()
{
sigset_t newmask;
sigset_t oldmask;
sigset_t pendmask;
struct sigaction act;
act.sa_handler = handler;  //handler为信号处理函数首地址
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);  //信号捕捉函数,捕捉Ctrl+C

sigemptyset(&newmask);//初始化信号量集
sigaddset(&newmask, SIGINT);//将SIGINT添加到信号量集中

sigprocmask(SIG_BLOCK, &newmask, &oldmask);//将newmask中的SIGINT阻塞掉,并保存当前信号屏蔽字到Oldmask

sleep (5);//休眠5秒钟,说明:在5s休眠期间,任何SIGINT信号都会被阻塞,如果在5s内收到任何键盘的Ctrl+C信号,则此时会把这些信息存在内核的队列中,等待5s结束后,可能要处理此信号。 
sigpending(&pendmask);//检查信号是悬而未决的,
if (sigismember(&pendmask, SIGINT))//SIGINT是悬而未决的。所谓悬而未决,是指SIGINT被阻塞还没有被处理
{
printf("/nSIGINT pending/n");
}
sigprocmask(SIG_SETMASK, &oldmask, NULL);//恢复被屏蔽的信号SIGINT

 //此处开始处理信号,调用信号处理函数
printf("SIGINT unblocked/n");

return (0);
}

//注意:上面还有一种方式:
sigprocmask(SIG_BLOCK, &newmask, NULL); //阻塞
sigprocmask(SIG_UNBLOCK, &newmask, NULL);//取消阻塞

signal和sigaction函数
这里写图片描述

signal函数相对简单,给定一个信号,给出信号处理函数即可。实际运用中,需要对不同到signal设定不同的到信号处理函数,SIG_IGN忽略/SIG_DFL默认,这俩宏也可以作为信号处理函数。同时SIGSTOP/SIGKILL这俩信号无法捕获和忽略。注意,经过实验发现,signal函数也会堵塞当前正在处理的signal,但是没有办法阻塞其它signal,比如正在处理SIG_INT,再来一个SIG_INT则会堵塞,但是来SIG_QUIT则会被其中断,如果SIG_QUIT有处理,则需要等待SIG_QUIT处理完了,SIG_INT才会接着刚才处理。

这里写图片描述

与这个函数相关的有个结构体sigaction
这里写图片描述

  1. 阻塞,sigaction函数有阻塞的功能,比如SIGINT信号来了,进入信号处理函数,默认情况下,在信号处理函数未完成之前,如果又来了一个SIGINT信号,其将被阻塞,只有信号处理函数处理完毕,才会对后来的SIGINT再进行处理,同时后续无论来多少个SIGINT,仅处理一个SIGINT,sigaction会对后续SIGINT进行排队合并处理。

  2. sa_mask,信号屏蔽集,可以通过函数sigemptyset/sigaddset等来清空和增加需要屏蔽的信号

  3. sa_flags如果取值为0,则表示默认行为。还可以取如下俩值:

SA_NODEFER,如果设置来该标志,则不进行当前处理信号到阻塞

SA_RESETHAND,如果设置来该标志,则处理完当前信号后,将信号处理函数设置为SIG_DFL行为

信号集操作函数

#include<signal.h>
int sigemptyset(sigset_t *set); //置空
初始化由set指向的信号集,清除其中所有信号
int sigfillset(sigset_t *set);   //把所有位置1
初始化由set指向的信号集,使其包括所有信号
int sigaddset(sigset_t *set,int signo);//把某个信号置到位图的某一位上

int sigdelset(sigset_t* set,int signo);//去掉指定信号的位
int sigismember(const sigset_t *set,int signo);//是否在信号集上

sigpending函数
这里写图片描述
读取当前进程的未决信号集,通过set参数传出。

信号的捕捉
这里写图片描述

内核实现信号的捕捉:
如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为信号捕捉。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例:用户程序注册了SIGQUIT信号的处理函数sighandler,当前正在执行main函数,这时发生中断或异常切换到内核态。在中断处理完毕后要返回用户态的main函数之前,检查到有信号SIGQUIT递达,内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。如果没有新的处理函数要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值