First step to Signal —— in Linux C Programing
1. What's signal
信号是软件中断,提供了一种处理异步事件的方法。(见《Unix环境高级编程》)一般使用时需包含 signal.h 库。
每个信号命名由SIG开头,实际值为正整数。(不存在编号为0的信号,0有特殊用途)
具体信号列表卡参见《Unix环境高级编程》 或者
2. signal 函数
信号机制最基础的接口是signal函数,声明如下:
1 // Original Definition 2 void ( *signal(int signum, void (*handler)(int)) ) (int); 3 4 // Readable Definition 5 typedef void (*sighandler_t)(int); 6 sighandler_t signal(int signum, sighandler_t handler);
第二行是最原始的表示方法,利用typedef我们可提高可读性,得到第5和6行的声明。
一般带有指针的handler都代表一个处理函数的指针。
由line 6 我们可得知,signal函数表示将一个参数为信号的函数handler注册到一个信号上,即一旦触发该信号,该信号就会作为参数传给函数handler立即执行。或者说,信号一旦触发,该函数handler就会捕捉该信号,覆盖掉系统默认动作。(有一些如SIGSTOP等的信号不可被捕捉或忽略)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <signal.h> 4 5 6 void handler(int sig) { 7 printf("\nSignal comes!\n"); 8 } 9 10 int main() { 11 printf("pid:%ld\n",(long)getpid()); 12 signal(SIGINT, handler); 13 14 getchar(); 15 16 return 0; 17 18 }
执行上列程序,输入时按ctrl+c(SIGINT中断条件)试试?
3. kill 和 raise
定义如下
1 int kill(pid_t pid, int signo); 2 int raise(int signo); 3 4 /* 两者关系 */ 5 pid_t self = getpid(); 6 7 raise(signo) == kill(self, signo);
kill函数是当前进程将信号发给对应的进程pid,而raise则是当前进程向自身发信号。(因此有上面的等式)
kill的pid参数有以下情况:
- pid > 0 将信号发送给pid进程
- pid == 0 将信号发送给与当前进程属于同一进程组的所有进程
- pid < 0 将信号发送给(-pid)进程组,前提是当前进程有权限向其发送信号
- pid == -1 将信号发送给所有当前进程有权限发送信号的进程
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <signal.h> 4 5 6 void handler(int sig) { 7 printf("\nSignal comes!\n"); 8 } 9 10 int main() { 11 printf("pid:%ld\n",(long)getpid()); 12 signal(SIGINT, handler); 13 14 // raise(SIGINT); 15 kill(getpid(), SIGINT); 16 17 return 0; 18 19 }
注释可以互换一下,结果是一样的。
4. 信号集
信号集可以理解成多个信号的集合。
常用的函数如下:
1 int sigemptyset(sigset_t *set); // 置空集合 2 int sigfillset(sigset_t *set); // 使set包括所有信号 3 int sigaddset(sigset_t *set, int signo); // 将信号添加到集合 4 int sigdelset(sigset_t *set, int signo); // 将信号从集合中删除 5 // 以上函数成功返回 0, 错误返回 -1 6 7 int sigismamber(const sigset_t *set, int signo); // 判断信号是否在集合中 8 // 以上函true返回1, false返回0,出错返回-1
5. sigaction
sigaction 既是一个函数的名字也是一个结构体的名字,他们的定义分别如下:
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact); // 成功返回0,出错返回-1 struct sigaction { void (*sa_handler)(int); // 信号处理函数 sigset_t sa_mask; // 作为该信号集合的标识 int sa_flags; /* alter handler */ void (*sa_sigaction)(int, siginto_t *, void *); }
【待定】
6. 信号阻塞
sigprocmask 函数
1 int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset); 2 // 成功返回0,出错返回-1
how 参数有三种选择:
- SIG_BLOCK 相当于set 与 oset的并集作为阻塞信号,set为新添加想要阻塞的信号。 相当于&mask = &oset , mask = set | oset
- SIG_UNBLOCK 相当于¬set 与 oset的交集作为阻塞信号,set为想要解除信号的集合。 相当于&mask = &oset , mask = ~set & oset
- SIG_SETMASK 新阻塞集合直接等于set的集合。相当于 &mask = &oset, mask = set
如果 set 为 NULL, 则不会改变当前进程的信号屏蔽字,how取值无意义。有时利用这点用来取得当前进程阻塞信号的集合变量。
1 #include <stdio.h> 2 #include <signal.h> 3 4 void check() { 5 sigset_t set; 6 printf("====START\n"); 7 if(sigprocmask(0, NULL, &set)<0) 8 { 9 printf("Sigprocmask error!!\n"); 10 exit(-1); 11 } 12 if(sigismember(&set,SIGINT)) 13 printf("sigint\n"); 14 15 printf("====END\n"); 16 17 } 18 19 void handler() { 20 printf("\nSignal comes!\n"); 21 } 22 23 int main() { 24 sigset_t blockset, oldblockset; 25 26 signal(SIGINT, handler); 27 28 sigemptyset(&blockset); 29 30 check(); 31 32 33 sigaddset(&blockset,SIGINT); 34 check(); 35 36 sigprocmask(SIG_SETMASK,&blockset,&oldblockset); 37 check(); 38 39 sigprocmask(SIG_SETMASK,&oldblockset,NULL); // clear mask set 40 check(); 41 42 return 0; 43 }
运行结果: