概念
信号是一种软件中断,它提供了一种处理异步事件的方法,也是进程间唯一的异步通信方式。
信号的来源:
①硬件方式:按键会产生信号,比如ctrl+c组合键,产生一个SIGINT信号;硬件异常信号,比如除数为0,无效存储访问,由cpu通知内核,内核生成相应信号。
②软件方式: 用户在终端下调用kill命令向进程发送任意信号;进程调用kill或sigqueue函数发送信号;当某种条件已经具备时发送信号,如settimer定时器超时产生SIGALRM信号。
信号的种类:
使用kill -l 命令系统支持的全部信号。

①SIGHUP:用户退出shell,由该shell启动的所有进程将收到这个信号,默认动作为终止进程。
②SIGINT:用户按下ctrl+c组合键,用户终端向正在运行中的由该终端启动的进程发送此信号,默认动作为终止进程。
③SIGQUIT:用户按下ctrl+\组合键,用户终端向正在运行中的由该终端启动的进程发送此信号,默认动作为终止进程并产生core文件。
④SIGILL:cpu检测到某进程执行了非法指令,默认动作为终止进程并产生core文件。
⑤SIGTRAP:由断点指令或其他trap指令产生,默认动作为终止进程并产生core文件。
⑥SIGABRT:调用abort函数产生该信号,,默认动作为终止进程并产生core文件。
⑦SIGBUS:非法访问内存地址,包括内存地址对齐出错,默认动作为终止进程并产生core文件。
⑧SIGFPE:在发生致命的算术运算时产生,不仅包括浮点运算错误,还包括溢出及除数为0等错误,默认动作为终止进程并产生core文件。
⑨SIGKILL:无条件终止进程,本信号不能被忽略、处理和阻塞。默认动作为终止进程,它向系统管理员提供了一种可以杀死任何进程的方法。
⑩SIGUSR1:用户定义的信号,即程序员可以在程序中定义并使用该信号。默认动作为终止进程。
⑪SIGSEGV: 指示进程进行了无效的内存访问。默认动作为终止进程并产生core文件。
⑫SIGUSR2:另一个用户定义的信号,即程序员可以在程序中定义并使用该信号。默认动作为终止进程。
⑬SIGPIPE:向一个没有读端的管道写数据,默认动作为终止进程。
⑭SIGALRM:定时器超时,超时的时间由系统调用alarm设置,默认动作为终止进程。
⑮SIGTERM:程序结束信号,与SIGKILL不同,这个信号可以被阻塞和处理。通常要求程序正常退出,执行shell命令kill时,缺省产生这个信号。默认动作为终止进程。
⑯SIGSTKFLT :该信号仅仅在Linux中有定义,出现在早期的Linux系统中,其原意是用于数字协处理器的堆栈错误,该信号在内核中并不会被产生,但是为了兼容性被保留下来了。
⑰SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略该信号。
⑱SIGCONT:让一个暂停的进程继续执行。
⑲SIGSTOP:停止进程的执行,注意它和SIGTERM以及SIGINT的区别。该进程还未结束,只是暂停执行,本信号不能被忽略、阻塞或处理。默认动作为暂停进程。
⑳SIGTSTP:停止进程的执行,按下ctrl+z组合键产生,本信号可以被忽略或处理。默认动作为暂停进程。
从34) SIGRTMIN到64 )SIGRTMAX为linux的实时信号,它们没有固定含义,或者说由用户自由使用,注意linux线程使用了前3个实时信号。
所有的实时信号默认动作为终止进程。
可靠与优先级
从1号SIGHUP信号到31号SIGSYS之间的信号都是继承自UNIX系统,是不可靠信号。
linux系统根据posix.4标准定义了SIGRTMIN(33号)至SIGRTMANX(64号)之间的信号,是可靠信号,也称实时信号。
在linux系统中,信号的可靠性是指信号是否会丢失,或者说信号是否支持排队。
当信号产生后,内核会在进程表中设置某种形式的标志,当内核设置了这个标志,我们说内核向一个进程递送了一个信号,在信号产生和递送之间的时间间隔,称为信号未决。
被递送多次的信号(即信号在未决队列里排队)称为可靠信号,只被递送一次的称为不可靠信号。
如果一个进程有多个未决信号,则对于同一个未决的实时信号,内核按照发送的顺序来递送;如果存在多个实时未决信号,内核按照信号的编号越小越先递送;如果即存在不可靠信号,又存在可靠信号,内核优先递送不可靠信号。
进程对信号的响应
当信号发生时,用户可以要求进程以下列3种方式之一来做出响应:
① 捕捉信号:对于要捕捉的信号,可以为其制定信号处理函数,信号发生时改函数自动被调用
② 忽略信号:大多数信号都可使用这种方式处理,但是SIGKILL和SIGSTOP这两个信号不能被忽略与捕捉。
③按照系统默认方式处理:即终止进程。

