本章先对信号机制进行综述,并说明每种信号的一般用法。

信号概念

每个信号都有一个名字,这些名字都以3个字符SIG开头。在头文件<signal.h>中,信号名都被定义为正整形常量。

在某个信号出现时,可以按下列3种方式之一进行处理:

1 忽略该信号。大多数信号都可以使用这种方式进行处理,但有两种信号却决不能被忽略:SIGKILL和SIGSTOP(只能执行系统默认动作)。

2 捕获信号。通知内核在某信号发生时,调用一个用户函数对这种时间进行处理。

3 执行系统默认动作。对于大多数信号的系统默认动作是终止该进程。

信号类型

程序错误类信号:默认动作使进程流产,产生core文件。

SIGABRT:   调用abort函数生成的信号。

SIGFPE:      浮点计算错误。

SIGILL:      非法指令错误。

SIGBUS/SIGSEGV: 硬件错误-非法地址访问。

SIGEMT:    硬件错误

SIGSYS:       非法系统调用。

SIGTRAP:   硬件错误(通常为断点指令)。

程序终止类信号:默认动作使进程终止,我们通常要处理这类信号,做一些清理工作,句柄函数应在结束时为此信号指定默认动作,然后再次生成该信号,使得程序终止。

SIGHUP:终端断开连接时,生成此信号给控制进程。

SIGINT:Ctrl-C或Delete按下时,由终端驱动生成,并发送给前台进程组中的所有进程。

SIGKILL:使程序立即终止,不能被捕获或忽略,也不能被阻塞。

SIGQUIT:Ctrl-\,如SIGINT,并且产生core。

SIGTERM:该信号使程序终止,但是可以阻塞、捕获、忽略。

闹钟类信号:通知定时器到期,默认动作是终止程序,但通常会设置句柄。

SIGALRM:alarm/setitimer函数设置定时到期后,会产生此信号。

SIGPROF:

SIGVTALRM:

I/O类信号:通知进程在描述字上发生了感兴趣事件,支持信号驱动IO。

SIGIO: fd准备执行输入输出时发送此信号。

SIGPOLL:异步I/O信号。

SIGURG:网络收到带外数据时可选择生成此信号。

作业控制类信号:

SIGCHLD:   进程终止或停止时会向其父进程发送该信号,默认动作为忽略。

SIGCONT:     使停止的进程恢复运行。

SIGSTOP:      停止进程。

SIGTSTP/SIGTTIN/SIGTTOU:

操作错误类信号:默认动作终止程序。

SIGPIPE:    管道破裂。

SIGXCPU/SIGXFSZ:

函数signal

UNIX系统信号机制最简单的接口是signal函数

#include <signal.h>
void (*signal(int signo,void (*func)(int)))(int);

可以使用typedef使其变得简单一点

typedef void Sigfunc(int);
Sigfunc *signal(int,Sigfunc *);

第一个int参数是要捕获的信号(整形常量),第二个参数是一个函数指针(处理函数),该函数指针指向的函数返回值是void,参数是int。

下面给出一个简单得信号处理程序:

 #include "apue.h"

 static void    sig_usr(int);    /* one handler for both signals */

 int
main(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR1");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR2");
for ( ; ; )
pause();
} static void
sig_usr(int signo) /* argument is signal number */
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
err_dump("received signal %d\n", signo);
}

我们使该程序在后台运行,而且用kill命令将信号发送给它:

中断的系统调用

早期UNIX系统的一个特性是:如果进程在执行一个低速系统调用而阻塞期间捕获到一个信号,则该系统调用就被中断不再继续执行。

该系统调用返回出错,其errno设置为EINTR。后面的章节会更多的涉及到被中断的系统调用。

可重入函数

进程捕获到信号并对其进行处理时,进程正在执行的正常指令序列就被信号处理程序临时中断,它首先执行该信号处理程序中的指令。

如果从信号处理程序返回,则继续执行在捕获到信号时进程正在执行的正常指令序列。

在信号处理函数中调用某些函数可能对导致安全问题(其结果是不可预知的),下面列出了这些异步信号安全的函数,没有列入图中的大多数函数是不可重入的。

