`

详解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

    ### 信号捕捉函数sigaction详解 #### 一、引言 在计算机编程中,尤其是在Linux环境下,信号(Signal)作为一种重要的通信机制被广泛应用于进程间通信。为了更好地控制信号的行为,POSIX标准引入了`sigaction`函数...

    linux C函数详解

    4. **信号处理**:`signal`和`sigaction`函数允许程序响应特定的系统事件,如键盘中断或定时器事件。信号处理是实现异步错误处理和协调进程行为的关键。 5. **线程编程**:在Linux中,可以使用`pthread`库进行多...

    linux编程技术详解源码

    这份"linux编程技术详解源码"提供了丰富的实践示例,帮助我们深入理解这些概念。以下将详细介绍其中涵盖的一些关键知识点。 1. **系统调用**:Linux编程的基础是系统调用,它们是操作系统提供给用户程序的接口。...

    《Linux编程技术详解》随书光盘源程序

    学习如何使用`signal`、`raise`、`sigaction`等函数来捕获和处理信号是非常实用的。 5. **网络编程**:Linux提供了丰富的网络编程接口,如socket API,用于创建TCP/UDP套接字、进行连接、发送和接收数据。光盘中...

    Linux C 函数详解

    8. **信号处理**:`signal()` 和 `sigaction()` 用于设置信号处理器,`raise()` 用于发送信号给当前进程。 9. **系统信息**:`getpid()`、`getppid()` 获取进程ID,`gettimeofday()` 获取时间戳,`sysconf()` 获取...

    LinuxC函数详解.rar

    5. **信号处理**:通过`signal`或`sigaction`函数,开发者可以捕获和处理操作系统发送的信号,如SIGINT(中断)和SIGALRM(定时器)。 6. **网络编程**:在Linux下,C语言可以使用套接字(socket)API进行网络通信...

    C语言函数库详解(收藏版)

    ### C语言函数库详解知识点梳理 #### 第一章:C标准库 在这一章节中,我们将深入了解C语言标准库中的各个头文件及其提供的功能。这些库函数为C语言程序员提供了强大的工具箱,使得开发人员能够高效地进行各种操作...

    LinuxC函数详解

    在Linux系统中,C语言是核心编程语言,用于编写系统级程序和开发各种应用程序。LinuxC函数是C标准库在Linux环境下的实现,包含了...在学习过程中,可以参考《LinuxC函数详解.pdf》这样的资料,结合实践,逐步提升技能。

    Linux C函数详解

    Linux C函数详解PDF可能涵盖了许多关于在Linux环境下使用C语言编程的重要知识点。以下是一些可能包含在该PDF中的关键概念: 1. **C语言基础**:C语言的基本语法,包括变量声明、数据类型(如int、char、float、...

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

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

    Linux C函数详解下载[评价可免费]

    6. **信号处理**:`signal`和`sigaction`用于设置信号处理器,`raise`用于发送信号给当前进程。理解信号机制对于处理异步事件至关重要。 7. **网络通信**:Linux C编程中,`socket`、`bind`、`listen`、`accept`和`...

    linux编程详解

    【Linux编程详解】 Linux操作系统为C语言编程提供了一个强大的平台,尤其适合初学者学习和实践。C语言在Linux环境下有着广泛的应用,从系统级编程到应用程序开发,都是C语言的重要领域。 1. **源程序的编译** 在...

    linux 函数详解

    4. **信号处理**:`signal` 和 `sigaction` 函数用于处理进程接收到的信号。例如,`signal(SIGINT, handler)` 注册了一个处理中断信号的函数。 5. **内存管理**:`malloc`、`calloc`、`realloc` 和 `free` 分别用于...

    深刻理解Linux进程间通信(IPC)-详解.doc

    Linux提供了标准的`signal`函数以及更安全、更灵活的`sigaction`函数,后者支持更复杂的信号处理策略。 3. **消息队列**: - 消息队列是一种有序的消息列表,每个消息都有特定的格式。进程可以向队列中添加或删除...

    深刻理解Linux进程间通信(IPC)-详解.pdf

    sigaction()是用于处理信号的主要系统调用,它提供了比signal()更为强大的接口。在Linux的信号处理机制中,我们同样看到了BSD和System V信号处理的混合。 消息队列允许进程通过定义好的消息格式发送和接收消息。...

    嵌入式Linux应用程序开发详解的C编程基础_linux_becausek6q_

    C语言提供了sigaction()函数来注册信号处理函数,以响应特定的系统事件,如SIGINT(中断)或SIGALRM(定时器)。 九、设备驱动编程 在嵌入式Linux系统中,设备通常以字符设备或块设备的形式存在。通过编写设备驱动...

    linux下系统编程函数大全

    #### 二、信号处理函数详解 ##### 1. `alarm` **函数原型**: ```c #include unsigned int alarm(unsigned int seconds); ``` **功能**: `alarm` 函数用于设置一个 SIGALRM 信号,当经过指定的秒数后,该信号将...

Global site tag (gtag.js) - Google Analytics