1、信号与系统调用的关系:当一个进程正在执行一个系统调用时,如果向该进程发送一个信号,那么对于大多数系统调用来说,这个信号在系统调用完成之前将不起作用,因为这些系统调用不能被信号打断。但是有少数几个系统调用能被信号打断,例如: wait(),pause()以及对慢速设备 (终端、打印机等)的 read()、 write()、 open()等。如果一个系统调用被打断,它就返回 -1,并将 errno设为 EINTR。可以用下列代码来处理这种情况:if (wirte(tfd,buf,SIZE)#include int catch(int sig); /信号绑定函数,必须在 main 函数前声明。int
2、 main(void)printf(“the program is starting.n“);signal(SIGINT, catch);/接受到 SIGINT 信号,执行绑定的 catch 函数/signal(SIGINT, SIG_DFL);/SIG_DFL 恢复成系统的缺省动作/signal(SIGINT, SIG_IGN);/告诉进程将 SIGINT 信号忽略,CTRL+Csleep(10);printf(“the end.n“);return 0;int catch(int sig)printf(“Interrupt called.n“);sleep(4) ;printf(“Inte
3、rrupt Func end.n”);执行它,结果如下:the program is starting.Interrupt calledInterrupt Func end.Interrupt calledInterrupt Func end.the end只能保存一个 CTRL+C。如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断:the program is starting.Interrupt calledQuit calledQuit ended.Interrupt Func end。the ended.在进程间发送信号:个进程通过对 signal()的调用来处
4、理其它进程发送来的信号。一个进程也可以向其它的进程发送信号。这一操作是由系统调用 kill()来完成的。 kill()在 linux 系统库 signal.h 中的函数声明:int kill(pid_t pid, int sig);参数 pid 指定了信号发送的对象进程:它可以是某个进程的进程标识符(pid),也可以是以下的值:如果 pid 为零,则信号被发送到当前进程所在的进程组的所有进程;如果 pid 为-1 ,则信号按进程标识符从高到低的顺序发送给全部的进程(这个过程受到当前进程本身权限的限制) ;如果 pid 小于-1,则信号被发送给标识符为 pid 绝对值的进程组里的所有进程。需要说
5、明的是,一个进程并不是向任何进程均能发送信号的,这里有一个限制,就是普通用户的进程只能向具有与其相同的用户标识符的进程发送信号。也就是说,一个用户的进程不能向另一个用户的进程发送信号。只有 root 用户的进程能够给任何线程发送信号。参数 sig 指定发送的信号类型。它可以是任何有效的信号。由于调用 kill()的进程需要直到信号发往的进程的标识符,所以这种信号的发送通常只在关系密切的进程之间进行,比如父子进程之间。下面是一个使用 kill()调用发送信号的例子。这个程序建立两个进程,并通过向对方发送信号 SIGUSR1 来实现它们之间的同步。这两个进程都处于一个死循环中,在接收对方发送的信号
6、之前,都处于暂停等待中。这是通过系统调用 pause()来实现的,它能够使一个程序暂停,直至一个信号到达,然后进程输出信息,并用 kill 发送一个信号给对方。当用户按了中断键,这两个进程都将终止。#include int ntimes=0;main()int pid,ppid;int p_action(), c_action();/* 设定父进程的 SIGUSR1 */signal(SIGUSR1,p_action);switch(pid=fork() case -1: /*fork 失败*/perror(“synchro“);exit(1);case 0: /*子进程模块*/* 设定子进程
7、的 SIGUSR1 */signal(SIGUSR1,c_action);/* 获得父进程的标识符*/ppid=getppid();for(;) sleep(1);kill(ppid,SIGUSR1);pause();- 58- Linux 网络编程/*死循环*/break;default: /*父进程模块*/for (;) pause();sleep(1);kill(pid,SIGUSR1);/*死循环*/p_action()printf(“Patent caught signal #%dn“,+ntimes);c_action()printf(“Child caught signal #%
8、dn“,+ntimes);程序运行结果如下:Patent caught signal #1Child caught signal #1Patent caught signal #2Child caught signal #2Patent caught signal #3Child caught signal #3Patent caught signal #4Child caught signal #4kill 命令用于向一个运行进程发送信号,它发送的信号默认为 SIGTERM,但是也可以指定为其它信号。我们可以直接用信号的号码来指定 kill 命令所发送信号之类型,也可以用符号名指定。比如可以
9、用下面的命令来完成向进程标识符为 1234 的进程发送 SIGINT 信号:kill s SIGINT 1234系统调用 alarm() 和 pause():alarm()它可以建立一个进程的报警时钟,在时钟定时器到时的时候,用信号向程序报告。alarm()系统调用在 Linux 系统函数库 unistd.h 中的函数声明如下:unsigned int alarm(unsigned int seconds);函数唯一的参数是 seconds ,其以秒为单位给出了定时器的时间。当时间到达的时候,就向系统发送一个 SIGARLM 信号。例如:alarm(60);这一调用实现在 60 秒后发一个 S
10、IGALRM 信号。alarm 不会象 sleep 那样暂停调用进程的执行,它能立即返回,并使进程继续执行,直至指定的延迟时间到达发出 SIGALRM信号。事实上,一个由 alarm()调用设置好的报警时钟,在通过 exec() 调用后,仍将继续有效。但是,它在 fork() 调用后中, 在子进程中失效。如果要使设置的报警时钟失效,只需要调用参数为零的 alarm():alarm(0)alarm()调用也不能积累。如果调用 alarm 两次,则第二次调用就取代第一次调用。但是,后一个 alarm 会把前一个 alarm 的剩余时间作为返回值,然后自己重新从 0 开始计数,前一个 alarm 就
11、此成为历史。当需要对某项工作设置时间限制时,可以使用 alarm()调用来实现。其基本方法为:先调用 alarm()按时间限制值设置报警时钟,然后进程作某一工作。如果进程在规定时间以内完成这一工作,就再调用 alarm(0)使报警时钟失效。如果在规定时间内未能完成这一工作,进程就会被报警时钟的 SIGALRM 信号中断,然后对它进行校正。下面这个程序使用上述方法来强制用户作出回答。在其中包括一个 quickreply()函数,它有一个参数 prompt,它是一个指向提示字符串的指针。quickreply 的返回值也是一个指针。它指向含有输入行信息的字符串。这个例行程序在试作五次之后,如果仍未得
12、到输入信息,就返回一个 null 指针。每当 quickreply 要提醒用户时,它就向终端发送 ASCII 码 007,这会使终端响铃。quickreply 调用了标准 I/O 库中的例行程序 gets()。gets()把标准输入上的下一行信息存入一个字符型数组,它返回一个指向该数组的指针。当到达文件末或出错时,gets 则返回一个 null 指针。函数 catch 是信号 SIGALRM 的关联函数,它完成对此信号的处理。catch 设置了一个 timed_out 标志,在 quickreply 中对这个标志进行检查,看它是否超过了规定的时限。#include #include #defi
13、ne TIMEOUT 5#define MAXTRIES 5#define LINESIZE 100#define BELL 007#define TRUE 1#define FALSE 0/* 判断超时是否已经发生的标志 */static int time_out;static char inputlineLINESIZE;char* quickreply (char* prompt);- 60- Linux 网络编程main()printf(“%sn“,quickreply(“Input“);char* quickreply (char* prompt)int (*was)(),catch
14、(),ntries;char* answer;/* 设定捕捉 SIGALRM 的的关联并保存原有关联*/was=signal(SIGALRM,catch);for (ntries=0;ntries“,prompt);/* 设定定时器*/alarm(TIMEOUT);/* 获取输入*/answer=gets(inputline);/* 关闭定时器*/alarm(0);if (!time_out)break;/* 恢复原有的 SIGALRM 关联*/signal(SIGALRM,was);return (time_out?(char*) 0):answer);/* SIGALRM 信号处理函数*/
15、catch()/* 设定超时标志*/time_out=TRUE;/* 响铃警告*/putchar(BELL);2系统调用 pause()系统调用 pause()能使调用进程暂停执行,直至接收到某种信号为止。pause()在 Linux系统函数库 unistd.h 中的函数声明如下:int pause(void);该调用没有任何的参数。它的返回始终是 -1 ,此时 errno 被设置为ERESTARTNOHAND 。下面这个程序为了在规定时间显示一个消息,使用了 alarm 和 pause。对它的调用方法如下:$tml minutes message-text /* SIGALRM 处理函数*/
16、setflag()alarm_flag=TRUE;main(int argc,char* argv)int nsecs;int i;if (argc2)fprintf(stderr,“Usage:tml #minutes messagen“);exit(1);if (nsecs=atoi(argv1)*60)=0)fprintf(stderr,“Invalid timen“);exit(2);/* 设定 SIGALRM 的关联动作*/signal(SIGALRM,setflag);- 62- Linux 网络编程/* 设定定时器*/alarm(nsecs);/*使用 pause()调用等待信号*/pause();if (alarm_flag)printf(BELLS);for (i=2;iargc;i+)printf(“%sn“,argvi);exit(0);