函数kill和raise

kill函数将信号发送给进程或进程组,raise函数则允许进程向自身发送信号。

#include <signal.h>
int kill(pid_t pid,int signo);
int raise(int signo);

函数alarm和pause

函数alarm设置一个定时器,当定时器超时时,产生SIGALRM信号。

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

pause函数使调用进程挂起直至捕捉到一个信号

#include <signal.h>
int pause(void);

只有执行了一个信号处理程序并从其返回时,pause才返回。此时,pause返回-1,errno设置为EINTR。

信号集

信号集是能表示多个信号的数据结构(sigset_t),下面列出5个处理信号集的函数

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set,int signo);
int sigdelset(sigset_t *set,int signo);
int siggismember(const sigset_t *set,int signo);

在使用信号集之前,要对该信号集进行初始化(调用sigemptyset或者sigfillset)。

函数sigprocmask

进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集。调用函数sigprocmask可以检测或更改进程的信号屏蔽字。

#include <signal.h>
int sigprocmask(int how,const sigset_t *restrict set,sigset_t *restrict oset);

若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。

若set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。SIG_BLOCK是或操作,SIG_SETMASK则是赋值操作

函数sigpending

sigpending函数返回一信号集,对于调用进程而言,其中的各信号是阻塞不能递送的,因而也一定是当前未决的。

#include <signal.h>
ing sigpending(sigset_t *set);

下面展示信号设置和sigprocmask实例

 #include "apue.h"

 static void    sig_quit(int);

 int
main(void)
{
sigset_t newmask, oldmask, pendmask; if (signal(SIGQUIT, sig_quit) == SIG_ERR)
err_sys("can't catch SIGQUIT"); /*
* Block SIGQUIT and save current signal mask.
*/
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT);
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < )
err_sys("SIG_BLOCK error"); sleep(); /* SIGQUIT here will remain pending */ if (sigpending(&pendmask) < )
err_sys("sigpending error");
if (sigismember(&pendmask, SIGQUIT))
printf("\nSIGQUIT pending\n"); /*
* Restore signal mask which unblocks SIGQUIT.
*/
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < )
err_sys("SIG_SETMASK error");
printf("SIGQUIT unblocked\n"); sleep(); /* SIGQUIT here will terminate with core file */
exit();
} static void
sig_quit(int signo)
{
printf("caught SIGQUIT\n");
if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
err_sys("can't reset SIGQUIT");
}

进程开始阻塞SIGQUIT信号,保存了当前信号屏蔽字(以便以后恢复),然后休眠5秒。在此期间所产生的退出信号SIGQUIT都被阻塞,不递送至该进程。

5秒休眠后,检查该信号是否是未决的,然后将SIGQUIT设置为不再阻塞。

运行程序,在5s之内键入退出字符Ctril+\(产生SIGQUIT信号),然后在第二个5s之内再次键入退出字符。

函数sigaction

sigaction函数的功能是检查或修改与制定信号相关联的处理动作。此函数取代了UNIX早期版本使用的signal函数。

#include <signal.h>
int sigaction(int signo,const struct sigction *restrict act,struct sigaction *restrict oact);

参数signo是要检测或修改其具体动作的信号编号。若act指针非空,则根据参数act修改其动作。若oact指针非空,则由oact指针返回该信号的上一个动作。

此函数使用下列结构:

struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
};

sa_handler字段包含一个信号捕捉函数的地址。

sa_mask字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加到进程的信号屏蔽字中。仅当从信号捕捉函数返回时将进程的信号屏蔽字恢复为原先值。

sa_flags字段指定对信号进行处理的各个选项。

sa_sigaction字段是一个替代的信号处理程序,当sa_flags设置为SA_SIGINFO时,使用该信号处理程序。

通常按下列方式调用信号处理程序:

void handler(int signo);

在设置了SA_SIGINFO标志,那么按下列凡是调用信号处理程序:

void handler(int signo,siginfo_t *info,void *context);

下面使用sigaction实现signal函数,它力图阻止被中断的系统调用重启动

typedef void Sigfunc(int);
Sigfunc* mysignal(int signo,Sigfunc *func)
{ struct sigaction act,oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = ;
if(signo == SIGALRM)
{
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
}
else
{
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART;
#endif
}
if(sigaction(signo,&act,&oact)<)
return (SIG_ERR);
return (oact.sa_handler);
}

函数sigsetjmp和siglongjmp

之前说明了setjmp和longjmp函数可以用户非局部转移,sigsetjmp跟siglongjmp指定了对信号屏蔽字的作用。

在信号处理程序中进行非局部转移时应当使用这两个函数。

#include <setjmp.h>
int sigsetjmp(sigjmp_buf env,int savemask);
void siglongjmp(sigjmp_buf env,int val);

与setjmp和longjmp函数唯一的区别是sigsetjmp增加了一个参数savemask。

如果savemask非0,则sigsetjmp在env中保存在env中保存进程的当前信号屏蔽字。调用siglongjmp时,从已经保存的env中恢复保存的信号屏蔽字。

函数sigsuspend

sigsuspend用于在接收到某个信号之前,临时用sigmask替换进程的信号屏蔽字,并暂停进程执行,直到捕捉到一个信号而且从该信号处理程序返回,并且进程的信号屏蔽字设置为调用sigsuspend之前的值。

#include <signal.h>
int sigsuspend(const sigset_t *sigmask);

下面显示了保护代码临界区,使其不被特定信号中断的正确方法

 #include "apue.h"

 static void    sig_int(int);

 int
main(void)
{
sigset_t newmask, oldmask, waitmask; pr_mask("program start: "); if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
sigemptyset(&waitmask);
sigaddset(&waitmask, SIGUSR1);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT); /*
* Block SIGINT and save current signal mask.
*/
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < )
err_sys("SIG_BLOCK error"); /*
* 代码临界区
*/
pr_mask("in critical region: "); /*
* Pause, allowing all signals except SIGUSR1.
*/
if (sigsuspend(&waitmask) != -)
err_sys("sigsuspend error"); pr_mask("after return from sigsuspend: "); /*
* Reset signal mask which unblocks SIGINT.
*/
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < )
err_sys("SIG_SETMASK error"); /*
* And continue processing ...
*/
pr_mask("program exit: "); exit();
} static void
sig_int(int signo)
{
pr_mask("\nin sig_int: ");
}

下面是程序运行结果:

函数abort

abort函数的功能是使程序异常终止

#include <stdlib.h>
void abort(void);

此函数将SIGABRT信号发送给调用进程。让进程捕捉SIGABRT信号目的是在进程终止之前由其执行所需的清理操作。默认情况是终止调用进程。

函数system

POSIX.1要求system函数忽略SIGINT和SITQUIT信号,阻塞SIGCHLD。

函数sleep

此函数使调用进程被挂起,直到满足下列条件之一:

(1)已经经过seconds所指定的墙上时钟时间。

(2)调用进程捕捉到一个信号并从信号处理程序返回。

