详解sigaction
这是挺好理解的,就好比在系统这个大进程里运行许多派生的进程,为了协调这些派生出的子进程,就必然要使用一些手段来通知监视。而信号就是这样一种系统级别的全局变量的通知。想想在写程序中,多个函数协调一个全局函数的情形。。。
the signal is an event generated by the UNIX and Linux systems in response to some condition,upon receipt of which a process may in turn take some action.
函数
我想我需要如下系列的函数,修改本身的信号处理函数,对其他进程发送信号,
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);
which
is the previous value of the function set up to handle this signal, or one of these two special values:
代码
1. SIG_IGN Ignore the signal.
2. SIG_DFL Restore default behavior.
比如想捕捉SIGINT信号,但是我们又只想捕捉一次,就可以用到DFL信号来恢复之前的行为,可能会是这样
但要注意的是,It is no safe to call all function, such as printf, from within a signal handler.A useful technique is to use a signal handler to set flag and then check that flag from the main
pg and print a message if required.Toward the end of the chapter, you will find a list of calls that can safely be made inside signal handlers.
代码
1. void ouch(int sig)
2. {
3. printf("OUCH ! - I got signal %d\n", sig);
4. signal(SIGINT, SIG_DFL);
5. }
6.
7. int main(int argc, char **argv)
8. {
9. signal(SIGINT, ouch);
10.
11. while(1)
12. {
13. printf("Hello World!\n");
14. sleep(1);
15. }
16.
17. return 0;
18. }
来点强壮的
X/Open规范的更新更健壮的接口
#include <signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
oact如果不为空,将把前次act的状态保存下来。
这个函数主要的是加入了信号集(sa_mask)这个功能。比如前面提到的,如果信号先发出而后调用pause(),则遗失掉这个信号。采用信号集他可以先收集或者说阻塞不传递给主进程,由主进程再来自主调用和处理。
代码
1. void (*) (int) sa_handler /* function, SIG_DFL or SIG_IGN
2. sigset_t sa_mask /* signals to block in sa_handler
3. int sa_flags /* signal action modifiers,SA_RESETHAND,具有reset功能
对这个信号集有如下几种操作:
初始为空集,初始为所有已有的信号,增加新信号,删除指定信号
Java代码
1. #include <signal.h>
2. int sigaddset(sigset_t *set, int signo);
3. int sigemptyset(sigset_t *set);
4. int sigfillset(sigset_t *set);
5. int sigdelset(sigset_t *set, int signo);
然后是一个批处理的函数
代码
1. int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
2. SIG_BLOCK The signals in set are added to the signal mask.
3. SIG_SETMASK The signal mask is set from set.
4. SIG_UNBLOCK The signals in set are removed from the signal mask.
判断是否是当前信号集里的信号
int sigismember(sigset_t *set, int signo);
最后就是最重要的,对信号集里的信号进行操作,假设已经把信号收集到,主程序可以阻塞/非阻塞式的调用。(不过非阻塞的调用好象不能自动把收到的信号清除掉,阻塞式的就可以自动清除)。
If a signal is blocked by a process, it won’t be delivered, but will remain pending. A program can determine
which of its blocked signals are pending by calling the function sigpending.
#include <signal.h>
int sigpending(sigset_t *set);
A process can suspend execution until the delivery of one of a set of signals by calling sigsuspend. This
is a more general form of the pause function we met earlier.
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
sigaction Flags
The sa_flags field of the sigaction structure used in sigaction may contain the following values to
modify signal behavior:
代码
1. SA_NOCLDSTOP Don’t generate SIGCHLD when child processes stop.
2. SA_RESETHAND Reset signal action to SIG_DFL on receipt.
3. SA_RESTART Restart interruptible functions rather than error with EINTR.
4. SA_NODEFER Don’t add the signal to the signal mask when caught.
通过以上的操作介绍,似乎已经很完美了。但是最大的问题来了,先了解几个概念:
不完全重入函数:即可能被其他信号中断触发EINTR
假设我们现在正在执行一个信号处理函数,突然发生一个中断,那么该信号函数将被打断。在一次情况下似乎没什么问题。但是假如连续性的被信号打断,而导致函数不断重启。就比如执行到一半退出,又执行,又退,又执行。。那么可能就有问题发生。有两个方法可以解决
1、根本方法,用完全重入函数。如下图所示英文版 P474
2、如果非不得以,那就不让信号来阻断信号函数的运行。可以用SA_RESTART标志把信号先放到屏蔽集的缓冲区里
代码
1. SA_RESTART(似乎默认也是这个行为)
2. void ouch(int sig)
3. {
4. //..
5. select()//10秒
6. //..
7. }
8.
9. int main(int argc, char **argv)
10. {
11. struct sigaction act, act_g;
12. act.sa_handler = ouch;
13. act.sa_flags = SA_RESTART; //设置重启
14. sigemptyset(&act.sa_mask);
15. sigaddset(&act.sa_mask, SIGTERM);
16.
17. if( -1==sigaction(SIGTERM, &act, 0)) //捕捉SIGTERM
18. {
19. perror("sigaction\n");
20. }
21.
22. select//10秒
23.
24. }
操作如下,首先运行该程序,然后在另一个tty里发送一个kill命令(在第一个select未结束前),那么将进入信号处理函数ouch,接着在ouch的select运行之际,再次发送kill,因为select也不完全函数,将会被再次打断,而又一次进入信号。但是因为设置了SA_RESTART,ouch将不会被打断,而是执行完后,接着执行响应第二次的信号函数。
SA_NODEFER
和上面一样的操作,发送第二次KILL信号时,第一个信号函数马上中断(由于是select),又再次进入信号函数。这里加点东西打印可以更清楚些。
流程图
以上这两种实现,有个东西起了至关重要的作用。那就是进程的信号屏蔽字。原理流程大概是这样的,当向一个进程发送信号时,可根据是否被屏蔽掉而发送至进程信号屏蔽字集合中或者进程本身。对于后者,sigaction可直接捕捉到,而前者,可以看成是一个暂存的集合,可用sigpending来取得。通常的情况是,已经用了sigaction函数直接获取信号,如果再次触发信号会马上跳出当前正在执行的信号函数而再次执行信号函数。而SA_RESTART这个标志应该是将信号保存到被屏蔽的信号集合里等待下次取出后执行,保证了当前函数的运行。SA_NODEFER就不做这一操作,直接发送到进程,让sigaction继续马上捕捉。
整个走向可看下图,当然这里没有参考系统源码,必然存在着一定的疏忽
目前已知的对屏蔽集的操作有pending/suspend函数,SA_RESTART,SA_NODEFER标志操作。写到这里,把屏蔽集合集看成一个临时的信号存放缓冲区更形象点。
sigaction
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
通过sa_mask设置信号掩码集。
信号处理函数可以采用void (*sa_handler)(int)或void (*sa_sigaction)(int, siginfo_t *, void *)。到底采用哪个要看sa_flags中是否设置了SA_SIGINFO位,如果设置了就采用void (*sa_sigaction)(int, siginfo_t *, void *),此时可以向处理函数发送附加信息;默认情况下采用void (*sa_handler)(int),此时只能向处理函数发送信号的数值。
sa_falgs还可以设置其他标志:
SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
#define INPUTLEN 100
void inthandler(int);
int main(){
struct sigaction newhandler;
sigset_t blocked; //被阻塞的信号集
char x[INPUTLEN];
newhandler.sa_flags=SA_RESETHAND;
newhandler.sa_handler=inthandler;
sigemptyset(&blocked); //清空信号处理掩码
sigaddset(&blocked,SIGQUIT);
newhandler.sa_mask=blocked;
if(sigaction(SIGINT,&newhandler,NULL)==-1)
perror("sigaction");
else
while(1){
fgets(x,INPUTLEN,stdin); //fgets()会在数据的最后附加"\0"
printf("input:%s",x);
}
}
void inthandler(int signum){
printf("Called with signal %d\n",signum);
sleep(signum);
printf("done handling signal %d\n",signum);
}
复制代码
Ctrl-C向进程发送SIGINT信号,Ctrl-\向进程发送SIGQUIT信号。
$ ./sigactdemo
^CCalled with signal 2
^\done handling signal 2
Quit (core dumped)
由于把SIGQUIT加入了信号掩码集,所以处理信号SIGINT时把SIGQUIT屏蔽了。当处理完SIGINT后,内核才向进程发送SIGQUIT信号。
$ ./sigactdemo
^CCalled with signal 2
^Cdone handling signal 2
由于设置了SA_RESETHAND,第一次执行SIGINT的处理函数时相当于执行了signal(SIGINT,SIG_DFL),所以进程第二次收到SIGINT信号后就执行默认操作,即挂起进程。
修改代码,同时设置SA_RESETHAND和SA_NODEFER。
newhandler.sa_flags=SA_RESETHAND|SA_NODEFER;
$ ./sigactdemo
^CCalled with signal 2
^C
在没有设置SA_NODEFER时,在处理SIGINT信号时,自动屏幕SIGINT信号。现在设置了SA_NODEFER,则当SIGINT第二次到来时立即响应,即挂起了进程。
首先,需要理解几个signal相关的函数。
sigaddset(sigset_t* sigSet, int sigNum ) : 将信号sigNum 添加到信号集 sigSet 中;
sigdelset(sigset_t* sigSet, int sigNum) : 将信号 sigNum 从信号集 sigSet 中删除;
sigemptyset(sigset_t* sigSet) : 清空信号集;
sigfillset(sigset_t* sigSet) : 在信号集中打开所有的信号。
但是这个时候只是定义好了如此一个信号集,还有对信号的操作函数:
pthread_sigmask(int opCode, sigset_t* sigSet, sigset_t* oldSigSet) : opCode 指定了如何对 sigSet 里的信号进行处理。opCode 有三个值: SIG_BLOCK (将sigSet中的信号加到当前线程的屏蔽集中),SIG_UNBLOCK (将sigSet 中的信号从当前线程屏蔽集中删除),SIG_SETMASK (将sigSet 设为当前线程的屏蔽集)。 若oldSigSet != NULL,则将之前的信号屏蔽集存入其中。
另外还有个函数 sigprocmask() 也有类似功能。区别是:pthread_sigmask() 是线程库函数,用于多线程进程。sigprocmask() 是旧的实现,用于单线程的进程。
sigwait(sigset_t* sigSet, int* sigNum) : 当前线程等待 sigSet 中的信号。没有捕获到信号时,线程挂起;当捕获到时,函数返回,并将信号值存入 sigNum。
sigaction(int sigNum, sigaction* newAct, sigaction* oldAct) : 捕获信号 sigNum,并调用相应的处理函数(定义在 newAct 中)。
虽然sigwait() 和 sigaction() 都是用于捕获信号,但两者还是有较大区别:sigwait() 是阻塞的,线程会一直挂起直到捕获到信号,并且对信号的处理是定义在 sigwait()后的,只会在当前线程内执行;而sigaction()是非阻塞的,当信号被捕获时,会由进程内当前被调度到的线程来执行处理函数(好像是,not very sure...),被哪个线程处理是随机的。
所以,sigaction()适用于对实时性要求很高的时候。而在普通情况下建议使用sigwait(),因为其具有较好的可控性。
另外,还需要注意的是,SIG_KILL(大家应该都用过kill -9 吧) 和 SIG_STOP 是不能被用户屏蔽或捕获的。
好了,当理解了这几个函数后,可以自己试着来对信号进行处理了。对于lz的需求,简单举例如下:
sigset_t blockSet, waitSet;
int sigNum;
sigfillset(&blockSet); // open all signals in blockSet
pthread_sigmask(SIG_BLOCK, blockSet, NULL); //block all signals (except SIG_KILL & SIG_STOP also) in current thread
sigemptyset(&waitSet); // empty all signals in waitSet
sigaddset(&waitSet, SIG_XXX); // a signal wanted to be captured
sigaddset(&waitSet, SIG_YYY); // another signal wanted to be captured
if ( sigwait(&waitSet, &sigNum) != 0 ) // wait for wanted signals
{
perror("Error - sigwait() is failure!\n");
exit(1);
}
switch (sigNum)
{
case SIG_XXX:
printf("SIG_XXX is captured!\n");
break;
case SIG_YYY:
printf("SIG_YYY is captured!\n");
break;
default:
printf("Error - cannot reach here!\n");
exit(1);
}
return OK;
另外,用 sigaction() 也可以实现。
相关推荐
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
,,基于SMO的三相PMSM无速度传感器控制(基于反正切函数) ,核心关键词:SMO(滑模观测器); 三相PMSM(永磁同步电机); 无速度传感器控制; 反正切函数; 控制系统。,基于SMO算法的三相PMSM无速度传感器反正切函数控制
网络文化互动中的舆论引导与危机应对
人力资源+大数据+薪酬报告+涨薪调薪,在学习、工作生活中,越来越多的事务都会使用到报告,通常情况下,报告的内容含量大、篇幅较长。那么什么样的薪酬报告才是有效的呢?以下是小编精心整理的调薪申请报告,欢迎大家分享。相信老板看到这样的报告,一定会考虑涨薪的哦。
内容概要:本文全面探讨了大学生沉迷网络游戏的现状及成因,强调该问题已严重影响大学生的学业和个人发展。据统计显示,中国大学生网络游戏成瘾患病率超过15%,问题广泛且严重。分析指出沉迷原因涵盖个人因素(如自我管理能力缺失、逃避现实压力)、家庭因素(例如家庭教育缺失和家庭氛围不和谐)、学校因素(如大学管理松散和校园文化活动匮乏),以及社会因素(例如网游设计吸引人和监管部门不严)。基于以上成因,提出了多层次综合治理方案,包括但不限于强化家庭教育和沟通、改善大学管理模式、丰富校园文化、加强网络游戏审查力度和社会心理健康辅导等方面的对策。 适用人群:本研究适用于高校辅导员、心理学家、教育政策决策人员,以及关心青年成长的社会各界人士。 使用场景及目标:本文旨在引起社会对该问题的关注,并为教育界和其他相关群体提供了详细的参考资料用于制定相应的干预措施,以减少大学生游戏成瘾情况的发生。此外,也可供家长学习科学育子知识。 其他说明:除了直接提出具体治理办法外,还特别提到了营造健康的网络文化环境的重要性,提倡多方协作共促学生健康发展。同时呼吁进一步加强对网络游戏产业的研究与管理,确保产业的良性发展的同时也能
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
矢量边界,行政区域边界,精确到乡镇街道,可直接导入arcgis使用
TI维也纳整流器设计.rar
自驾游中的手机APP推荐
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
视讯镜头专利复现,基本复现
,,OMRON CP1H PLC脉冲控制三轴伺服, 码垛机,实际项目,程序结构清析,有完整的注释,重复功能做成FB功能块,在其它项目可以导出直接用,MCGS触摸屏程序,有电气CAD图纸。 ,关键词:OMRON CP1H PLC;脉冲控制;三轴伺服;码垛机;程序结构清晰;完整注释;FB功能块;MCGS触摸屏程序;电气CAD图纸。,OMRON PLC三轴伺服脉冲控制程序:结构清晰、注释完整,FB功能块可复用,配合MCGS触摸屏及CAD图纸的实际项目应用
是一款基于JAVA的串口调试工具,支持波特率9600-115200,仅供参考学习使用,
,,CO2激光切割机雕刻机打标机写字机喷涂机巡边机控制软件,包含上位机和控制板,也可源码 视频展示只体现工作流程和加工效果,如果激光功率足够大最快速度能跑到每秒两米 支持文件格式说明: 控制版和上位机通信接口为百兆以太网接口,数据载体为标准TCP协议 1.g代码 2.打印图片 3.plt格式文件 4.激光机在切割有效线条时匀速切割 5.有效线条切割速度和空程速度分别设置 6.空程运行具备加减速控制 7.图片打印时上位机界面实时显示打印进度 8.打开的图片和图形文件可鼠标缩放和拖动 9.图片格式转并保存转完成的指定格式图片 10.手动回原点控制 ,核心关键词: CO2激光切割机; 雕刻机; 打标机; 写字机; 喷涂机; 巡边机; 控制软件; 上位机; 控制板; 源码; 视频展示; 工作流程; 加工效果; 激光功率; 速度; 两秒; 文件格式; g代码; 打印图片; plt格式文件; 有效线条切割; 空程速度设置; 加减速控制; 上位机界面实时显示; 图片缩放和拖动; 图片格式转换; 手动回原点控制。 关键词用分号隔开: CO2激光切割机; 喷涂机; 控制软件; g代码; 图片格式转
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
城市周边自驾游短途路线
漫画作品与粉丝经济新模式