1. 信号的产生
1.1 硬件产生
ctrl + c:SIGINT(2)
ctrl + z:SIGTSTP(20)
ctrl + |:SIGQUIT(3)
如何查看信号的信息以及查看信号的默认处理动作?
man 7 signal
操作系统对信号的处理动作:
Term:终止
Ign:忽略
Core:终止+产生coredump文件
Stop:停止
Cont:继续
如果某一个信号的处理动作是“core”:
1.默认是需要完成终止进程+产生coredump文件
2.产生coredump文件,依赖ulimit -a==>“core file size”和磁盘大小
core file size 设置成为unlimited
信号具体的信息:信号的名称 + 信号的值(整数)+ action + 描述
1.2 软件产生
kill命令:
kill [pid]可以终止一个进程
kill -[num] [pid] :给进程号为pid的进程发送一个信号值为num的信号
kill函数
int kill(pid_t pid, int sig);
功能:给pid进程发送sig信号
给自己发信号
eg:kill (getpid(),9)
int raise(int sig);
功能:谁调用给谁发送sig信号
2. 信号的注册
1.一个进程收到一个信号,这个过程我们称之为注册。
2.信号的注册和信号的注销并不是一个过程,是两个独立的过程。
3.信号的注册分为两种情况,非可靠信号的注册和可靠信号的注册
1.在操作系统内核“struct task_struct”结构体内部有一个变量:“struct sigpending pending”
2.内核定义的结构体“struct sigpending”当中有两个变量:一个是内核定义的双向链表第二个是:sigset_t signal
3.内核定义的类型“sigset_t”为一个结构体,在结构体内部有一个变量,该变量为一个数组,是无符号长整型的数组
unsigned long sig[_NSIG_WORDS];
1.信号的注册本质上就是在使用sig数组,但是并不是按照数组类型的方式在使用,而是按照位图(比特为)的方式在使用:
eg:某一个信号注册,则将某一个信号对应的比特位置为1
2.sig数组的比特位个数远远大于62,剩余的比特位为保留位
struct sigpending ==> sigset_t signal ==> sig[xxx]
一般在信号注册的时候,称之为操作sig位图(sig[xxx]),内核当中对于注册的时候,还有一个sigqueue队列,信号的注册逻辑位,将信号对应的sig位图当中的比特位置为1,并且在sigqueue队列当中添加一个sigqueue节点;通过注册第一个信号两次,来区分可靠信号和非可靠信号的注册逻辑。
非可靠信号的注册:
第一次:
1.更改信号对应在sig位图当中的比特位(0–1)
2.在sigqueue队列当中添加sigqueue节点
第二次:
1.更改信号对应的sig位图当中的比特位(1–1)
2.对于第二次的信号,不添加sigqueue节点到sigqueue队列当中
注意:
如果有多个同一个信号来注册,那么对于非可靠信号而言,只会添加一次sigqueue节点,换言之,只注册了一次。
可靠信号的注册:
第一次:
1.更改信号对应在sig位图当中的比特位(0–1)
2.在sigqueue队列当中添加sigqueue节点
第二次:
1.更改sig位图(1–1)
2.在sigqueue队列当中添加sigqueue节点
注意:
如果有多次同一个可靠信号来注册,那么对于可靠信号而言,会添加多次sigqueue节点,换言之,会注册多次
3. 信号的注销
3.1 非可靠信号
1.将信号对应到sig位图当中的比特位置为0
2.将对应的非可靠信号的sigqueue节点进行出队操作
3.2 可靠信号
1.先将可靠信号对应的sigqueue进行出队操作
2.判断sigqueue队列当中是否有同类的可靠信号的sigqueue节点,如果有,不会将sig位图当中对应的比特位置为0,如果没有,将sig位图当中对应的比特位置为0
4. 信号的处理方式
默认处理方式: 在操作系统内核当中定义好了
宏:SIG_DFL
忽略处理方式: 操作系统定义进程收到某一个信号之后,忽略掉(进程即使收到了某个信号,进程也不会做任何事情)
宏:SIG_IGN
eg:僵尸进程:子进程先于父进程退出,子进程会向父进程发送SIGCHLD信号,父进程对SIGCHLD信号是忽略处理的,所以父进程并不会做任何事情,导致子进程的资源没有进程进行回收,从而子进程变成僵尸进程
自定义处理方式:
1.程序员可以定义某一个信号的处理方式
2.函数
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
signum:待要更改的信号的值
handler:函数指针,接收一个函数的地址,这个函数没有返回值,有一个int类型的参数自定义signum这个信号的处理方式,定义为handler这个函数指针保存的函数地址对应的函数,换句话说,当进程收到signum这个信号的时候,就会调用handler当中保存的函数。
代码
现象
1.signal函数向内核注册了一个信号的处理函数,调用signal函数的时候,并没有调用注册的函数(注册的函数在进程收到进程之后才调用),这种方式称之为“回调”。
int sigaction(int signum, const struct sigaction *act, struct sigaction * oldact);
signum:待要自定义处理的信号
act:要将信号处理方式更改为act
oldact:原来的处理方式
struct sigaction
{
void (*sa_hander)(int);//保存信号默认函数处理方式的函数指针
void (*sa_sigaction)(int, siginfo_t * , void *);//函数指针,但是这个函数指针需要配合sa_flags一起使用,当sa_flags当中的值为SA_SIGINFO的时候,信号处理是按照“sa_sigaction”当中保存的函数地址来处理的
sigset_t sa_mask;//当进程在处理某一个信号的时候,有可能还会收到其他的信号,此时,其他的信号就暂时存在sa_mask当中。
int sa_flags;
void (*sa_restorer)(void);//保留字段
};