【Linux 编程】Linux信号处理
信号驱动式I/O是指进程预先告知内核,使得当某个描述字上发生某事时,内核使用信号通知相关进程。图1概括展示信号驱动式I/O模型。
图1 信号驱动式I/O模型
针对一个进程建立一个相关进程的处理函数,需要通过signal()函数来建立。
基本信号:(linux 控制台中输入:man 7 signal)
SIGINT(值为2,默认动作:terminal):Interrupt from keyboard。
SIGTERM(值为15,默认动作:terminal):Termination signal
SIGCHILD(值为20,17,28,默认动作:ignore):child stopped or terminated。
其中,SIGINT信号是由用户按中断键(即DELETE或ctrl+c)时,终端驱动程序产生此信号并发送至前段进程组中的每一个进程。
SIGCHLD信号,在一个进程终止或停止时,将SIGCHLD发送给其父进程。
SIGTERM信号,由kill(1)命令发送的系统默认终止信号。
在系统提供的信号值是从0至31,即4 bytes大小。但是用户可以自己设置超过该范围的信号,通过kill -ith pidno来触发新建立的信号。
利用kill来触发信号处理函数,是因为信号实际上就是一个位数组中相应位来建立是否被触发。例如,sigdelset函数
1 int sigdelset( sigset_t *set, int signo ) 2 { 3 if ( SIGBAD( signo ) ) 4 { 5 errno = EINVAL; 6 return (-1); 7 } 8 9 // 该语句帮助理解sigset_t变量实际上就是一个位数组。 10 *set &= ~(1 << (signo - 1)); 11 12 return 0; 13 }
建立超过31th的信号处理函数与正常的信号处理函数相同,只是触发方式为:kill -ith pidno
1 #include <signal.h> 2 #include <stdio.h> 3 4 static void sig_default(int); 5 6 int main() 7 { 8 sigset_t set; 9 sigemptyset(&set); 10 sigfillset(&set); 11 sigdelset(&set, 36); 12 sigdelset(&set, 37); 13 sigdelset(&set, 38); 14 sigprocmask(SIG_SETMASK, &set, NULL); 15 16 if (signal(36, sig_default) == SIG_ERR) 17 { 18 perror("can not reset the 36th signal handler"); 19 return 1; 20 } 21 22 if (signal(37, sig_default) == SIG_ERR) 23 { 24 perror("can not reset the 37th signal handler"); 25 return 1; 26 } 27 28 if (signal(38, sig_default) == SIG_ERR) 29 { 30 perror("can not reset the 38th signal handler"); 31 return 1; 32 } 33 34 while (1) 35 { 36 pause(); 37 } 38 39 return 0; 40 } 41 42 static void sig_default(int signo) 43 { 44 if (signo == 36) 45 printf(" 36th signal\n"); 46 else if (signo == 37) 47 printf(" 37th signal\n"); 48 else if (signo == 38) 49 printf(" 38th signal\n"); 50 else 51 printf(" Unknown signal\n"); 52 53 return ; 54 }
其运行结果:
下面来考虑,主进程利用fork()函数来建立的子进程是否继承父进程的信号处理函数?
1. 若在子进程中,没有清楚父进程建立的响应的信号处理函数,则继承父进程的信号处理函数。
1 #include <unistd.h> 2 #include <signal.h> 3 #include <stdio.h> 4 5 /* In this file: 6 * We test child process created by fork(). 7 * The child process whether inherit signal 8 * process of parent process or not. 9 * 10 * This file not clear signal set. 11 */ 12 static void sig_parent(int signo) 13 { 14 if (signo == 36) 15 printf(" this is parent signal\n"); 16 else if (signo == 37) 17 printf(" this is children signal\n"); 18 else 19 printf(" Unkown signal\n"); 20 21 return ; 22 } 23 24 void ChildProc() 25 { 26 printf(" child process id is %d\n", getpid()); 27 if (signal(37, sig_parent) == SIG_ERR) 28 { 29 perror(" can not reset the 37th signal handler\n"); 30 return; 31 } 32 33 while (1) pause(); 34 35 return; 36 } 37 38 int main() 39 { 40 pid_t pid; 41 42 if (signal(36, sig_parent) == SIG_ERR) 43 { 44 perror(" can not reset the 36th signal handler\n"); 45 return -1; 46 } 47 48 if ((pid = fork()) < 0) 49 perror(" failed to fork()\n"); 50 else if (pid == 0){ 51 ChildProc(); 52 return 0; 53 } else 54 while (1) pause(); 55 56 return 0; 57 }
运行结果:
2. 若在进程中,利用sigemptyset()、sigfillset()、sigdelset()、sigprocmask()等函数来清理并设置新的触发位,则不继承父进程的信号处理函数
1 #include <unistd.h> 2 #include <signal.h> 3 #include <stdio.h> 4 5 /* In this file: 6 * We test child process created by fork(). 7 * The child process whether inherit signal 8 * process of parent process or not. 9 * 10 * This file will clear signal headler. 11 */ 12 static void sig_parent(int signo) 13 { 14 if (signo == 36) 15 printf(" this is parent signal\n"); 16 else if (signo == 37) 17 printf(" this is children signal\n"); 18 else 19 printf(" Unkown signal\n"); 20 21 return ; 22 } 23 24 void ChildProc() 25 { 26 sigset_t set; 27 sigemptyset(&set); 28 sigfillset(&set); 29 sigdelset(&set, 37); 30 sigprocmask(SIG_SETMASK, &set, NULL); 31 32 printf(" child process id is %d\n", getpid()); 33 if (signal(37, sig_parent) == SIG_ERR) 34 { 35 perror(" can not reset the 37th signal handler\n"); 36 return; 37 } 38 39 while (1) pause(); 40 41 return; 42 } 43 44 int main() 45 { 46 pid_t pid; 47 48 if (signal(36, sig_parent) == SIG_ERR) 49 { 50 perror(" can not reset the 36th signal handler\n"); 51 return -1; 52 } 53 54 if ((pid = fork()) < 0) 55 perror(" failed to fork()\n"); 56 else if (pid == 0){ 57 ChildProc(); 58 return 0; 59 } else 60 while (1) pause(); 61 62 return 0; 63 }
运行结果:
3. 若在继承父进程的信号处理函数的情况下,设置与父进程相同的信号位。将屏蔽父进程的信号处理函数。
1 #include <unistd.h> 2 #include <signal.h> 3 #include <stdio.h> 4 5 static void sig_parent(int signo) 6 { 7 if (signo == 36) 8 printf(" this is parent signal handler\n"); 9 else 10 printf(" Unkown signal\n"); 11 12 return ; 13 } 14 15 static void sig_child(int signo) 16 { 17 if (signo == 36) 18 printf(" this is child signal handler\n"); 19 else 20 printf(" unkown signal\n"); 21 22 return; 23 } 24 25 void ChildProc() 26 { 27 printf(" child process id is %d\n", getpid()); 28 if (signal(36, sig_child) == SIG_ERR) 29 { 30 perror(" can not reset the 37th signal handler\n"); 31 return; 32 } 33 34 while (1) pause(); 35 36 return; 37 } 38 39 int main() 40 { 41 pid_t pid; 42 43 if (signal(36, sig_parent) == SIG_ERR) 44 { 45 perror(" can not reset the 36th signal handler\n"); 46 return -1; 47 } 48 49 if ((pid = fork()) < 0) 50 perror(" failed to fork()\n"); 51 else if (pid == 0){ 52 ChildProc(); 53 return 0; 54 } else 55 while (1) pause(); 56 57 return 0; 58 }
运行结果: