signal函数

    signal函数是早起Unix系统的信号接口,早期系统中提供不可靠的信号机制。在后来的分支中,部分系统使用原来的不可靠机制定义signal函数,如 Solaris 10 。而更多的系统采用新语义 可靠信号机制,如4.4BSD。
    出于signal函数不同系统的不统一性,我们一般使用sigaction函数取代它。关于sigaction函数,我们在本文后面做详细介绍。
函数原型:
#include <signal.h>
void (*signal(int signo,void (*func)(int)))(int);
Returns: previous disposition of signal (see following) if OK,SIG_ERR on error
参数:
    signo:信号的名字。
    func:可以看出是一个函数指针,用于指定针对信号的处理方式。
    
    信号有三种处理方式,1)忽略,此时func赋值为SIG_IGN; 2)使用默认动作,此时func赋值为SIG_DFL; 3)自定义动作,此时func赋值为我们自定义函数的函数指针,会调用到信号处理程序(signal handler)或信号捕捉函数(signal-catching
function)。
#define SIG_ERR (void (*)())-1
#define SIG_DFL (void (*)())0
#define SIG_IGN (void (*)())
函数类型分析
    返回值:   返回的是一个函数指针,该指针指向的函数是类似void fun(int )型,带一个int参数,返回值void的类型。其实跟signal的第二个参数func是一样类型的。
    
    可以对func或者函数返回类型进行typedef以下:
typedef void Sigfunc(int);
即使sigfunc就是一个 返回值为void,带一个int型参数的函数。

signal函数可以变形为:
Sigfunc *signal(int, Sigfunc *)

使用signal的一个例子:
#include "apue.h"
#include "myerr.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);
}
~

运行结果:
windeal@ubuntu:~/Windeal/apue$ ./exe &
[1] 2982
windeal@ubuntu:~/Windeal/apue$ kill -USR1 2982
windeal@ubuntu:~/Windeal/apue$ received SIGUSR1
kill -USR2 2982
received SIGUSR2
windeal@ubuntu:~/Windeal/apue$ kill 2982
[1]+ Terminated ./exe
windeal@ubuntu:~/Windeal/apue$

新程序的启动 与signal的缺陷

    来看下,运行一个程序时,信号的状态。
    使用fork创建子进程时,子进程会继承父进程的信号状态。Note:用户定义的信号捕捉函数的的地址在子进程时有效的。
    使用exec运行新程序时,exec调用者要捕捉的信号,在子进程中会被置为默认动作(因为调用者定义的信号捕捉函数的地址在新程序中已经失效了),而其他信号不变(设置忽略的信号,新进程也会忽略)。
    
Example:
    shell启动一个后台进程,会设置该后台进程自动屏蔽掉中断和退出信号。这样前台退出时,后台进程才能继续进行。很多捕捉这两个信号的交互式程序使用下列形式代码:
void sig_int(int), sig_quit(int);
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, sig_int);
if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
signal(SIGQUIT, sig_quit);

    该代码反应了一个signal的一个缺陷,就是它必须通过改变系统信号的处理方式才能获得系统当前的处理方式(好像很绕口,下面继续解释)。
    分析这段代码,首先我们明确代码的目的:判断信号当前的处理方式,如果设置被忽略,那就不做处理,如果不是处理被忽略的状态,就捕获它。根据前面后台进程的例子,shell(前台)设置了新要开启的后台进程的处理方式为忽略,那么if(signal(SIGINT,
SIG_IGN)!=
SIG_IGN) 就为假signal(SIGINT,
sig_int);就不执行。而判断句中的signal(SIGQUIT,
SIG_IGN)只是继续把信号处理方式重复设置为忽略(相当于没做处理)
   下面针对中断信号的状态做一个通俗点的解释。
        /* STATUS_1 */
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
/* STATUS_2 */
signal(SIGINT, sig_int);
/* STATUS_3 */
        假设在STATUS_1信号处理方式为忽略,那么在进程执行了signal(SIGINT,
SIG_IGN),然后进入STATUS_3,仍然为忽略状态。
    假设在STATUS_1中信号处理方式不为忽略,则signal(SIGINT,
SIG_IGN) 将信号处理方式也设为忽略(也就是说进入STATUS_2时,STATUS_2中信号的处理方式为忽略),但是
signal(SIGINT,
sig_int)又将信号处理方式设置为捕获,且捕获函数为sig_int。因此STATUS_3时,信号的处理方式为sig_int捕获。

    signal缺陷:从上面的代码可以看出,signal需要通过改变信号的处理方式(上面的例子是一直讲信号处理方式设置为SIG_IGN)来获得当前信号处理方式; 简单地说就是它没有提供测试当前信号处理方式的功能,而需要在用户代码中来实现。sigaction提供了可以确定信号处理方式的方法,下面将给予介绍。

