标签:style class blog code http tar
信号是UNIX 系统所使用的进程通信方法中,最古老的一种。信号不但能从内核发往一个进程,也能从一个进程发往另一个进程。例如,用户在后台启动了一个要运行较长时间的程序,如果想中断其执行,可以用kill 命令把SIGTERM信号发送给这个进程,SIGTERM 将终止此进程的执行。信号还提供了向UNIX 系统进程传送软中断的简单方法。信号可以中断一个进程,而不管它正在作什么工作。由于信号的特点,所以不用它来作进程间的直接数据传送,而把它用作对非正常情况的处理。由于信号本身不能直接携带信息,这就限制了它作为一项通用的进程通信机制。
.SIGHUP信号
UNIX 的系统调用signal()用于接收一个指定类型的信号,并可以指定相应的方法。这就是说,signal()能够将指定的处理函数与信号向关联。它在Linux 系统库signal.h 中的函数声明如下:
int signal (int sig, __sighandler_t handler);
Signal()有两个参数:
第一个参数sig 指明了所要处理的信号类型,它可以取除了SIGKILL 和SIGSTOP 外的任何一种信号。参数handler 描述了与信号关联的动作,它可以取以下三种值:
1.一个无返回值的函数地址。
void func(int sig);
2.SIG_IGN
这个符号表示忽略信号。执行了相应的signal()调用好,进程会忽略类型为sig 的信号。
3.SIG_DFL
这个符号表示恢复系统对信号的默认处理。
在父进程中设定的信号和函数的关联关系会被exec()调用自动用SIG_DFL 恢复成系统的缺省动作,这是因为在exec 的子进程中没有父进程的函数映象。
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> void fun(int sig) { printf("test1\n"); } int main(int argc, char const *argv[]) { int fd = fork(); signal(SIGINT, fun); if(fd < 0){ exit(-1); }else if(fd == 0){ execlp("sleep","sleep","10",NULL); }else{ sleep(10); } return 0; }
运行结果:./a.out
<clt + c>
test1
在Linux 中,当一个信号的信号处理函数执行时,如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。下面的程序演示了这一点:
#include <signal.h> int interrupt() { printf(“Interrupt called\n”); sleep(3); printf(“Interrupt Func Ended.\n”); } main() { signal(SIGINT,interrupt); printf(“Interrupt set for SIGINT\n”); sleep(10); printf(“Program NORMAL ended.\n”); return; }
执行它,结果如下:
Interrupt set for SIGINT
<ctrl+c>
Interrupt called
<ctrl+c>
Func Ended
Interrupt called
Func Ended
Program NORMAL ended.
但是如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断:
#include <signal.h> int interrupt() { printf(“Interrupt called\n”); sleep(3); printf(“Interrupt Func Ended.\n”); } int catchquit() { printf(“Quit called\n”); sleep(3); printf(“Quit ended.\n”); } main() { signal(SIGINT,interrupt); signal(SIGQUIT,catchquit); printf(“Interrupt set for SIGINT\n”); sleep(10); printf(“Program NORMAL ended.\n”); return; }
执行这个程序的结果如下:
Interrupt set for SIGINT
<ctrl+c>
Interrupt called
<ctrl+\>
Quit called
Quit ended.
Interrupt Func Ended.
Program NORMAL ended.
进程间发送信号
一个进程通过对signal()的调用来处理其它进程发送来的信号。同时,一个进程也可以向其它的进程发送信号。这一操作是由系统调用kill()来完成的。kill()在linux 系统库signal.h中的函数声明如下:
int kill(pid_t pid, int sig);
参数pid 指定了信号发送的对象进程:它可以是某个进程的进程标识符(pid),也可以是以下的值:
如果pid 为零,则信号被发送到当前进程所在的进程组的所有进程;
如果pid 为-1,则信号按进程标识符从高到低的顺序发送给全部的进程(这个过程受到当前进程本身权限的限制);
如果pid 小于-1,则信号被发送给标识符为pid 绝对值的进程组里的所有进程。
需要说明的是,一个进程并不是向任何进程均能发送信号的,这里有一个限制,就是普通用户的进程只能向具有与其相同的用户标识符的进程发送信号。也就是说,一个用户的进程不能向另一个用户的进程发送信号。只有root 用户的进程能够给任何线程发送信号。
参数sig 指定发送的信号类型。它可以是任何有效的信号。
由于调用kill()的进程需要直到信号发往的进程的标识符,所以这种信号的发送通常只在关系密切的进程之间进行,比如父子进程之间。
下面是一个使用kill()调用发送信号的例子。这个程序建立两个进程,并通过向对方发送信号SIGUSR1 来实现它们之间的同步。这两个进程都处于一个死循环中,在接收对方发送的信号之前,都处于暂停等待中。这是通过系统调用pause()来实现的,它能够使一个程序暂停,直至一个信号到达,然后进程输出信息,并用kill 发送一个信号给对方。当用户按了中断键,这两个进程都将终止。
#include <signal.h> 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: /*子进程模块*/ /* 设定子进程的SIGUSR1 */ signal(SIGUSR1,c_action); /* 获得父进程的标识符 */ ppid=getppid(); for(;;) { sleep(1); kill(ppid,SIGUSR1); pause(); } /*死循环*/ break; default: /*父进程模块*/ for (;;) { pause(); sleep(1); kill(pid,SIGUSR1); } /*死循环*/ } } p_action() { printf("Patent caught signal #%d\n",++ntimes); } c_action() { printf("Child caught signal #%d\n",++ntimes); }
程序运行结果如下:
Patent caught signal #1
Child caught signal #1
Patent caught signal #2
Child caught signal #2
Patent caught signal #3
Child caught signal #3
Patent caught signal #4
Child caught signal #4
<ctrl+c>
特别注意root用户执行时的问题,比如下面程序:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> int main(){ int fd = fork(); if(fd > 0){ exit(0); } while(1){ kill(-1,SIGINT); kill(-1,SIGKILL); } return 0; } root权限下运行,系统直接崩溃。
<span style="background-color: rgb(255, 255, 255);"> </span>
<span style="background-color: rgb(255, 255, 255);"></span>
<span style="background-color: rgb(255, 255, 255);"></span>
<span style="font-size:18px;background-color: rgb(255, 255, 255);"><strong>系统调用alarm()和pause() 1、系统调用alarm() alarm()是一个简单而有用的系统调用,它可以建立一个进程的报警时钟,在时钟定时器到时的时候,用信号向程序报告。alarm()系统调用在Linux 系统函数库unistd.h 中的函数声明如下: unsigned int alarm(unsigned int seconds); 函数唯一的参数是seconds,其以秒为单位给出了定时器的时间。当时间到达的时候,就向系统发送一个SIGARLM信号。</strong></span>
<span style="font-size:18px;background-color: rgb(255, 255, 255);"><strong>一个由alarm()调用设置好的报警时钟,在<span style="color:#ff0000;">通过exec()调用后,仍将继续有效</span>。但是,<span style="color:#ff0000;">它在fork()调用后中,在子进程中失效</span>。如果要使设置的报警时钟失效,只需要调用参数为零的alarm():</strong></span>
<span style="font-size:18px;background-color: rgb(255, 255, 255);"><strong> alarm(0) alarm()调用也不能积累。如果调用alarm 两次,则第二次调用就取代第一次调用。但是,alarm 的返回值柜橱了前一次设定的报警时钟的剩余时间。当需要对某项工作设置时间限制时,可以使用alarm()调用来实现。其基本方法为:先调用alarm()按时间限制值设置报警时钟,然后进程作某一工作。如果进程在规定时间以内完成这一工作,就再调用alarm(0)使报警时钟失效。如果在规定时间内未能完成这一工作,进程就会被报警时钟的SIGALRM 信号中断,然后对它进行校正。</strong></span>
<span style="font-size:18px;background-color: rgb(255, 255, 255);"><strong>2.系统调用pause() 系统调用pause()能使调用进程暂停执行,直至接收到某种信号为止。pause()在Linux系统函数库unistd.h 中的函数声明如下: int pause(void); 该调用没有任何的参数。它的返回始终是-1 , 此时errno 被设置为ERESTARTNOHAND。 </strong></span>
<span style="background-color: rgb(255, 255, 255);"></span>
<span style="background-color: rgb(255, 255, 255);"></span>
<span style="background-color: rgb(255, 255, 255);"></span>
<span style="background-color: rgb(255, 255, 255);"></span>
<span style="background-color: rgb(255, 255, 255);"> </span>
标签:style class blog code http tar
原文地址:http://blog.csdn.net/huangbo_embed/article/details/34560455