【Linux】进程信号
一、首先,用 kill -l 命令可以查看系统定义的信号列表:
每个信号都有一个编号和一个宏定义名称,这些宏定义可以在 signal.h 中查看,例如:#define SIGINT 2 。编号34以上的信号为实时信号。
二、信号常见的三种处理方式:
(1)忽略此信号。(SIG_IGN)
(2)执行信号的默认处理动作。(SIG_DFL)
(3)提供一个信号处理函数,捕捉信号进行自定义处理。
三、信号的产生:
1、常见的几种产生信号方式:
a.用户产生信号
b.系统异常产生信号
c.自定义捕捉
2、通过终端按键产生信号:
SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程,并且Core Dump
Core Dump (核心转储):当一个进程异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是Core,这叫做 Core Dump。
系统默认情况不予 Core 分配空间:
可使用命令 ulimit -c 1024 给Core 分配 1024个字节空间:
实例:写一个死循环
[lize-h@localhost 0409_Siganl]$ cat core_Dump.c
#include<stdio.h>
int main()
{
printf("pid is %d\n",getpid());
while(1);
return 0;
}
3、调用系统函数向进程发信号:
(1)我们可以用 kill 命令向进程发信号:
[lize-h@localhost 0409_Siganl]$ ./a.out &
[1] 3102
[lize-h@localhost 0409_Siganl]$ kill -SIGSEGV 3102
[lize-h@localhost 0409_Siganl]$
[1]+ 段错误 (core dumped) ./a.out
[lize-h@localhost 0409_Siganl]$ cat core_Dump.c
#include<stdio.h>
int main()
{
//printf("pid is %d\n",getpid());
while(1);
return 0;
}
令程序在后台执行,用 kill 命令向进程发送信号,令进程异常终止,异常信息存入core文件。
(2)发送信号的函数有:
#include<signal.h>
int kill(pid_t pid,int signo); //向任意进程发送任意信号;成功返回0,出错返回-1。
int raise(int signo); //向自己发送任意信号;成功返回0,出错返回-1。
#include<stdlib.h>
void abort(void); //向自己发送 abort 信号
(3)alarm函数:
#include<unistd.h>
unsigned int alarm( unsigned int seconds);
/*
调用alarm函数可设置一个闹钟;当参数seconds设置为0表示取消闹钟,返回值为以前设置的
时间剩余的秒数;当参数seconds设置为8表示8秒后触发闹钟,向当前进程发送SIGALRM信号,
该信号的默认处理动作是终止当前进程。
*/
四、阻塞信号
1、信号的相关概念:
(1)实际执行信号的处理动作称为信号递达。
(2)信号从产生到递达之间的状态,称为信号未决(pending)。
(3)进程可以选择阻塞(Block)某个信号。
(4)被阻塞的信号产生时将保持在未决状态,直到信号解除对此信号的阻塞,才执行递达的动作。
(5)阻塞和忽略不同,只要信号被阻塞就不会递达,而忽略是在递达后可选的一种处理动作。
(6)发送信号相当于更改PCB中的位图。
2、在内核中的表示:
Linux信号的实现:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。
3、sigset_t 信号集:
未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。
4、信号集操作函数:
#include<signal.h>
int sigemptyset(sigset_t* set); //初始化set所指向的信号集,使其中所有信号的对应bit清零。
int sigfillset(sigset_t* set); //初始化set所指向的信号集,使其中所有信号的对应bit置1。
int sigaddset(sigset_t* set,int signo); //在信号集中添加编号为signo的信号
int sigdelset(sigset_t* set,int signo); //删除信号集中编号为signo的信号
//以上四个函数成功返回0,出错返回-1。
int sigismember(const sigset_t* set,int signo);
//sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含返回0,出错返回-1。
sigprocmask函数:
调用该函数可以读取或更改进程的信号屏蔽字(阻塞信号集)
#include<signal.h>
int sigprocmask(int how,const sigset_t* set,sigset_t* oset);
//若调用成功返回0,出错返回-1
如果oset为空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,以下为how参数的可选值。
sigpending函数:
#include<signal.h>
int sigpending(sigset_t* set)
//读取当前进程的未决信号集,通过set参数传出。调用成功返回0,出错返回-1。
范例:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void printsigset(sigset_t *set)
{
int i=0;
for(;i<32;i++)
{
if(sigismember(set,i))
{
putchar('1');
}
else
{
putchar('0');
}
}
puts("");
}
int main()
{
sigset_t s,p;
sigemptyset(&s);
sigaddset(&s,SIGINT);
gprocmask(SIG_BLOCK,&s,NULL);
while(1)
{
sigpending(&p);
printsigset(&p);
sleep(1);
}
return 0;
}
运行结果:
[lize-h@localhost 0409_Siganl]$ ./a.out
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
^C10100000000000000000000000000000
10100000000000000000000000000000
10100000000000000000000000000000
10100000000000000000000000000000
^Z
[1]+ Stopped ./a.out
[lize-h@localhost 0409_Siganl]$