apue学习笔记(第十章 信号)的更多相关文章

  1. APUE学习笔记——10.9 信号发送函数kill、 raise、alarm、pause

    转载注明出处:Windeal学习笔记 kil和raise kill()用来向进程或进程组发送信号 raise()用来向自身进程发送信号. #include <signal.h> int k ...

  2. APUE学习笔记——10.可靠信号与不可靠信号

    首先说明:现在大部分Unix系系统如Linux都已经实现可靠信号. 1~31信号与SIGRTMIN-SIGRTMAX之间并不是可靠信号与不可靠信号的区别,在大多数系统下他们都是可靠信号. 只不过: 1 ...

  3. APUE学习笔记3_文件IO

    APUE学习笔记3_文件IO Unix中的文件IO函数主要包括以下几个:open().read().write().lseek().close()等.这类I/O函数也被称为不带缓冲的I/O,标准I/O ...

  4. 交换机安全学习笔记 第九~十章 HSRP VRRP

    HSRP  (Hot Standby Router Protocol) 热备份路由器协议 思科私有 HSRP消息使用UDP 端口号 1985(IPv6时为2029) 使用多播地址 224.0.0.2( ...

  5. APUE学习笔记——10.11~10.13 信号集、信号屏蔽字、未决信号

    如有转载,请注明出处:Windeal专栏 首先简述下几个概念的关系: 我们通过信号集建立信号屏蔽字,使得信号发生阻塞,被阻塞的信号即未决信号. 信号集: 信号集:其实就是一系列的信号.用sigset_ ...

  6. APUE学习笔记——10 信号

    信号的基本概念     信号是软件中断,信号提供了解决异步时间的方法.     每一中信号都有一个名字,信号名以SIG开头. 产生信号的几种方式     很多条件可以产生信号:     终端交互:用户 ...

  7. APUE学习笔记——10信号——信号接口函数 signal 和 sigaction

    signal函数     signal函数是早起Unix系统的信号接口,早期系统中提供不可靠的信号机制.在后来的分支中,部分系统使用原来的不可靠机制定义signal函数,如 Solaris 10 .而 ...

  8. APUE 学习笔记(七) 信号

    1.信号是软件中断,提供一种异步处理事件的方法 很多事件产生信号: (1)用户按下某些中断键,如 Ctrl + C键产生 SIGINT信号 (2)硬件异常产生信号,比如 除数为0,无效的内存引用  ( ...

  9. APUE学习笔记5——信号、信号集和进程信号屏蔽字

    1 信号传递过程 当引发信号的事件发生时(如软硬件异常.软件定时.终端产生信号或调用kill函数等等),会产生信号,内核会发送给目标进程. 在信号产生到信号传递给目标进程之间的时间间隔内,称该信号为未 ...

随机推荐

  1. 【bzoj2096】[Poi2010]Pilots 双指针法+STL-set

    题目描述 Tz又耍畸形了!!他要当飞行员,他拿到了一个飞行员测试难度序列,他设定了一个难度差的最大值,在序列中他想找到一个最长的子串,任意两个难度差不会超过他设定的最大值.耍畸形一个人是不行的,于是他 ...

  2. BZOJ 4826 [Hnoi2017]影魔 ——扫描线 单调栈

    首先用单调栈和扫描线处理出每一个数左面最近的比他大的数在$l[i]$,右面最近的比他大的数$r[i]$. 然后就可以考虑每种贡献是在什么时候产生的. 1.$(l[i],r[i])$产生$p1$的贡献 ...

  3. KM算法【带权二分图完美匹配】

    先orz litble--KM算法 为什么要用KM算法 因为有的题丧心病狂卡费用流 KM算法相比于费用流来说,具有更高的效率. 算法流程 我们给每一个点设一个期望值[可行顶标] 对于左边的点来说,就是 ...

  4. BZOJ3295 [Cqoi2011]动态逆序对 【CDQ分治】

    题目 对于序列A,它的逆序对数定义为满足i 输入格式 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为 ...

  5. 上传相同文件名的时候不能触发change事件的解决方案

    方法一 在上传完文件之后,将<input type="file" /> 的值置为null,即可. 方法二 在上传完文件之后替换dom 方法三 在上传完文件之后刷新页面

  6. MYSQL 存储过程 多表更新异常捕捉和异常处理方式

    今天在做MYSQL 存储过程 多表更新的功能   多表更新时候注意事项 1.首先是确保多表更新能够一次执行,途中没有哪个表的sql语句错误 2.上线后修改表结构及字段,请注意检查是否影响mysql 过 ...

  7. POJ1195Mobile phones

    二维树状数组板子题. #include<cstdio> #include<cstring> #include<iostream> #include<cstdl ...

  8. NOI2001食物链

    描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A吃B,B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人 ...

  9. 多表的时候怎样在MVC VIEW中显示

    原文发布时间为:2011-04-01 -- 来源于本人的百度文章 [由搬家工具导入] Linq join query displayed in MVC view Instead of returnin ...

  10. [LeetCode] Surrounded Regions 广度搜索

    Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. A region is captured ...