signal函数

    signal函数是早起Unix系统的信号接口,早期系统中提供不可靠的信号机制。在后来的分支中,部分系统使用原来的不可靠机制定义signal函数,如 Solaris 10 。而更多的系统采用新语义 可靠信号机制,如4.4BSD。
    出于signal函数不同系统的不统一性,我们一般使用sigaction函数取代它。关于sigaction函数,我们在本文后面做详细介绍。
函数原型:
  1. #include <signal.h>
  2. void (*signal(int signo,void (*func)(int)))(int);
  3. 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)。
  1. #define SIG_ERR (void (*)())-1
  2. #define SIG_DFL (void (*)())0
  3. #define SIG_IGN (void (*)())
函数类型分析
    返回值:   返回的是一个函数指针,该指针指向的函数是类似void fun(int )型,带一个int参数,返回值void的类型。其实跟signal的第二个参数func是一样类型的。
    
    可以对func或者函数返回类型进行typedef以下:
  1. typedef void Sigfunc(int);
即使sigfunc就是一个 返回值为void,带一个int型参数的函数。

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

使用signal的一个例子:
  1. #include "apue.h"
  2. #include "myerr.h"
  3. static void sig_usr(int); /* one handler for both signals */
  4. int
  5. main(void)
  6. {
  7. if (signal(SIGUSR1, sig_usr) == SIG_ERR)
  8. err_sys("can’t catch SIGUSR1");
  9. if (signal(SIGUSR2, sig_usr) == SIG_ERR)
  10. err_sys("can’t catch SIGUSR2");
  11. for ( ; ; )
  12. pause();
  13. }
  14. static void
  15. sig_usr(int signo) /* argument is signal number */
  16. {
  17. if (signo == SIGUSR1)
  18. printf("received SIGUSR1\n");
  19. else if (signo == SIGUSR2)
  20. printf("received SIGUSR2\n");
  21. else
  22. err_dump("received signal %d\n", signo);
  23. }
  24. ~

运行结果:
  1. windeal@ubuntu:~/Windeal/apue$ ./exe &
  2. [1] 2982
  3. windeal@ubuntu:~/Windeal/apue$ kill -USR1 2982
  4. windeal@ubuntu:~/Windeal/apue$ received SIGUSR1
  5. kill -USR2 2982
  6. received SIGUSR2
  7. windeal@ubuntu:~/Windeal/apue$ kill 2982
  8. [1]+ Terminated ./exe
  9. windeal@ubuntu:~/Windeal/apue$

新程序的启动 与signal的缺陷

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

    该代码反应了一个signal的一个缺陷,就是它必须通过改变系统信号的处理方式才能获得系统当前的处理方式(好像很绕口,下面继续解释)。
    分析这段代码,首先我们明确代码的目的:判断信号当前的处理方式,如果设置被忽略,那就不做处理,如果不是处理被忽略的状态,就捕获它。根据前面后台进程的例子,shell(前台)设置了新要开启的后台进程的处理方式为忽略,那么if(signal(SIGINT,
SIG_IGN)!=
SIG_IGN) 就为假signal(SIGINT,
sig_int);就不执行。而判断句中的signal(SIGQUIT,
SIG_IGN)只是继续把信号处理方式重复设置为忽略(相当于没做处理)
   下面针对中断信号的状态做一个通俗点的解释。
  1. /* STATUS_1 */
  2. if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  3. /* STATUS_2 */
  4. signal(SIGINT, sig_int);
  5. /* 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提供了测试、改变某个信号处理方式的方法(测试和改变可以一起进行),

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

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

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


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

Example

用sigaction函数实现signal
  1. #include "apue.h"
  2. /* Reliable version of signal(), using POSIX sigaction(). */
  3. Sigfunc *
  4. signal(int signo, Sigfunc *func)
  5. {
  6. struct sigaction act, oact;
  7. act.sa_handler = func;
  8. sigemptyset(&act.sa_mask);
  9. act.sa_flags = 0;
  10. if (signo == SIGALRM) {
  11. #ifdef SA_INTERRUPT
  12. act.sa_flags |= SA_INTERRUPT;
  13. #endif
  14. }else {
  15. act.sa_flags |= SA_RESTART;
  16. }
  17. if (sigaction(signo, &act, &oact) < 0)
  18. return(SIG_ERR);
  19. return(oact.sa_handler);
  20. }








    

    

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. 【分库分表】sharding-jdbc—分片策略

    一.分片策略 Sharding-JDBC认为对于分片策略存有两种维度: 数据源分片策略(DatabaseShardingStrategy):数据被分配的目标数据源 表分片策略(TableShardin ...

  2. Linux各目录缩写含义

    Unix已经有35年历史了.许多人认为它开始于中世纪,这个中世纪是相对于计算机技术的产生和发展来说的.在过去的时间里,Unix和它的子分支Linux收集有许多的历史和一些完全古老的语言.在这篇技巧文章 ...

  3. COGS 723. [SDOI2007] 超级数组

    ★★☆   输入文件:arr.in   输出文件:arr.out   简单对比 时间限制:1 s   内存限制:3 MB  Source: SDOI2007 Day2[问题描述] 一般的数组大家都经常 ...

  4. SaltStack部署服务及配置管理apache+php-第二篇

    实验目标 1.使用SaltStack部署apache和php, 2.使用salt管理httpd.conf配置文件配置访问info.php使用账户密码 3.在salt里面增加对conf.d目录进行配置管 ...

  5. Spring Boot CRUD+分页(基于Mybatis注解方式)

    步骤一:关于Mybatis Mybatis 是用来进行数据库操作的框架.其中分页使用Mybatis中的PageHelper插件. Mybatis与hibernate对比: 1.hibernate是一个 ...

  6. intellij idea rearrange code

        reformat code的时候,无法将filed放在method前边,很恶心. 那么先去

  7. 【论文解析】MTCNN论文要点翻译

    目录 0.论文连接 1.前言 2.论文Abstract翻译 3.论文的主要贡献 4.4 训练 5 模型性能分析 5.1 关于在线挖掘困难样本的性能 5.2 将人脸检测与对齐联合的性能 5.3 人脸检测 ...

  8. centos vsftp 500 OOPS: cannot change directory:/home/ftp

    系统是CentOS,是RH派系的.我把vsftpd安装配置好了,以为大功告成,但客户端访问提示如下错误:500 OOPS: cannot change directory:/home/ftp原因是他的 ...

  9. c语言的tcp和udp客户端和服务器

    都是最简单的用来记忆. this is my 的git地址:https://github.com/yanjinyun/cLanguageTcpUdp tcp最简单的服务器: int main(int ...

  10. Spring ApplicationListener 理解

    在开发时有时候需要在整个应用开始运行时执行一些特定代码,比如初始化环境,准备测试数据.加载一些数据到内存等等. 在spring中可以通过ApplicationListener来实现相关的功能,加载完成 ...