信号是linux系统中一种常用的通信机制,A给B发送信号,B在收到信号之前执行自己的代码,收到信号后,不管执行什么程序,都暂停运行,去处理信号,处理完毕后再继续执行原来的程序,是一种软中断。
Linux常规信号一览表
1) SIGHUP: 当用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程
2) SIGINT:当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动
作为终止进程。
3) SIGQUIT:当用户按下<ctrl+\>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信
号。默认动作为终止进程。
4) SIGILL:CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件
5) SIGTRAP:该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件。
6) SIGABRT: 调用abort函数时产生该信号。默认动作为终止进程并产生core文件。
7) SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。
8) SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件。
9) SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。
10) SIGUSE1:用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。
11) SIGSEGV:指示进程进行了无效内存访问。默认动作为终止进程并产生core文件。
12) SIGUSR2:另外一个用户自定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。
13) SIGPIPE:Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。
14) SIGALRM: 定时器超时,超时的时间 由系统调用alarm设置。默认动作为终止进程。
15) SIGTERM:程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行shell命令Kill时,缺省产生这个信号。默认动作为终止进程。
16) SIGSTKFLT:Linux早期版本出现的信号,现仍保留向后兼容。默认动作为终止进程。
17) SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。
18) SIGCONT:如果进程已停止,则使其继续运行。默认动作为继续/忽略。
19) SIGSTOP:停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为暂停进程。
20) SIGTSTP:停止终端交互进程的运行。按下<ctrl+z>组合键时发出这个信号。默认动作为暂停进程。
21) SIGTTIN:后台进程读终端控制台。默认动作为暂停进程。
22) SIGTTOU: 该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程。
23) SIGURG:套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号。
24) SIGXCPU:进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程。
25) SIGXFSZ:超过文件的最大长度设置。默认动作为终止进程。
26) SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程。
27) SGIPROF:类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程。
28) SIGWINCH:窗口变化大小时发出。默认动作为忽略该信号。
29) SIGIO:此信号向进程指示发出了一个异步IO事件。默认动作为忽略。
30) SIGPWR:关机。默认动作为终止进程。
31) SIGSYS:无效的系统调用。默认动作为终止进程并产生core文件。
34) SIGRTMIN ~ (64) SIGRTMAX:LINUX的实时信号,它们没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程。
1.kill函数
函数原型:int kill(pid_t pid, int sig)
函数描述:给指定进程发送信号(不一定杀死),第一个参数是进程ID,是发送信号的目标,第二个参数是发送信号的类别,返回值为0,表示成功,返回值为-1,表示失败。
举例:子进程杀死父进程
#include <iostream>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
using namespace std;
int main()
{
int i;
for (; i<5; i++){
pid_t pid=fork();
if (pid==0) break;
}
if (i==5){
cout<<"i am the father"<<endl;
sleep(1);
}else if(i==2){
sleep(1);
kill(getppid(), SIGKILL);
}
}
2.raise函数
函数原型:int raise(int sig)
函数描述:给当前的进程发送信号(自己给自己发),唯一入口参数是信号的类别,这个函数相当于kill(getpid(), sig)。成功时返回值为0,失败时返回值为非零。
3.abort函数
函数原型:void abort(void)
函数描述:给当前信号发送异常终止信号6) SIGABRT(自己把自己杀死,并留下遗书),并产生core文件
4.alarm函数
函数原型:unsigned int alarm(unsigned int seconds);
函数描述:设置定时器,在指定入口参数seconds以后,内核会给当前进程发送14)SIGALRM信号。进程收到该信号,默认动作终止。函数返回值是上次闹钟结束时剩余时间。
举例
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
int ret = alarm(6);
//这个语句执行的过程中,也就是在计时过程中,继续往下执行程序的,不是这个6s执行完以后再去执行下面
//的语句
cout<<ret<<endl;//该返回值是0,因为这是在进程中第一次使用alarm函数
sleep(3);
ret = alarm(2);//该返回值是3,因为上一个alarm函数计时到现在剩余3
cout<<ret<<endl;
while (1){
cout<<"come to hit me!\n";
sleep(1);
}//当alarm计时结束时,按默认设置就会结束进程
}
5.setitimer函数
函数原型:int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
函数描述:这个函数可以周期性计时,其中
which:指定定时方式
① 自然定时:ITIMER_REAL → 14)SIGLARM 计算自然时间
② 虚拟空间计时(用户空间):ITIMER_VIRTUAL → 26)SIGVTALRM 只计算进程占用cpu的时间
③ 运行时计时(用户+内核):ITIMER_PROF → 27)SIGPROF 计算占用cpu及执行系统调用的时间
new_value和old_value都是结构体itimerval ,new_value是这一次设置的周期参数,old_value存储的是进程中上一个setitimer函数的参数。一般情况下只用new_value,old_value一般设为NULL。
itimerval结构体具体结构如下:
struct itimerval {
struct timeval it_interval; /* Interval for periodic timer *///周期定时的周期
struct timeval it_value; /* Time until next expiration *///第一次定时开始的时间
};//这个结构体主要表达周期定时的一些参数,比如什么时候开始第一次定时,每个周期有多长。
struct timeval {
time_t tv_sec; /* seconds *///秒
suseconds_t tv_usec; /* microseconds *///微秒
};//这个结构体就是表达一个精确到微秒的时间
返回值:在成功的时候返回0,在失败的时候返回-1。
举例:自己编写alarm函数
#include <iostream>
#include <unistd.h>
#include <sys/time.h>
using namespace std;
unsigned int myalarm(int seconds)
{
struct itimerval oldit, myit={{0,0},{seconds,0}};
setitimer(ITIMER_REAL, &myit, &oldit);
//这里的&oldit只是一个地址参数,用来存储进程中上一个setitimer函数中的myit中的参数
cout<<oldit.it_value.tv_sec<<" "<<oldit.it_value.tv_usec<<endl;
return oldit.it_value.tv_sec;
}
int main()
{
int ret;
ret=myalarm(5);
//这次setitimer函数是进程中第一个出现的,所以oldit为0
cout<<"ret = "<<ret<<endl;
sleep(3);
ret=myalarm(3);
//这次的setitimer函数是进程中第二个出现的,oldit中第一个参数it_interval就是上一个setitimer
//函数中的0,第二个参数it_value就是上一个setitimer函数中还剩余的时间,本来函数运行5秒发出闹钟
//信号,但是运行三秒以后,执行另外一个setitimer函数了,所以还剩2秒,这就是it_value的值。
//不过由于还有其他语句执行需要时间,所以是接近两秒。
cout<<"ret = "<<ret<<endl;
return 0;
}
6.信号集
信号集可以理解为信号位图,在linux系统中常用的信号是1-31,全部信号是1-63,可以理解为这64个信号依次构成一个64位的二进制数,或者64 个方格组成的位图。在linux中,这个信号集用sigset_t数据结构来表示。
信号集操作函数
int sigemptyset(sigset_t *set); 将某个信号集清0 成功:0;失败:-1
int sigfillset(sigset_t *set); 将某个信号集置1 成功:0;失败:-1
int sigaddset(sigset_t *set, int signum); 将某个信号加入信号集 成功:0;失败:-1//加入信号集就是在信号对应的数位上置1
int sigdelset(sigset_t *set, int signum); 将某个信号清出信号集 成功:0;失败:-1//清出信号集就是在信号对应的数位上置0
int sigismember(const sigset_t *set, int signum);判断某个信号是否在信号集中 返回值:在集合:1;不在:0;出错:-1
sigset_t类型的本质是位图。但不应该直接使用位操作,而应该使用上述函数,保证跨系统操作有效。
其中参数signum可以理解为位图当中的第几位,或者信号前的编号。
sigprocmask函数
用来屏蔽或者解除屏蔽信号集(理解为设置阻塞信号集,屏蔽可以理解为阻塞)。
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
set:传入参数,是一个位图,set中哪位置1,就表示当前进程屏蔽哪个信号。
oldset:传出参数,保存旧的信号屏蔽集。
how参数取值: 假设当前的信号屏蔽字为mask
- SIG_BLOCK: 当how设置为此值,set表示需要屏蔽的信号。相当于 mask = mask|set
- SIG_UNBLOCK: 当how设置为此,set表示需要解除屏蔽的信号。相当于 mask = mask & ~set
- SIG_SETMASK: 当how设置为此,set表示用于替代原始屏蔽集的新屏蔽集。相当于 mask = set,调用sigprocmask解除了对当前若干个信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。
sigpending函数
用来读取当前进程的未决信号集。
int sigpending(sigset_t *set);地址参数set就是存放读取结果。
运行举例:
1.信号集操作函数举例
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
using namespace std;
int main()
{
sigset_t sigproc;//这个就可以看成是一个变量,并且信号集操作函数就是对这个变量数值中
//的一个或多个数位进行操作。
sigemptyset(&sigproc);//将所有数位都置0
sigaddset(&sigproc, SIGINT);//将第2位置1
sigaddset(&sigproc, SIGQUIT);//将第3位置1
int i=1;
for (; i<32; i++){
if (sigismember(&sigproc,i)==1)
cout<<1;
else cout<<0;
}
sleep(1);
cout<<endl;
}
结果图
打印未决信号集
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
using namespace std;
int main()
{
sigset_t pend, sigproc;//pend作为未决信号集,sigproc作为阻塞信号集
/*设置阻塞信号集*/
sigemptyset(&sigproc);//清空阻塞信号集
sigaddset(&sigproc, SIGINT);//将第2位置1
sigaddset(&sigproc, SIGQUIT);//将第3位置1
sigprocmask(SIG_BLOCK, &sigproc, NULL);//将sigproc设置为阻塞信号集,其中2,3位
//是1,就是表明需要阻塞的信号。
while (1){
sigpending(&pend);//将未决信号读取到pend变量中
int i=1;
for (; i<32; i++){
if (sigismember(&pend,i)==1)
cout<<1;
else cout<<0;
}
sleep(1);
cout<<endl;
}
}
执行结果
当按下ctrl+c时,2号位阻塞,未决信号显示1
当按下ctrl+/时,3号位阻塞,未决信号显示1
7.信号捕获
函数原型:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
signum是需要捕获的信号参数,act是传入参数,传入新的处理方式,oldact是传出参数,是旧的处理方式
其中sigaction结构体如下:
truct sigaction {
void (*sa_handler)(int);//指定信号捕捉后的处理函数名(即注册函数)。也可赋值为
//SIG_IGN表忽略 或 SIG_DFL表执行默认动作
void (*sa_sigaction)(int, siginfo_t *, void *);//当sa_flags被指定为
//SA_SIGINFO标志时,使用该信号处理程序。与sa_action相比,这个函数入口参数更多,更复杂(很少使用)
sigset_t sa_mask;//调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在
//处理函数被调用期间屏蔽生效,是临时性设置。
int sa_flags;//通常设置为0,表使用默认属性。
void (*sa_restorer)(void);//该元素是过时的,不应该使用,POSIX.1标准将不指定
//该元素。(弃用)
};
程序举例如下,用sigaction捕捉SIGALRM信号
#include <iostream>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
using namespace std;
void catch_sig(int sig)//注册函数,当捕捉到信号时,就调用这个函数
{
cout<<"catch "<<sig<<endl;
}
int main()
{
struct sigaction act;
act.sa_flags=0;
act.sa_handler=catch_sig;
sigemptyset(&act.sa_mask);//全部置0,这样就没有阻塞信号
sigaction(SIGALRM, &act, NULL);
itimerval myit={{3,0},{2,0}};
setitimer(ITIMER_REAL, &myit, NULL);
while (1) {
cout<<"kill me\n";
sleep(1);
}
return 0;
}
8.SIGCHLD信号使用
SIGCHLD是在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程,可以通过这个信号来结束子进程。
举例:利用SIGCHLD回收子进程
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
using namespace std;
void catch_sig(int sig)
{
pid_t wpid;
while ((wpid=waitpid(-1, NULL, WNOHANG))>0)
cout<<"wait child"<<wpid<<endl;
}
int main()
{
int i=0;
for (; i<10; i++){
int pid=fork();
if (pid==0) break;
}
if (i<10){
cout<<"i am "<<i<<" child, my pid = "<<getpid()<<endl;
sleep(i);
}else if (i==10){
struct sigaction act;
act.sa_handler=catch_sig;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(SIGCHLD, &act, NULL); //在父进程中捕捉信号
while (1){
sleep(1);
}
}
}