在阅读《APUE》的第十章第16节关于sigsuspend部分的内容时,不太清楚调用该函数后对原有进程阻塞的信号是否仍然阻塞这个问题,加上书中给出的第一个例子里信号处理程序输出的阻塞信号居然是SIGINT和SIGUSR1,这就更加导致了我对这一问题的疑惑。以下是源码:
- #include <unistd.h>
- #include <signal.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
- static void pr_mask(const char *cmd)
- {
- sigset_t sigset;
- int errno_save;
- /*防止对信号集进行判断操作时,改变errno里的值*/
- errno_save = errno;
- if(sigprocmask(0, NULL, &sigset) < 0)
- {
- printf("%s : sigprocmask failed!\n",__func__);
- return -1;
- }
- printf("%s :",cmd);
- if(sigismember(&sigset, SIGINT) )
- printf("SIGINT ");
- if(sigismember(&sigset, SIGQUIT) )
- printf("SIGQUIT ");
- if(sigismember(&sigset, SIGUSR1) )
- printf("SIGUSR1 ");
- if(sigismember(&sigset, SIGALRM) )
- printf("SIGALARM ");
- printf("\n");
- errno = errno_save;
- }
- static void sig_deal(int signo)
- {
- pr_mask("\n In sig_deal!");
- }
- int main(void)
- {
- sigset_t newmask, oldmask, waitmask;
- pr_mask("program start");
- if(signal(SIGINT, sig_deal) == SIG_ERR)
- {
- printf("%s : signal function failed!\n",__func__);
- return -1;
- }
- sigemptyset(&newmask);
- sigemptyset(&waitmask);
- sigaddset(&newmask, SIGINT);
- sigaddset(&waitmask ,SIGUSR1);
- if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
- {
- printf("%s : sigprocmask failed!\n",__func__);
- return -1;
- }
- pr_mask("\n In middle");
- if(sigsuspend(&waitmask) != -1)
- {
- printf("%s : sigsuspend failed!\n",__func__);
- return -1;
- }
- pr_mask("\n after sigsuspend");
- if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
- {
- printf("%s : sigprocmask failed!",__func__);
- return -1;
- }
- pr_mask("\n In end");
- return 0;
- }
输出信息:
program start :
In middle :SIGINT
^C
In sig_deal! :SIGINT SIGUSR1
after sigsuspend :SIGINT
In end :
在网上阅读了不少网友的博文和前后回顾了《APUE》的内容之后,终于明白在信号处理函数中打印SIGINT和SIGURS1的原因:这是因为执行了以下步骤:
1) 进程调用第一个sigprocmask阻塞信号SIGINT,于是打印“In middle :SIGINT”
2) 进程调用sigsuspend之后,进程的信号屏蔽字被替换为至阻塞信号SIGUSR1。此时进程被挂起,等待除SIGUSR1以外的信号使进程从sigsuspend中唤醒。这里有两种情况:
a) 信号使进程结束,则sigsuspend不会返回
b) 执行信号处理程序,sigsuspend返回-1,并且将进程屏蔽字替换为调用函数之前的屏蔽字。
3) 产生中断信号SIGINT之后,进入信号处理函数sig_deal,输出的内容是:
“In sig_deal! :SIGINT SIGUSR1”。
此时sigsuspend并未返回,因此对信号SIGUSR1是保持阻塞的。并且因为是在信号处理程序之中,所以系统自动将SIGINT阻塞,以防止信号处理程序被中断的情况发生。这里并不是因为sigsuspend返回了,所以有对SIGINT的阻塞。
4) sigsuspend返回之后,恢复了之前对信号SIGINT的阻塞,因此输出的内容是:
“after sigsuspend :SIGINT”。
Sigsuspend提供了一种等待信号的系统调用,和pause类似。只是当需要等待某个信号的时候,直接调用pause会有一个时间窗口。如果在该时间窗口内产生了信号,则pause将接收不到这个信号,永远阻塞下去。Sigsuspend则不一样,它提供了一个原子操作,先恢复信号屏蔽字,然后使其休眠。
注意:清晰且可靠的等待信号到达的方式,通常是先调用sigprocmask阻塞该信号,然后调用sigsuspend放开对此信号的阻塞,等待信号产生,执行信号处理程序返回之后,再调用sigprocmask将信号屏蔽字还原。
另外如果需要在信号处理程序中执行一些复杂的操作,则可以这样设计程序:
1) 信号处理程序对全局变量置位
2) 主程序根据全局变量的置,执行后续操作。
以下是一个简单的演示:
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <unistd.h>
- #define STR_LEN 64
- #define CONFIG_TEST “xxx"
- volatile sig_atomic_t flag;
- static void sig_handler(int signo)
- {
- flag = 1;
- printf("In signal handler function!\n");
- }
- int show_file_info(void)
- {
- FILE *fp = NULL;
- char str_tmp[STR_LEN] = {0};
- fp = fopen(CONFIG_TEST, "r");
- if(NULL == fp)
- {
- printf("%s : can't open file %s!\n", __func__, CONFIG_TEST);
- return -1;
- }
- printf("%s : show file info!\n",__func__);
- while(fgets(str_tmp, STR_LEN, fp) != NULL)
- {
- printf("str_tmp is %s", str_tmp);
- }
- fclose(fp);
- fp = NULL;
- return 0;
- }
- int main()
- {
- sigset_t newmask,oldmask,zeromask;
- if(signal(SIGUSR1, sig_handler) == SIG_ERR)
- {
- printf("%s : signal failed!\n ",__func__);
- return -1;
- }
- sigemptyset(&zeromask);
- sigemptyset(&newmask);
- sigaddset(&newmask, SIGUSR1);
- if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
- {
- printf("%s : step1, sigprocmask failed!\n",__func__);
- return-1;
- }
- while(flag == 0)
- sigsuspend(&zeromask);
- printf("%s : after signal handler!\n", __func__);
- flag = 0;
- if(show_file_info() < 0)
- {
- printf("%s : show_file_info failed!\n",__func__);
- return -1;
- }
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
{
printf("%s : sigprocmask failed!",__func__);
return -1;
}
pr_mask("\n In end");
return 0;
}
该程序主要就是等待信号SIGUSR1到来,然后去CONFILE_TEST里读取文件内容。但是因为要将信号处理程序设计的简单一些,所以改成信号处理程序只是i只为全局的标志,主程序真正执行相对应的操作。要顺利执行此程序需要另一个程序用kill函数向其发送一个信号SIGUSR1或者在shell下输入kill –SIGUSR1 pid(进程号)。
本文参考文章:http://www.cublog.cn/u2/82750/showart_2158032.html
http://bigwhite.blogbus.com/logs/1453143.html
阅读(2522) | 评论(1) | 转发(0) |