2. 信号发送的函数
2.1 安装信号处理函数:signal
(1)signal函数
头文件 |
#include <signal.h> |
函数 |
void (*signal(int signo, void(*func)(int)))(int); |
返回值 |
若成功则返回先前的信号处理函数指针,出错则返回SIG_ERR |
参数 |
signo:要登记的信号值(如SIGCHLD) func: ①信号处理函数指针; ②如果设为SIG_IGN,表示忽略信号 ③如果设置为SIG_DFL:采用系统默认的方式处理信号,执行默认操作。 |
功能 |
向内核登记信号处理函数 |
备注 |
①signal是个系统函数,它有两个参数signo和func。返回值为先前的信号处理函数指针。 ②typedef的方式 typedef void(*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); |
【编程实验】捕获信号(如SIGUSR1、SIGSTOP、ctrl-z、ctrl-c等)
//signal_catch.c
#include <signal.h> #include <stdlib.h> #include <stdio.h> /*演示信号的捕获*/ //定义信号处理函数 //signo:进程捕获到的信号 void sig_handler(int signo) { //输出进程ID和信号值 printf("%d, %d ocurred\n", getpid(), signo); } int main(void) { //向内核登记信号处理函数(用于捕获ctrl-z和ctrl-c信号) //1.忽略ctrl-Z信号 // if(signal(SIGTSTP, SIG_IGN) == SIG_ERR){ //2.ctrl-z的默认处理 // if(signal(SIGTSTP, SIG_DFL) == SIG_ERR){ //3.捕获ctrl-z信号 if(signal(SIGTSTP, sig_handler) == SIG_ERR){ perror("signal sigtstp error"); } //捕获ctrl-c信号 if(signal(SIGINT, sig_handler) == SIG_ERR){ perror("signal sigint error"); } //捕获SIGUSR1信号(可在另一终端中发送kill -SIGUSR1 进程号 //来观察,默认是被忽略的) if(signal(SIGUSR1, sig_handler) == SIG_ERR){ perror("signal sigusr1 error"); } //捕获SIGUSR2信号(默认被忽略的) if(signal(SIGUSR2, sig_handler) == SIG_ERR){ perror("signal sigusr2 error"); } //捕获/忽略SIGKILL或SIGSTOP信号都会提示无效参数, //以下代码不能起作用! //1. 捕获SIGKILL,会提示错误 //if(signal(SIGKILL, sig_handler) == SIG_ERR){ //2. 忽略SIGKILL,会提示错误,也无法起作用 if(signal(SIGKILL, SIG_IGN) == SIG_ERR){ perror("signal sigkill error"); } int i = 0; while(i<30){ //不停输出信息,在这过程中,可在另一个终端给该进程发送 //各种信号(如SIGUSR1,SIGKILL等),也可按ctrl-c或ctrl-z //观察信号被捕获的情况。 printf("%d out %d \n", getpid(), i++); sleep(1); } return 0; } /*输出结果: 1852 out 0 1852 out 1 1852 out 2 ^C1852, 2 ocurred 1852 out 3 1852 out 4 1852 out 5 1852 out 6 1852 out 7 ^Z1852, 20 ocurred 1852 out 8 1852 out 9 ... */
(2)SIGCHLD信号
①子进程状态发生变化(子进程结束)产生该信号,父进程需要使用wait函数来等待子进程结束并回收它。
②避免僵尸进程
【编程实验】避免僵尸进程
//sig_child.c
#include <signal.h> #include <stdlib.h> #include <stdio.h> #include <sys/wait.h> void sig_handler(int signo) { //子进程结束时,输出提示信息 printf("child process deaded, signo: %d\n", signo); //当父进程捕获到SIGCHLD信号后要调用wait来回收子进程 //否则,子进程会成为僵尸进程。 if(SIGCHLD == signo) wait(0); } void out(int n) { int i = 0; for(i=0; i<n; i++){ printf("%d out %d \n",getpid(), i); sleep(1); } } int main(void) { //在父进程中捕获子进程结束的信号 if(signal(SIGCHLD, sig_handler) == SIG_ERR){ perror("signal sigchld error"); } pid_t pid = fork(); if(pid < 0){ perror("fork error"); exit(1); }else if(pid > 0){ //parent process out(20); }else{ //child process out(10); //让子进程先结束 } return 0; }
2.1 信号的发送
(1)信号的发送
①除了内核和超级用户,并不是每个进程都可以向其他进程发送信号。
②一般的进程只能向具有相同uid和gid的进程发送信号,或向相同进程组中的其他进程发送信号。
③常用发送信号的函数有kill、raise、alarm、setitimer和abort
(2)kill和raise函数
头文件 |
#include <signal.h> |
函数 |
int kill(pid_t pid, int signo);//向指向进程发送一个信号 int raise(int signo); //向进程本身发送一个信号 |
返回值 |
若成功返回0,出错返回-1 |
参数 |
pid:接受信号进程的pid ①pid>0:将信号发给进程ID为pid的进程 ②pid==0:将信号发给与发送进程同一进程组的所有进程 ③pid<0:将该信号发送给进程ID等于pid的绝对值 ④pid==-1:将该信号发送给发送进程有权限向他们发送信号的系统上所有进程。 signo:要发送的信量值 |
功能 |
向指定进程或本身发送一个信号。 |
备注 |
①kill函数将信号发送给进程或进程组。0为空信号,常用来检测特定的进程是否存在 ②raise相当于kill(getpid(), sig); |
【编程实验】信号的发送
//signal_send.c
#include <signal.h> #include <stdlib.h> #include <stdio.h> /*演示信号的发送与捕获*/ //定义信号处理函数 //signo:进程捕获到的信号 void sig_handler(int signo) { //输出进程ID和信号值 printf("%d, %d ocurred\n", getpid(), signo); } int main(void) { //向内核登记信号处理函数 //捕获SIGUSR1信号 if(signal(SIGUSR1, sig_handler) == SIG_ERR){ perror("signal sigusr1 error"); } //捕获SIGUSR2信号(默认被忽略的) if(signal(SIGUSR2, sig_handler) == SIG_ERR){ perror("signal sigusr2 error"); } int i = 0; while(i<10){ //观察信号被捕获的情况。 printf("%d out %d \n", getpid(), i++); /*进程自杀 if(i == 5){ kill(getpid(), SIGKILL); } */ sleep(1); } //向进程自己发送SIGUSR1和SIGUSR2信号 raise(SIGUSR1); kill(getpid(), SIGUSR2); return 0; } /*输出结果: 2049 out 0 2049 out 1 2049 out 2 2049 out 3 2049 out 4 2049 out 5 2049 out 6 2049 out 7 2049 out 8 2049 out 9 2049, 10 ocurred 2049, 12 ocurred */
(3)alarm函数
头文件 |
#include <unistd.h> |
函数 |
unsigned int alarm(unsigned int seconds); |
返回值 |
返回0或以前设置的定时器时间余留秒数 |
参数 |
seconds为0表示取消以前设置的定时器 |
功能 |
定时器 |
备注 |
①alarm函数可以设置定时器,当定时器超时,产生SIGALRM信号。 ②信号是由内核产生的,在指定的seconds秒之后,给进程本身发送一个SIGALRM信号。 ③alarm是一次性的定时器,每次触发后,要重新设置才能周期性的触发。 |
【编程实验】alarm定时器
//signal_alarm.c
#include <unistd.h> #include <signal.h> #include <stdlib.h> #include <stdio.h> #include <math.h> void sig_handler(int signo) { if(signo == SIGALRM){ printf("alarm clock timeout\n"); //由于alarm是一次性的,要重新设置定时器以达到周期性的目的。 alarm(5); } } void out_data(void) { int i = 1; while(i <= 20){ double d = drand48(); printf("%-10d:%lf\n", i++, d); if(i == 15) alarm(0); //取消定时器 sleep(1); } } int main(void) { //注册信号处理函数 if(signal(SIGALRM, sig_handler) == SIG_ERR){ perror("signal sigalrm error"); } //设置定时器 alarm(5); printf("begin running main\n"); out_data(); printf("end running main\n"); return 0; }
(4)gettitimer/setitimer函数
头文件 |
#include <unistd.h> |
函数 |
int getitimer(int which, struct itimerval *value); //获取定时器状态 int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue); //设置定时器 |
返回值 |
成功调用返回0,错误返回-1 |
参数 |
①which: ITIMER_REAL:按实际时间计时,计时到达将给进程发送SIGALRM信号。 ITIMER_VIRTUAL:仅当进程执行时才进行计时。计时到达将发送SIGVALRM。 ITMIER_PROF:当进程执行时和系统为该进程执行动作时都计时。与定时器经常用来统计进程在用户态和内核态花费的时间。计时到达将发送SIGPROF信号给进程。 ②value:用来指时定时器的时间,其结构如下: struct itimerval{ struct timeval it_interval; //下一次的取值,相当于以后每次触发的时间间隔 struct timeval it_value; //本次的设定值,相当于调用setitimer以后,将于多久后第1次触发。 } ③timeval结构体 struct timeval{ long tv_sev; //秒 long tv_usec; //微秒,1秒=1000000微秒 } |
功能 |
获取/设置定时器状态。 |
备注 |
①定时器将it_value递减到0时,产生一个信号,并将it_value的值设定为it_interval的值,然后重新开始计时。 ②it_value设定为0时,计时器停止,或者当它计时到期,而it_interval为0时停止。 ③ovalue不为空,则其中保留的是上次调用设定的值。 |
【编程实验】周期性定时器
//signal_setitimer.c
#include <signal.h> #include <unistd.h> #include <stdio.h> #include <sys/time.h> /*周期性定时器*/ void sig_handler(int signo) { switch(signo){ case SIGALRM: printf("Catch a signal--SIGALRM(%d)\n", signo); break; case SIGVTALRM: printf("Catch a signal--SIGVTALRM(%d)\n", signo); break; } } int main(void) { struct itimerval value1, value2, ovalue; signal(SIGALRM, sig_handler); signal(SIGVTALRM, sig_handler); printf("process id is %d\n", getpid()); //按实际时间计时,超时发送SIGALRM信号 value1.it_value.tv_sec = 5; //it_value:调用settimer后,5秒以后第1次触发 value1.it_value.tv_usec = 0; value1.it_interval.tv_sec = 1; //以后每次触发的时间间隔 value1.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &value1, &ovalue); //仅当进程执行时才进行计时,超时发送SIGVTALRM信号 value2.it_value.tv_sec = 0; value2.it_value.tv_usec = 500000; //0.5秒 value2.it_interval.tv_sec = 0; value2.it_interval.tv_usec = 500000; setitimer(ITIMER_VIRTUAL, &value2, &ovalue); for(;;); return 0; } /*输出结果: process id is 2185 Catch a signal--SIGVTALRM(26) Catch a signal--SIGVTALRM(26) Catch a signal--SIGVTALRM(26) Catch a signal--SIGVTALRM(26) Catch a signal--SIGVTALRM(26) Catch a signal--SIGVTALRM(26) Catch a signal--SIGVTALRM(26) Catch a signal--SIGVTALRM(26) Catch a signal--SIGVTALRM(26) Catch a signal--SIGALRM(14) Catch a signal--SIGVTALRM(26) Catch a signal--SIGVTALRM(26) Catch a signal--SIGALRM(14) Catch a signal--SIGVTALRM(26) ... */