`

详解sigaction

阅读更多
本杂文主要是讲解了下信号和进程的关系。前面主要是一些man式的资料描述和书上一些例子的摘要。因为我想一篇记载性的东西多少得放点让人有点回忆性的代码和知识点。主要内容可从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:
SIG_IGN 			Ignore the signal.
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.
void ouch(int sig)
{
	printf("OUCH ! - I got signal %d\n", sig);
	signal(SIGINT, SIG_DFL);	
}

int main(int argc, char **argv)
{
	signal(SIGINT, ouch);	
	
	while(1)
	{
		printf("Hello World!\n");
		sleep(1);	
	}
	
	return 0;
}


来点强壮的
X/Open规范的更新更健壮的接口
#include <signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
oact如果不为空,将把前次act的状态保存下来。
这个函数主要的是加入了信号集(sa_mask)这个功能。比如前面提到的,如果信号先发出而后调用pause(),则遗失掉这个信号。采用信号集他可以先收集或者说阻塞不传递给主进程,由主进程再来自主调用和处理。

void (*) (int) sa_handler 	  /* function, SIG_DFL or SIG_IGN
sigset_t sa_mask 		  /* signals to block in sa_handler
int sa_flags 		  	 /* signal action modifiers,SA_RESETHAND,具有reset功能


对这个信号集有如下几种操作:
初始为空集,初始为所有已有的信号,增加新信号,删除指定信号
#include <signal.h>
int sigaddset(sigset_t *set, int signo);
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigdelset(sigset_t *set, int signo);

然后是一个批处理的函数
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
SIG_BLOCK 	The signals in set are added to the signal mask.
SIG_SETMASK 	The signal mask is set from set.
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:

SA_NOCLDSTOP 		Don’t generate SIGCHLD when child processes stop.
SA_RESETHAND 		Reset signal action to SIG_DFL on receipt.
SA_RESTART 		Restart interruptible functions rather than error with EINTR.
SA_NODEFER 		Don’t add the signal to the signal mask when caught.



通过以上的操作介绍,似乎已经很完美了。但是最大的问题来了,先了解几个概念:
不完全重入函数:即可能被其他信号中断触发EINTR

假设我们现在正在执行一个信号处理函数,突然发生一个中断,那么该信号函数将被打断。在一次情况下似乎没什么问题。但是假如连续性的被信号打断,而导致函数不断重启。就比如执行到一半退出,又执行,又退,又执行。。那么可能就有问题发生。有两个方法可以解决
1、根本方法,用完全重入函数。如下图所示英文版 P474
2、如果非不得以,那就不让信号来阻断信号函数的运行。可以用SA_RESTART标志把信号先放到屏蔽集的缓冲区里

SA_RESTART(似乎默认也是这个行为)
void ouch(int sig)
{
 //..
 select()//10秒
 //..
}

int main(int argc, char **argv)
{
	struct sigaction act, act_g;
	act.sa_handler = ouch;
	act.sa_flags = SA_RESTART;   //设置重启
	sigemptyset(&act.sa_mask);
	sigaddset(&act.sa_mask, SIGTERM);

	if( -1==sigaction(SIGTERM, &act, 0))  //捕捉SIGTERM
	{
		perror("sigaction\n");	
	}

	select//10秒

}

操作如下,首先运行该程序,然后在另一个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标志操作。写到这里,把屏蔽集合集看成一个临时的信号存放缓冲区更形象点。
3
1
分享到:
评论

相关推荐

    sigaction.c 文件

    sigaction.c 文件下载后直接编译运行,详情看博客

    信号捕捉函数sigaction

    sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)

    Linux系统调用 sigaction 的用法

    sigaction 函数原型定义如下:  int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact)  这个系统调用的作用是改变进程接收到的指定信号的行动。  使用这个函数需要包含头文件#...

    C例子:IPC-sigaction

    该程序是我写的博客“一起talk C栗子吧(v第八十六回:C语言实例--使用信号进行进程间通信三)”的配套程序,共享给大家使用

    linux 下实现sleep详解及简单实例

    linux 下实现sleep详解及简单实例 sleep: 普通版本 1、基本设计思路:  1&gt;注册SIGALRM信号的处理函数;  2&gt;调用alarm(nsecs)设定闹钟;  3&gt;调⽤pause等待,内核切换到别的进程运行;  4&gt;nsecs秒之后,闹钟超时,内...

    c处理函数 学习c精通必备

     定义函数 int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);  函数说明 sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和...

    Linux被中断的系统如何调用详解

    前言 慢系统调用,指的是可能永远无法返回,从而使进程永远... 如果信号处理函数是用sigaction注册的 默认情况下,系统调用不会自动重启,函数将返回失败,同时errno被置为EINTR 只有中断信号的SA_RESTART标志有效时

    Linux进程通信之信号

    sigaction函数的具体介绍

    linux进程间通信——信号机制

    对linux信号机制的详细阐述,包括signal,sigaction等函数的用法,并配以实例,通俗易懂,适合初学者阅读。。。

    linux下几种最常用的IPC接口,这样一来,统一了接口,提高代码重用性.rar

    linux除了支持Unix早期信号语义函数signal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);...

    设置网络延时的时长(练习)

    利用setsockopt函数和sigaction函数分别实现修改网络延时的时长

    NDK20_线程轮询实现双进程守护

    在Linux系统下,如果使用sigaction将信号SIGCHLD的sa_flags中的SA_NOCLDSTOP选项打开,当子进程停止(STOP作业控制)时, 不产生此信号(即SIGCHLD)。不过,当子进程终止时,仍旧产生此信号(即SIGCHLD)。 3....

    Linux高性能服务器编程

    10.1.3 Linux信号 10.1.4 中断系统调用 10.2 信号函数 10.2.1 signal系统调用 10.2.2 sigaction系统调用 10.3 信号集 10.3.1 信号集函数 10.3.2 进程信号掩码 10.3.3 被挂起的信号 10.4 统一事件源 10.5 ...

    宋劲彬的嵌入式C语言一站式编程

    4.2. sigaction 4.3. pause 4.4. 可重入函数 4.5. sig_atomic_t类型与volatile限定符 4.6. 竞态条件与sigsuspend函数 4.7. 关于SIGCHLD信号 34. 终端、作业控制与守护进程 1. 终端 1.1. 终端的基本概念 1.2. 终端...

    my_gperftools-2.0.tar.gz

    sa.sa_sigaction_ = SignalHandler; 修改src/base/linuxthreads.cc static void SignalHandler(int signum, siginfo_t *si, void *data) 其中 siginfo_t *si 改成 siginfo *si echo "please modify the src/...

    Linux内核中的信号机制–从用户层到内核层

    主要有两个函数实现信号的注册:signal()和sigaction()。  2、signal()  signal()的函数原型如下: void (*signal(int signum, void (*handler)(int)))(int);  在使用该调用的进程中加入以下头文件: ...

    Linux线程编程之信号处理

     本文通过sigwait()调用来“等待”信号,而通过signal()/sigaction()注册的信号处理函数来“捕获”信号,以体现其同步和异步的区别。  一 概念  1.1 进程与信号  信号是向进程异步发送的软件通知,通

    preeny:一些有用的预载库,用于填充东西

    Preeny具有以下模块: 姓名概括发牌器禁用警报() 叉禁用fork() 痕迹禁用ptrace() 德兰禁用rand()和random() 设计禁用sigaction() 脱壳将套接字通信通道传递到控制台desock_dup 将套接字通信通道传送到...

    Linux内核之Kernel目录

    4、在signal.c中我们将会学习到信号处理过程中内核堆栈和用户堆栈的操作过程,对sigaction函数与signal函数有更深入的理解; 5、通过exit.c的学习对系统编程中的kill、waitpid函数会有更深入的理解; 5、mktime.c中...

Global site tag (gtag.js) - Google Analytics