三十一、Linux 进程与信号——SIGCHLD 信号、kill和raise函数以及alarm函数
31.1 SIGCHLD 信号
- 子进程状态发生变化(子进程结束)产生该信号,父进程需要使用 wait 调用来等待子进程结束并回收它。
- 避免僵尸进程
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <signal.h> 4 #include <sys/wait.h> 5 #include <unistd.h> 6 7 void sig_handler(int signo) 8 { 9 printf("child process deaded, signo: %d\n", signo); 10 11 /** 当父进程捕获到 SIGCHLD 信号后要调用 wait 12 * 函数去回收子进程,否则子进程会成为僵尸进程 */ 13 wait(0); 14 } 15 16 17 void out(int n) 18 { 19 int i; 20 for(i = 0; i < n; i++) { 21 22 printf("%d out %d\n", getpid(), i); 23 sleep(2); 24 } 25 } 26 27 int main(void) 28 { 29 if(signal(SIGCHLD, sig_handler)){ 30 perror("signal sigchld error"); 31 } 32 33 pid_t pid = fork(); 34 if(pid < 0) { 35 36 perror("fork error"); 37 exit(1); 38 } else if(pid > 0) { 39 /** parent process */ 40 out(100); 41 } else { 42 /** child process */ 43 out(10); 44 } 45 46 return 0; 47 }
31.2 信号发送
- 除了内核和超级用户,并不是每个进程都可以向其他的进程发送信号
- 一般的进程只能向具有相同 uid 和 gid 的进程发送信号,或向相同进程组中的其他进程发送信号
- 常用的发送信号的函数由 kill()、raise()、alarm()、settitimer()、abort()等。
31.2.1 kill() 函数 和 raise() 函数
1 #include <signal.h> 2 int kill(pid_t pid, int signo);
- 函数功能:向指定的进程发送某一个信号
- 函数参数:
- pid:接受信号进程的 pid
- pid > 0 将信号发给进程 ID 为 pid 的进程
- pid == 0 将信号发给与发送进程同一进程组的所有进程
- pid < 0 将该信号发送给进程组 ID 等于 pid 的绝对值
- pid == -1 将该信号发送给发送进程有权限向他们发送信号的系统上的所有进程
- signo:要发送的信号值
- pid:接受信号进程的 pid
- 返回值:成功返回0,出错返回 -1
- 说明:kill 函数将信号发送给进程或进程组
- 0 为空信号,常用来检测特定的进程是否存在
1 #include <signal.h> 2 int raise(int signo);
- 函数功能:向进程本身发送一个信号,相当于 kill(getpid(), sig)
- 函数参数:
- signo:要发送的信号值
- 返回值:成功返回0,出错返回 -1
1 #include <signal.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <unistd.h> 5 6 #define KILLSELF 0 7 8 /** 定义信号处理函数 9 * signo 进程捕获到的信号 10 */ 11 void sig_handler(int signo) 12 { 13 printf("%d, %d occured\n", getpid(), signo); 14 } 15 16 int main(void) 17 { 18 /** 向内核登记信号处理函数以及信号值 */ 19 if(signal(SIGTSTP, sig_handler) == SIG_ERR) { 20 perror("signal sigtstp error"); 21 } 22 23 if(signal(SIGINT, sig_handler) == SIG_ERR) { 24 perror("signal sigint error"); 25 } 26 27 if(signal(SIGUSR1, sig_handler) == SIG_ERR) { 28 perror("signal usr1 error"); 29 } 30 31 if(signal(SIGUSR2, sig_handler) == SIG_ERR) { 32 perror("signal usr2 error"); 33 } 34 /* 35 if(signal(SIGKILL, SIG_IGN) == SIG_ERR) { 36 perror("signal sigtstp error"); 37 } 38 39 if(signal(SIGSTOP, SIG_IGN) == SIG_ERR) { 40 perror("signal sigint error"); 41 } 42 */ 43 int i = 0; 44 while(i < 20) { 45 printf("%d out %d\n", getpid(), i++); 46 if(i == 10) { 47 #ifdef KILLSELF 48 kill(getpid(), SIGKILL); 49 #endif 50 sleep(1); 51 } 52 sleep(1); 53 } 54 55 /** 向进程自己发送 SIGUSR1 和SIGUSR2 信号 */ 56 raise(SIGUSR1); 57 kill(getpid(), SIGUSR2); 58 return 0; 59 }
31.2.2 alarm() 函数
1 #include <unistd.h> 2 unsigned int alarm(unsigned int seconds);
- 函数说明:
- alarm 函数可设置定时器,当定时器超时,产生 SIGALRM 信号
- 信号由内核产生,在指定的 seconds 秒之后,给进程本身发送一个 SIGALRM 信号。
- 参数为 0,取消以前设置的定时器
- 返回值:
- 0 或以前设置的定时器时间余留秒数
1 #include <signal.h> 2 #include <unistd.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <math.h> 6 7 void sig_handler(int signo) 8 { 9 if(SIGALRM == signo) { 10 printf("clock time out\n"); 11 alarm(5); ///< 重新设置定时器(周期性的定时) 12 } 13 } 14 15 void out_data(void) 16 { 17 int i = 1; 18 while(i <= 20) { 19 double d = drand48(); 20 printf("%-10d: %lf\n", i++, d); 21 if(i == 16) 22 alarm(0); ///< 取消定时器 23 sleep(1); 24 } 25 } 26 27 int main(void) 28 { 29 if(signal(SIGALRM, sig_handler) == SIG_ERR) { 30 perror("signal sigalrm error"); 31 } 32 33 // 设置定时器 34 alarm(5); 35 printf("begin running main\n"); 36 out_data(); 37 printf("end running main\n"); 38 return 0; 39 }