signal函数捕捉并处理信号。
typedef void (*sighandler_t)(int); sighandler_t是一个函数指针型,它指向的函数有一个int参数,返回值是void。
#include <signal.h>
#include <stdio.h>
void handler_sigint(int signo)
{
printf("recv SIGINT %d\n",signo);
}
int main()
{
signal(SIGINT, handler_sigint);
while(1)
;
return 0;
}

按下ctrl+c组合键,产生信号SIGINT,被程序捕捉并处理。最后按下ctrl+\组合键发送SIGQUIT信号,由于程序没有处理该信号,按照默认方式处理–即结束进程。
信号的发送
信号的发送主要由函数kill、raise、sigqueue、alarm、setitimer以及abort来完成。

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
① kill第一个参数是进程号,第二个参数是信号编号。
函数执行成功返回0,错误返回-1.
只有具有root权限的进程才能向其他任意进程发送信号,非root权限的进程只能向属于同一个组或同一个用户的进程发送信号。
② raise函数是ANSI C而非POSIX标准定义的。

在单线程中,它等价于 kill(getpid(), sig);
③sigqueue 函数是一个比较新的发送信号函数,它支持信号带有参数。与函数sigaction配合使用,因此这个额外的信号带有参数就能完成数据的传递。
④alarm 函数用来设置定时器,定时器超时会产生SIGALRM信号给调用进程。

参数代表设定的秒数,经过seconds秒后,内核将给调用该函数的进程发送SIGALRM信号。如果seconds为0,则不再发送SIGALRM信号。
最新一次调用alarm函数将取消之前一次的设定。
注意alarm只设定为发送一次信号,如果要多次发送,就要对alarm进行多次调用。
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void send_ip()
{
printf("send a icmp echo request panckt\n");
}
void recv_ip()
{
printf("recv a ip\n");
while(1)
;
}
void handler_sigalarm(int signo)
{
//信号处理程序再次设定定时器
send_ip();
alarm(2);
}
int main()
{
//安装信号处理
signal(SIGALRM, handler_sigalarm);
//触发信号给本进程
raise(SIGALRM);
recv_ip();
return 0;
}
如果没有raise的话,第一次就无法触发,在信号处理程序中反复设定定时器,以获得多次发送效果。

执行结果来看,当程序运行到 raise(SIGALRM)时,已经触发了handler_sigalarm,所以输出send a icmp echo request panckt,之后再执行
recv_ip。
⑤setitimer函数 也是用来设置定时器,它的功能更强大。

第一个参数which制定使用哪一个定时器,种类有ITIMER_REAL,ITIMER_VIRTUAL,ITIMER_PROF。
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
void handler_sigtime(int signo)
{
switch (signo)
{
case SIGALRM:
printf("recv SIGALRM\n");
break;
case SIGPROF:
printf("recv STGPROF\n");
break;
default:
break;
}
}
int main()
{
struct itimerval value;
//相关信号共用一个处理函数
signal(SIGALRM, handler_sigtime);
signal(SIGPROF, handler_sigtime);
//第一次1秒触发信号
value.it_value.tv_sec=1;
value.it_value.tv_usec=0;
//第二次开始每5秒触发信号
value.it_interval.tv_sec=5;
value.it_interval.tv_usec=0;
//设置定时器
setitimer(ITIMER_REAL,&value,NULL);
setitimer(ITIMER_PROF,&value,NULL);
while(1)
;
return 0;
}
本文详细介绍了Linux系统编程中的信号机制,包括信号的来源、种类、处理方式和发送方法。信号作为一种软件中断,提供了处理异步事件的方法,并且是进程间唯一的异步通信方式。内容涵盖SIGHUP到SIGALRM等常见信号的默认行为,以及如何通过函数如kill、raise、sigqueue来发送信号。
1242

被折叠的 条评论
为什么被折叠?



