赞
踩
本章内容主要对之前的内容做一些补充。
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
之前我们学过signal来对信号进行捕捉,sigaction也是一个对信号进行捕捉的系统接口函数,不过sigaction要相对复杂一些。
参数 int signum 是要捕捉的信号编号。
参数struct* sigaction 在这里作为输入型参数,是提供给我们的一个结构体指针类型,这里的结构体名和函数名相同。
参数struct sigaction *oldact 在这里作为输出型参数。
那么struct sigaction 里面有什么呢?
struct sigaction { /* Signal handler. */ #ifdef __USE_POSIX199309 union { /* Used if SA_SIGINFO is not set. */ __sighandler_t sa_handler; /* Used if SA_SIGINFO is set. */ void (*sa_sigaction) (int, siginfo_t *, void *); } __sigaction_handler; # define sa_handler __sigaction_handler.sa_handler # define sa_sigaction __sigaction_handler.sa_sigaction #else __sighandler_t sa_handler; #endif /* Additional set of signals to be blocked. */ __sigset_t sa_mask; /* Special flags. */ int sa_flags; /* Restore handler. */ void (*sa_restorer) (void); };
我们今天主要对函数体内部的sa_handler和sa_mask进行讨论
typedef void __signalfn_t(int);
typedef __signalfn_t *__sighandler_t;
根据__sighandler_t的定义,我们可以知道其本质是一个函数指针,所以这里的我们就可以知道其实本质也是像signal一样使用回调函数来进行信号的捕捉。
typedef __sigset_t sigset_t;
之前我们在学习sigprocmask和sigaddset等信号集接口函数的时候有过接触sigset_t,那么这里的sa_mask是什么呢?
先提出一个观点,在一个信号被处理(递达)过程中,如果同一个信号再次被发送且进入pending表,那么OS是怎样处理的? OS的处理方式是block(阻塞)相同信号,不再重复递达,等到处理完正在被处理的信号再根据情况决定。 而sa_mask在这里的作用就是可以根据其信号集的有效信号,在signum信号正在被处理时,同时阻塞sa_mask的有效信号和其本身信号。
示例代码如下
#include<iostream> #include<cstdio> #include<signal.h> #include<unistd.h> void ShowPending() { sigset_t pending; sigemptyset(&pending); for (int i = 1; i <= 31; i++) { sigpending(&pending); // 通过sigismember来打印我们的pending信号集 std::cout << sigismember(&pending, i); } std::cout << std::endl; } void catchSig(int signum) { std::cout << "捕捉到" << signum << "信号!" << std::endl; int count = 0; while(1) { ShowPending(); count++; if(count == 50) break; sleep(5); } } int main() { std::cout << "pid: " << getpid() << std::endl; //1.定义struct sigaction类型 struct sigaction act , oldact; //2.mask信号集初始化 sigset_t mask; sigemptyset(&mask); //3.mask信号集添加1号,2号,3号, 4号,5号,6号作为有效信号 sigaddset(&mask,1); sigaddset(&mask,2); sigaddset(&mask,3); sigaddset(&mask,4); sigaddset(&mask,5); sigaddset(&mask,6); //4.修改act中的数据 act.sa_handler = catchSig; act.sa_mask = mask; //5.调用sigaction sigaction(2, &act , &oldact); while(1) sleep(1); return 0; }
运行结果
我们之前的学习过程中,也提到过编译器会进行优化,例如我们之前讲的拷贝构造和右值引用都有提到过,而volatile主要解决关于编译器优化所导致的问题。
是的,编译器优化在少数情况下是会造成一些问题的。
而Linux中的gcc编译器是有几种不同程度的优化方案的
-O -O0 -O1 -O2 -O3 -Os -Ofast -Og
在使用gcc或g++命令时,上面的选项从左到右,编译时优化程度依次变大。
示例代码如下
#include<iostream> #include<cstdio> #include<signal.h> #include<unistd.h> int flag = 0; void catchSig(int signum) { std:: cout << flag ; flag = 1; std::cout << "->" << flag <<std::endl; } int main() { signal(2, catchSig); while(1) { if(flag == 1) break; ; } std::cout << "程序正常退出" << std::endl; return 0; }
这段代码如果使用
g++ -o mysignal mysignal.cc -std=c++11
进行编译
结果则是
这段代码如果使用
g++ -o mysignal mysignal.cc -std=c++11 -O3
进行编译
结果则是
程序不会自动退出。
这是因为在-O3的优化程度下,编译器检测默认执行流不会修改flag的数据,所以这里的cpu寄存器一直储存着原有的flag值0,导致在判断flag时,一直使用寄存器中的0在判断,导致循环无法退出。
现在我们使用volatile来试试
#include<iostream> #include<cstdio> #include<signal.h> #include<unistd.h> volatile int flag = 0; void catchSig(int signum) { std:: cout << flag ; flag = 1; std::cout << "->" << flag <<std::endl; } int main() { signal(2, catchSig); while(1) { if(flag == 1) break; ; } std::cout << "程序正常退出" << std::endl; return 0; }
这个时候程序就正常推出了,所以这里volatile的意思就是让告诉编译器不要对flag进行优化,要让寄存器看到内存中的数据!
SIGCHLD 在子进程停止或者退出时可能收到。
所以我们再学习几种进程等待的方法。
#include <iostream> #include <cstdio> #include <signal.h> #include <unistd.h> int main() { signal(SIGCHLD, SIG_IGN); pid_t id = fork(); if(id == 0) { //子进程 sleep(10); exit(0); } //父进程 while(1); return 0; }
将SIGCHLD信号的处理方式变为忽略。
子进程没有僵尸,而是成功回收。
#include <iostream> #include <cstdio> #include <signal.h> #include <unistd.h> #include <sys/wait.h> void catchCHLD(int signum) { std::cout << "捕捉到SIGCHLD信号!" << std::endl; int pid = 0; while((pid = waitpid(0,nullptr,WNOHANG)) > 0) { std::cout << "成功等待" << pid << "号进程" << std::endl; } } int main() { signal(SIGCHLD, catchCHLD); pid_t id = fork(); if(id == 0) { //子进程 sleep(10); exit(0); } //父进程 while(1); return 0; }
也同样可以成功回收!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。