sigaction函数

    sigaction提供了测试、改变某个信号处理方式的方法(测试和改变可以一起进行),

函数原型:
#include <signal.h>
int sigaction(int signo,const struct sigaction *restrict act,struct sigaction *restrict oact);
Returns: 0 if OK,−1 on error
    signo:信号处理方式
    act:为空表示不改变信号动作,非空表示改变信号动作(处理方式)
    oact:非空时,此函数执行前信号的响应动作(处理方式)
结构体struct sigaction
struct sigaction {
void (*sa_handler)(int); /* addr of signal handler, */
/* or SIG_IGN, or SIG_DFL */
sigset_t sa_mask; /* additional signals to block */
int sa_flags; /* signal options, Figure 10.16 */
/* alternate handler */
void (*sa_sigaction)(int, siginfo_t *, void *);
};
    当更改信号动作时,act->sa_handler包含信号处理函数的地址指针(与SIG_IGN or SIG_DFL相对).act->sa_mask说明一个信号集,在调用信号捕获函数前,该信号集加入到进程的信号屏蔽字中(sa_mask只是起一个说明的作用,在sigaction函数中,没有真正的操作,真正的关于屏蔽自的操作,在sigaction之前就应该先完成)。当信号捕获函数返回时,进程的信号屏蔽字复位。
这样,调用信号处理函数时,就能阻塞信号。如下所示:
   //设置新的信号屏蔽字, 阻塞信号
sigaction(signo, act, oact);//调用sigaction函数
//复位信号屏蔽字
从上面说明可以看出,在调用信号处理函数时,操作系统建立的新屏蔽字阻塞该信号(包括正在被传送的信号)。这也保证了在处理信号过程中,如果信号在此发生,也会被阻塞。但是,大多数实现没有信号阻塞排队功能。也就是说,在处理信号时,不管信号在发生几次,均被阻塞,但是信号处理函数只会被调用一次。

    一旦给信号被安装了一个动作,那么在sigaction调用显示改变它之前,这个信号动作将一直有效。这种处理方式不同于早期系统的信号不可靠的信号处理方式。可以说现在系统的信号处理方式是可靠的。

其它参数:
    act->sa_flags指定了信号处理的一些选项。各个选项如下所示:


    act->sa_sigaction是act->sa_handler的一个可替代的方案。如果SA_SIGINFO选项被设置,则使用
 sa_sigaction(int signo, siginfo_t* info,void *context);
否则默认使用:
sa_handler(signo);
siginfo_t包含了信号产生原因的相关信息。
struct siginfo {
int si_signo; /* signal number */
int si_errno; /* if nonzero, errno value from errno.h */
int si_code; /* additional info (depends on signal) */
pid_t si_pid; /* sending process ID */
uid_t si_uid; /* sending process real user ID */
void *si_addr; /* address that caused the fault */
int si_status; /* exit value or signal number */
union sigval si_value; /* application-specific value */
/* possibly other fields also */
};
union sigval:
union sigval{
int sival_int;
void *sival_ptr;
}

