分类: LINUX
2010-11-08 00:53:02
信号通信
信号是UNIX 中所使用的进程通信的一种最古老的方法。它是在软件层次上对中断机制
的一种模拟,是一种异步通信方式。信号可以直接进行用户空间进程和内核进程之间的交互,
内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。它可以在任何时候发给某
一进程,而无需知道该进程的状态。如果该进程当前并未处于执行态,则该信号就由内核保
存起来,直到该进程恢复执行再传递给它为止;如果一个信号被进程设置为阻塞,则该信号
的传递被延迟,直到其阻塞被取消时才被传递给进程。
细心的读者是否还记得,在第2 章kill 命令中曾讲解到“−l”选项,这个选项可以列
出该系统所支持的所有信号列表。在笔者的系统中,信号值在32 之前的则有不同的名称,
而信号值在32 以后的都是用“SIGRTMIN”或“SIGRTMAX”开头的,这就是两类典型
的信号。前者是从UNIX 系统中继承下来的信号,为不可靠信号(也称为非实时信号);
后者是为了解决前面“不可靠信号”的问题而进行了更改和扩充的信号,称为“可靠信
号”(也称为实时信号)。那么为什么之前的信号不可靠呢?这里首先要介绍一下信号的
生命周期。
一个完整的信号生命周期可以分为3 个重要阶段,这3 个阶段由4 个重要事件来刻
画的:信号产生、信号在进程中注册、信号在进程中注销、执行信号处理函数,如图
8.6 所示。相邻两个事件的时间间隔构成信号生命周期的一个阶段。要注意这里的信号
处理有多种方式,一般是由内核完成的,当然也可以由用户进程来完成,故在此没有明
确画出。
一个不可靠信号的处理过程是这样的:如果发现该信号已经在进程中注册,那么就忽略
该信号。因此,若前一个信号还未注销又产生了相同的信号就会产生信号丢失。而当可靠信
号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此信号就
不会丢失。所有可靠信号都支持排队,而不可靠信号则都不支持排队。
用户进程对信号的响应可以有3 种方式。
· 忽略信号,即对信号不做任何处理,但是有两个信号不能忽略,即SIGKILL 及
SIGSTOP。
· 捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数。
· 执行缺省操作,Linux 对每种信号都规定了默认操作。
信 号 名 含 义 默 认 操 作
SIGHUP
该信号在用户终端连接(正常或非正常)结束时发出,
通常是在终端的控制进程结束时,通知同一会话内的各
个作业与控制终端不再关联
SIGINT 该信号在用户键入INTR字符(通常是Ctrl-C)时发出,终
端驱动程序发送此信号并送到前台进程中的每一个进程
SIGQUIT 该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-\)
来控制
SIGILL
该信号在一个进程企图执行一条非法指令时(可执行文
件本身出现错误,或者试图执行数据段、堆栈溢出时)
发出
SIGFPE
该信号在发生致命的算术运算错误时发出。这里不仅包
括浮点运算错误,还包括溢出及除数为0 等其他所有的
算术的错误
SIGKILL 该信号用来立即结束程序的运行,并且不能被阻塞、处
理和忽略
SIGALRM 该信号当一个定时器到时的时候发出终止
SIGSTOP 该信号用于暂停一个进程,且不能被阻塞、处理或忽略暂停进程
SIGTSTP 该信号用于交互停止进程,用户可键入SUSP字符时(通
常是Ctrl+Z)发出这个信号
SIGCHLD 子进程改变状态时,父进程会收到这个信号忽略
SIGABORT
信号发送与捕捉
发送信号的函数主要有kill()、raise()、alarm()以及pause(),下面就依次对其进行介绍。
1.kill()和raise()
(1)函数说明
kill 函数同读者熟知的kill 系统命令一样,可以发送信号给进程或进程组(实际上,kill
系统命令只是kill函数的一个用户接口)。这里要注意的是,它不仅可以中止进程(实际上发
出SIGKILL信号),也可以向进程发送其他信号。
与 kill函数所不同的是,raise函数允许进程向自身发送信号。
(1) 函数格式
所需头文件#include
#include
函数原型int kill(pid_t pid,int sig)
pid:
正数:要发送信号的进程号
0: 信号被发送到所有和pid进程在同一个进程组的进程
-1:信号发给所有的进程表中的进程(除了进程号最大的进程外)
sig:信号
函数返回值
成功:0
出错:-1
raise函数的语法要点。
所需头文件#include
#include
函数原型int raise(int sig)
函数传入值sig:信号
函数返回 成功:0
出错:-1
(3)函数实例
下面这个示例首先使用fork创建了一个子进程,接着为了保证子进程不在父进程调用kill
之前退出,在子进程中使用raise 函数向子进程发送SIGSTOP 信号,使子进程暂停。接下来
再在父进程中调用kill 向子进程发送信号,在该示例中使用的是SIGKILL,读者可以使用其
他信号进行练习。
/*kill.c*/
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
int ret;
/*创建一子进程*/
if((pid=fork())<0){
perror("fork");
exit(1);
}
if(pid == 0){
/*在子进程中使用raise函数发出SIGSTOP信号*/
raise(SIGSTOP);
exit(0);
}
else{
/*在父进程中收集子进程发出的信号,并调用kill函数进行相应的操作*/
printf("pid=%d\n",pid);
if((waitpid(pid,NULL,WNOHANG))==0){
if((ret=kill(pid,SIGKILL))==0)
printf("kill %d\n",pid);
else{
perror("kill");
}
}
}
}
该程序运行结果如下所示:
[root@(none) tmp]# ./kill
pid=15317
kill 15317
2.alarm()和pause()
(1)函数说明
alarm 也称为闹钟函数,它可以在进程中设置一个定时器,当定时器指定的时间到时,
它就向进程发送SIGALARM 信号。要注意的是,一个进程只能有一个闹钟时间,如果在调
用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。
pause 函数是用于将调用进程挂起直至捕捉到信号为止。这个函数很常用,通常可以用
于判断信号是否已到。
(2) 函数格式
所需头文件#include
函数原型unsigned int alarm(unsigned int seconds)
函数传入值seconds:指定秒数
函数返回值
成功:如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟
时间的剩余时间,否则返回0
出错:-1
pause函数的语法要点。
所需头文件#include
函数原型int pause(void)
函数返回值-1,并且把error 值设为EINTR
(3)函数实例
该实例实际上已完成了一个简单的sleep函数的功能,由于SIGALARM默认的系统动作
为终止该进程,因此在程序调用pause之后,程序就终止了。如下所示:
/*alarm.c*/
#include
#include
#include
int main()
{
int ret;
/*调用alarm定时器函数*/
ret=alarm(5);
pause();
printf("I have been waken up.\n",ret);
}
[root@(none) tmp]#./alarm
闹钟
chinaunix网友2010-11-08 15:34:16
很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com