信号集和信号屏蔽函数

来源:互联网 发布:img网络图片无法显示 编辑:程序博客网 时间:2024/04/26 05:32

信号集是一个或多个信号的集合,主要用在信号屏蔽函数中。数据类型为sigset_t。

信号屏蔽字是指一个进程中当前阻塞而不能送给该进程的信号集。

与信号集设置相关的函数有下面几个:

#include <signal.h>

int sigemptyset(sigset_t *set);

功能:set为信号集,将信号集清空,对应将所有信号屏蔽字置0;

int sigfillset(sigset_t *set);

功能:将所有信号加入到信号集中,对应将所有信号屏蔽字置1;

int sigaddset(sigset_t *set, int signo);

功能:将某个信号加入到信号集中,对应将信号屏蔽字某位置1;

int delset(sigset *set, int signo);

功能:将某个信号从信号集中删除,对应将信号屏蔽字某位置0;

上述函数返回:成功返回0,出错返回-1;

int sigismember(const sigset_t *set, int signo);

功能:测试信号集中是否包含某个信号,对应判断信号屏蔽字某位是否为1;

返回值:真返回1,假返回0,出错返回-1;


注意:所有进程在使用信号集前,要对信号集调用sigemptysetsigfillset一次。因为C编译器把未赋初值的外部和静态变量都初始化为0,而这与是否给定的信号集实现对应却并不清楚。


设置信号屏蔽字要调用下面的信号屏蔽函数。

#include <signal.h>

int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);

功能:利用set去覆盖内核中信号屏蔽字,oset存放原有的信号屏蔽字;

返回:成功返回0,出错返回-1;

int sigpending(sigset_t *set);

功能:获取信号未决字的内容(通过set返回当前正在阻塞的信号的信号集);

返回值:成功返回0,出错返回-1;

参数how

  • SIG_BLOCK:利用set中信号设置信号屏蔽字;
  • SIG_UNBLOCK:利用set中信号不设置信号屏蔽字;
  • SIG_SETMASK:利用set中信号去替换内核信号屏蔽字;

说明:1 进程可以暂时屏蔽信号,使得进程在执行过程中发生的相应信号暂时被阻塞,等待进程解除信号屏蔽后再由内核或驱动将该                信号递送给进程;

            2 信号屏蔽可屏蔽程序执行过程中的中断;

            3 若oset是非空指针,则返回进程的当前信号屏蔽字;

            4 若set为空,则进程信号屏蔽字不变,how值无意义;

            5 注意,不可以阻塞SIGKILLSIGSTOP信号;


下面给出一个mask(信号屏蔽字)的实例。

#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <signal.h>void out_set(sigset_t set){int i = 1;for(; i < 31; ++i){// 判断信号屏蔽字的某些位是否置1if(sigismember(&set, i)){printf("%d\n", i);}}}void sig_handler(int signo){printf("begin process the %d\n", signo);// 获得正在处理信号时内核中的信号屏蔽字内容sigset_t oset;sigemptyset(&oset);if(sigprocmask(SIG_BLOCK, NULL, &oset) < 0)perror("sigprocemask 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");if(signal(SIGINT, sig_handler) == SIG_ERR)perror("signal sigint error");// 定义一个信号集sigset_t oset;//放置内核信号屏蔽字的内容printf("before signal occured mask:\n");// 清空信号集osetsigemptyset(&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;}
程序分别输出信号产生前,处理信号时和信号处理完毕三个阶段的信号屏蔽字。

由结果可以看出,在信号产生前后,信号屏蔽字全置为了0。


下面给出一个pending(信号未决字)的实例。

#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <signal.h>void out_set(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 < 20; ++i){sigemptyset(&set);if(sigpending(&set) < 0)perror("sigpending error");else{printf("pending signal:");out_set(set);sigemptyset(&set);}printf("i is %d\n", i);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;}
在for循环中,我们不断的去获得SIGTSTP信号,当同时有两个及以上的SIGTSTP信号产生时,相应的信号未决字才被置为1。同时,如果产生多个SIGTSTP信号(2个以上),则只被处理两次,其他的被屏蔽掉。


利用信号屏蔽技术可以防止函数执行过程中被信号中断,下面给出一个实例来说明。

#include <signal.h>#include <stdio.h>#include <stdlib.h>int g_v[10];//全局变量int *h_v;//堆中的数据void set(int val){int a_v[10];// 局部变量int i = 0;//注意到这里的i是局部变量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]);elseprintf("%d", g_v[i]);}printf("\n");printf("h_v:");for(i = 0; i < 10; ++i){if(i != 0)printf(",%d", h_v[i]);elseprintf("%d", h_v[i]);}printf("\n");printf("a_v:");for(i = 0; i < 10; ++i){if(i != 0)printf(",%d", a_v[i]);elseprintf("%d", a_v[i]);}printf("\n");}void sig_handler(int signo){if(signo == SIGTSTP){printf("SIGTSTP occured\n");set(20);// 在信号处理函数中又调用setprintf("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;sigemptyset(&sigset);sigfillset(&sigset);// 要屏蔽所有的信号//这里我们不需要获得原内核中的信号屏蔽字,第三个参数设为NULL        if(sigprocmask(SIG_SETMASK, &sigset, NULL) < 0)perror("sigprocmask error"); set(10);//解除信号屏蔽if(sigprocmask(SIG_UNBLOCK, &sigset, NULL) < 0)perror("sigprocmask error");printf("end running main\n");return 0;}
这里我们对“函数的可重入性”中的函数进行了改进。在执行set函数之前,先通过sigfillset函数和sigprocmask函数屏蔽了1~31号信号,这样的话,在执行set过程中如果发生了信号,则不会影响第一次的结果输出。后面我们对信号解除了屏蔽,这样的话,在第一次执行set中产生的信号仍然被捕获,再次执行set函数。运行结果如下:


在第一次赋值过程中产生了一个SIGTSTP信号。


2 0
原创粉丝点击