Example

用sigaction函数实现signal
#include "apue.h"
/* Reliable version of signal(), using POSIX sigaction(). */
Sigfunc *
signal(int signo, Sigfunc *func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signo == SIGALRM) {
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
}else {
act.sa_flags |= SA_RESTART;
}
if (sigaction(signo, &act, &oact) < 0)
return(SIG_ERR);
return(oact.sa_handler);
}








    

    

APUE学习笔记——10信号——信号接口函数 signal 和 sigaction的更多相关文章

  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学习笔记——10.11~10.13 信号集、信号屏蔽字、未决信号

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

  4. APUE学习笔记——10.18 system函数 与waitpid

    system函数 system函数用方便在一个进程中执行命令行(一行shell命令). 用法如下: #include <stdio.h> #include <stdlib.h> ...

  5. APUE学习笔记——10.15 sigsetjmp和siglongjmp

    转载自:sigsetjmp使用方法 如侵犯您的权益,请联系:windeal12@qq.com sigsetjmp使用方法 分类: c/c++ linux2012-02-03 12:33 1252人阅读 ...

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

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

  7. 《C++ Primer Plus》学习笔记10

    <C++ Primer Plus>学习笔记10 <<<<<<<<<<<<<<<<<&l ...

  8. Spring 源码学习笔记10——Spring AOP

    Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...

  9. thinkphp学习笔记10—看不懂的路由规则

    原文:thinkphp学习笔记10-看不懂的路由规则 路由这部分貌似在实际工作中没有怎么设计过,只是在用默认的设置,在手册里面看到部分,艰涩难懂. 1.路由定义 要使用路由功能需要支持PATH_INF ...

随机推荐

  1. C++中去掉string字符串中的\r\n等

    string imagedata;imagedata = “dudau\r\ndadafca\r\n” CString Image; Image = imagedata.c_str(); Image. ...

  2. saltstack之keepalived的安装配置

    使用saltstack编译安装keepalived: 创建相应的目录,并在目录下创建相应的sls配置文件 [root@node1 ~]# mkdir /srv/salt/prod/keepalived ...

  3. 20145216史婧瑶《Java程序设计》第三次实验报告

    实验三 敏捷开发与XP实践 实验内容 使用git上传代码,两个人进行小组合作,队友下载代码并修改再重新上传. 实验步骤 一. 使用git上传代码 1.找到需要push的文件所在文件夹,右键点击Git ...

  4. LeetCode——Arithmetic Slices

    Question A sequence of number is called arithmetic if it consists of at least three elements and if ...

  5. PHP实体层基础类

    PHP实体层基础类 class BaseModel { private $tableName; private $fields=array(); function __construct() { $t ...

  6. angularjs控制器之间的数据共享与通信

    1.可以写一个service服务,从而达到数据和代码的共享; var app=angular.module('app',[]); app.service('ObjectService', [Objec ...

  7. 个人待办事项工具的设计和搭建(IFE前端2015春季 任务3)

    这是我几个月之前的项目作品,花了相当的时间去完善.博客人气不高,但拿代码的人不少,所以一直处于保密状态.没有公开代码.但如果对你有帮助,并能提出指导意见的,我将十分感谢. IFE前端2015春季 任务 ...

  8. 获得Python脚本所在目录

    如何获得Python脚本所在目录的位置   On this page... (hide) 1.  以前的方法 2.  正确的方法 3.  实例说明   (Edit) 1.  以前的方法 如果是要获得程 ...

  9. LeetCode 380. Insert Delete GetRandom O(1)

    380. Insert Delete GetRandom O(1) Add to List Description Submission Solutions Total Accepted: 21771 ...

  10. C++调用Python脚本中的函数

    1.环境配置 安装完python后,把python的include和lib拷贝到自己的工程目录下 然后在工程中包括进去 2.例子 先写一个python的测试脚本,如下 这个脚本里面定义了两个函数Hel ...