3.5 信号集和信号屏蔽
(1)信号集函数:信号集为一个或多个信号的集合,主要用在信号屏蔽函数中
(2)主要的信号集函数
头文件 |
#include <signal.h> |
函数 |
①int sigemptyset(sigset_t* set);// set为信号集,将信号集清空,对应将所有信号屏蔽字置0. ②int sigfillset(sigset* set); //将所有信号加入到信号集中,对应将所有信号的屏蔽字置1. ③int sigaddset(sigset* set, int signo); //将某个信号加入到信号集中,对应将的信号屏蔽字某位置1. ④int sigdelset(sigset* set, int signo); //将某个信号从信号集中删除,对应将的信号屏蔽字某位置0. ⑤int sigismember(const sigset_t* set, int signo);//测试信号集中是否包含有某个信号,对应判断信号屏蔽字某位是否置1。真返回1,假返回0,出错返回-1。 |
|
|
函数 |
int sigprocmask(int how, const sigset_t* set, sigset_t* oldset); |
功能 |
利用set去覆盖内核中的信号屏蔽字,oldset存放原有的信号屏蔽字 |
返回值 |
成功返回0,出错返回-1. |
参数 |
【参数how】: ①SIG_BLOCK:利用set中的信号设置信号屏蔽字(设置某些位的置为1) 相当于mask |= oldest; ②SIG_UNBLOCK:利用set中的信号不设置信号屏蔽字(取消某些位置为0)相当于mask &= ~oldset(先取反,再按位与) ③SIG_SETMASK:利用set信号去替换内核信号屏蔽字。相当于mask = oldest. |
备注 |
(1)进程可以暂时屏蔽信号,使得进程在执行过程中发生的相应信号可以暂时被屏蔽,等待进程解除信号屏蔽后再由内核或驱动将该信号投递给进程。 (2)信号屏蔽可屏蔽程序执行过程中的中断。 |
|
|
函数 |
int sigpending(sigset_t* set); |
功能 |
获取信号未决字的内容 |
返回值 |
成功返回0,出错返回-1。 |
【编程实验】查看进程的信号屏蔽字
//signal_mask.c
#include <signal.h> #include <stdio.h> #include <stdlib.h> void out_set(const sigset_t* set) { int i = 1; //信号是从1开始的 int bFind = 0; for(; i<31; i++){ //判断信号屏蔽字的某些位置是否置1 if(sigismember(set, i)){ bFind |= 1; printf("%d ", i); //查看每个信号 } } if (bFind) printf("\n"); } void sig_handler(int signo) { printf("begin process the %d\n", signo); //获得正在处理信号时内核中的信号屏蔽字的内容 sigset_t oset; //放置内核信号屏蔽字的内容 //清空信号集oset sigemptyset(&oset); if(sigprocmask(SIG_BLOCK, NULL, &oset) < 0){ perror("sigprocmask error"); } out_set(&oset); //输出信号屏蔽字的内容 printf("finish process the %d\n", signo); } int main(void) { if(signal(SIGUSR1, sig_handler) == SIG_ERR){ perror("signal sigusr1 error"); } if(signal(SIGUSR2, sig_handler) == SIG_ERR){ perror("signal sigusr2 error"); } sigset_t oset; //放置内核信号屏蔽字的内容 printf("before signal occured mask:\n"); //清空信号集oset sigemptyset(&oset); //在信号发生前,获得信号屏蔽字的内容 if(sigprocmask(SIG_BLOCK, NULL, &oset) < 0){ perror("sigprocmask error"); } out_set(&oset); //输出信号屏蔽字的内容 printf("process %d wait signal...\n", getpid()); pause();//进程暂停等待信号 printf("after signal occured mask:\n"); sigemptyset(&oset); //在信号发生后,获得信号屏蔽字的内容 if(sigprocmask(SIG_BLOCK, NULL, &oset) < 0){ perror("sigprocmask error"); } out_set(&oset); return 0; } /*输出结果: [root@localhost]# bin/signal_mask before signal occured mask: //mask为空 process 2053 wait signal... begin process the 12 12 //mask为SIGUSR2 finish process the 12 after signal occured mask: //mask为空 [root@localhost]# bin/signal_mask before signal occured mask: //mask为空 process 2054 wait signal... begin process the 10 10 //mask为SIGUSR1 finish process the 10 after signal occured mask: //mask为空 */
(3)信号屏蔽设置
①信号在处理过程中是被屏蔽的(被置1),处理完毕解除屏蔽(置0)。可在函数可重入性中利用信号屏蔽技术。
②内核中的task_struct中包含两个32位字(记录相关的信号信息),分别是信号屏蔽字blocked和信号未决字pending。其中:
blocked:共有31位信号,0号没有意义,每一位代表一个信号,初始为0。若为0,则该位上发生信号会被立即处理,若为1(设置1则信号被屏蔽,设置0则信号不屏蔽),则在该位上发生信号不会被处理,会延迟处理。
pending:初始为0,若blocked中某一位为1,但以发生了同样的信号,则在pending同样的位置会被置为1,以便让进程知道该信号又发生过而进行延迟处理。
③若干个信号一起设置为0或1称为信号集
④子进程继承父进程的信号屏蔽字,而不继承信号未决字。
【编程实验】利用信号屏蔽来防止函数的重入问题
//signal_reentry2.c
#include <unistd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <malloc.h> int g_v[10]; //全局数组 int* h_v; //堆数组 void set(int val) { int a_v[10]; //局部数组 int i = 0; for(; i<10; i++){ a_v[i] = val; g_v[i] = val; h_v[i] = val; sleep(1); } printf("g_v:"); for(i=0; i<10; i++){ if(i != 0) printf(",%d", g_v[i]); else printf("%d", g_v[i]); } printf("\n"); printf("h_v:"); for(i=0; i<10; i++){ if(i != 0) printf(",%d", h_v[i]); else printf("%d", h_v[i]); } printf("\n"); printf("a_v:"); for(i=0; i<10; i++){ if(i != 0) printf(",%d", a_v[i]); else printf("%d", a_v[i]); } printf("\n"); } //信号处理函数 void sig_handler(int signo) { if(signo == SIGTSTP){ printf("SIGTSTP occured\n"); set(20); //信号处理函数内部再次调用set,以验证函数的 //可重入性。注意,传入20 printf("end SIGTSTP\n"); } } int main(void) { if(signal(SIGTSTP, sig_handler) == SIG_ERR){ perror("signal sigtstp error"); } h_v = (int*)calloc(10, sizeof(int)); printf("begin running main\n"); //屏蔽信号(1-31) sigset_t sigset; sigfillset(&sigset); //屏蔽所有信号 if(sigprocmask(SIG_SETMASK, &sigset, NULL) < 0){ perror("sigprocmask error"); } set(10); //传入10,由于set之前屏蔽了所有信号,所以这时 //即使按ctrl-z,这个信号也不会马上被捕获,这意 //味着,执行流没转到信号处理函数,而是正常在main //中执行。 //解除信号屏蔽(如果在屏蔽期间发生信号,会在解释屏蔽之后被发送出去) if(sigprocmask(SIG_UNBLOCK, &sigset, NULL) < 0){ perror("sigprocmask error"); } printf("end running main\n"); free(h_v); } /*输出结果: [root@localhost]# bin/signal_reentry2 begin running main //正常执行流,不产生信号 g_v:10,10,10,10,10,10,10,10,10,10 h_v:10,10,10,10,10,10,10,10,10,10 a_v:10,10,10,10,10,10,10,10,10,10 end running main [root@localhost]# bin/signal_reentry2 begin running main //运行2-3秒后,按ctrl-z。注意信号并没有 ^Zg_v:10,10,10,10,10,10,10,10,10,10 //马上被捕获,而是正常执行main函数 h_v:10,10,10,10,10,10,10,10,10,10 a_v:10,10,10,10,10,10,10,10,10,10 SIGTSTP occured //解除屏蔽,信号才被发送过来,从而 g_v:20,20,20,20,20,20,20,20,20,20 //防止的set函数的重入 h_v:20,20,20,20,20,20,20,20,20,20 a_v:20,20,20,20,20,20,20,20,20,20 end SIGTSTP end running main */
【编程实验】连续发送同一信号时的信号未决字内容变化
//signal_pending.c
#include <stdio.h> #include <stdlib.h> #include <signal.h> void out_set(const sigset_t* set) { int i = 1; for(; i<=31; i++){ if(sigismember(set, i)){ printf("%d, ", i); //将被屏蔽的信号输出 } } printf("\n"); } void sig_handler(int signo) { printf("begin the signal handler\n"); int i = 0; sigset_t set; for(; i<10; i++){ printf("(%d)", i + 1); sigemptyset(&set); if(sigpending(&set) < 0){ perror("sigpending error"); }else{ printf("pending signal: "); out_set(&set); } sleep(1); } printf("end the signal handler\n"); } int main(void) { if(signal(SIGTSTP, sig_handler) == SIG_ERR){ perror("signal sigtstp error"); } printf("process %d wait signal...\n", getpid()); pause(); //进程暂停等待信号 printf("process finished.\n"); return 0; } /*输出结果: [root@localhost]# bin/signal_pending process 1535 wait signal... ^Zbegin the signal handler //发送ctrl-z,被唤醒,开始执行信号处理函数 (1)pending signal: //开始处理信号函数时,信号未决字被清零 (2)pending signal: (3)pending signal: ^Z^Z^Z^Z(4)pending signal: 20, //连续发送4个ctrl-z信号,由于处理信号过程 (5)pending signal: 20, //中,同一类型的信号被屏蔽。此时再发送的 (6)pending signal: 20, //同类型信号会被记录在pending中,以便告知 (7)pending signal: 20, //系统,又有一个同类型的信号等待处理 (8)pending signal: 20, (9)pending signal: 20, (10)pending signal: 20, end the signal handler begin the signal handler //处理完第1个信号,开始处理后来未决的信号 (1)pending signal: //由于是同类型的,被延迟到第1个信号处理完 (2)pending signal: //毕才开始。 (3)pending signal: (4)pending signal: (5)pending signal: (6)pending signal: (7)pending signal: (8)pending signal: (9)pending signal: (10)pending signal: end the signal handler //未决信号被处理完毕,同类型只再处理一次 process finished. */