• APUE学习笔记——10 信号


    信号的基本概念

        信号是软件中断,信号提供了解决异步时间的方法。
        每一中信号都有一个名字,信号名以SIG开头。

    产生信号的几种方式

        很多条件可以产生信号:
        终端交互:用户按下某一些按键,如ctl+c,会产生信号。
        硬件异常:如除数为0,内存引用错误。
        kill(2)函数:将信号发送到一个进程或者进程组
        kill(1)命令:该命令为kill(2)函数的接口。用于终止失控的后台in成。
        检测到某软件条件发生:如网络连接上传来外数据(产生SIGURG信号),闹钟超时(产生SIGALRM信号)

    处理信号的几种方式:

        忽略此信号:大部分信号可以忽略,但SIGKILL和SIGSTOP信号不能忽略。另,如果忽略硬件异常(如除数为0)信号,则运行结果未知。
        捕捉此信号:通知内核在某信号发生时,调用一个用户函数。SIGKILL和SIGSTOP信号不能被捕捉。如
        默认动作:执行系统默认动作,本文后面附带常见信号默认动作的表格。大部分信号的默认动作时终止进程

      SIGKILL和SIGSTOP信号既不能被忽略也不能被捕捉,因为这两个信号向内核或者超级用户提供了终止或停止信号的可靠方法。


    可靠信号与不可靠信号


    首先说明:现在大部分Unix系系统如Linux都已经实现可靠信号。
    1~31信号与SIGRTMIN-SIGRTMAX之间并不是可靠信号与不可靠信号的区别,在大多数系统下他们都是可靠信号。
    只不过:
    1~31信号                              ——不支持排队,为普通信号。(不能用于统计信号发生次数的情景。)
    SIGRTMIN-SIGRTMAX信号——支持排队,实时信号

    不可靠信号

    什么是不可靠信号:
    不可靠的意思是信号可能丢失或者被错误处理。
    在早起系统中,信号存在两大缺陷,导致了信号不可靠。

    缺陷一:

        信号发生后,信号处理方式被重置为系统默认动作。依旧是说,signal函数知识把信号和我们的信号处理函数关联一次,在发生一次信号后,信号的处理方式就被重置为系统默认了。
        这就导致了信号处理函数必须使用如下代码:
    int  sig_int(); /* my signal handling function */
    ...
    signal(SIGINT, sig_int); /* @1establish handler    */    
    ...
    sig_int()
    {
    signal(SIGINT, sig_int); /* @2reestablish handler for next time */
    ...
    ./*process the signal ... */
    ...
    }


        我们不得不在信号处理函数中再次使用signal()。
        但是,这样的处理并不能保证程序完全正确,因为在发生一次信号时,在我们开始调用sig_int函数,到执行sig_int函数中的signal函数(也就是我们@2代码)之间是有时间间隔的.如果在这段时间间隔里发生了再次发生了信号,那么针对这个信号的处理方式就是系统默认的方法了。
        所以早期的信号时不可靠的,因为他不能保证信号都使用正确的(我们期望的)处理方式进行处理。

    缺陷二:

        信号对进程的控制能力差:
        早期系统实现中,当我们不希望信号发生时,进程无法关闭一个 信号,并在记录它的发生。
        很多时候我们有这样的需求,我们不希望信号打断某项的工作,但是当工作执行完后,又希望系统告诉我们这段时间内发生了什么信号。比如我们运行一段程序,要求运行完之前不能中断它(比如我们的Ctl+C),这是就需要暂时关闭这个信号。
        首先我们明确需求,我们需要的是,信号暂时不起作用,并在之后能够提醒我们信号发生过。
        为了实现这一点,我们使用下面代码
    int  sig_int();     /* my signal handling function */
    int  sig_int_flag; /* set nonzero when signal occurs */
    main()
    {
        signal(SIGINT, sig_int); /* establish handler */
        ...
        while (sig_int_flag == 0)
            pause();  /* go to sleep, waiting for signal */
        ...
    }
    sig_int()
    {
        signal(SIGINT, sig_int); /* reestablish handler for next time */
        sig_int_flag = 1; /* set flag for main loop to examine */
    }


        sig_int只有两行代码,它的作用就是忽略信号,并且用sig_int_flag标志信号发生过。
        之所以用while只因为pause可能会被其他信号中断。(我的理解)
        
        在这段代码中仍然有缺陷,在while测试后,pause之前有一段时间间隔, 在这段时间间隔里如果信号发生,并且此后不再发生,则进程会一直进入睡眠状态。

    可靠信号:

        可靠信号针对解决不可靠信号的两点缺陷来理解:

    解决不可靠信号缺陷一

        用sigaction代替signal
    (在现代大多数系统中,signal也使用了sigaction实现,因此事可靠的。)建议尽可能使用sigaction替代signal,因为sigaction是同一的标准,可移植性强。
         一旦给信号被sigaction安装了一个动作,那么在sigaction调用显示改变它之前,这个信号动作将一直有效,也就是说不会执行一次就回复默认动作。这种处理方式不同于早期系统的信号不可靠的信号处理方式。可以说现在系统的信号处理方式是可靠的
        关于sigaction与signal的更多介绍可参考本博相关博文。

    解决不可靠信号缺陷二:

        用户可以通过sigprocmask、sigaction设置屏蔽字是信号阻塞。使信号处于pending状态,这样也就自然解决了缺陷二。
            

    信号列表:

        下面附上APUE Figure 10.1中信号列表的表格:
        在SUS列中,•表示该信号为POSIX.1的基础规范部分,XSI表示该信号为XSI拓展选项。
        在Default action列中,terminate+core表示进程存储映像被存放在当前目录的一个我们称之为core的文件中。core文件可以用于调试。关于不产生core文件的条件请参考http://blog.csdn.net/windeal3203/article/details/39289059



    附上每一个信号的具体介绍:来源: <http://baike.baidu.com/view/64630.htm?fr=aladdin>
    Signal
    Description
    SIGABRT
    由调用abort函数产生,进程非正常退出
    SIGALRM
    用alarm函数设置的timer超时或setitimer函数设置的interval timer超时
    SIGBUS
    某种特定的硬件异常,通常由内存访问引起
    SIGCANCEL
    由Solaris Thread Library内部使用,通常不会使用
    SIGCHLD
    进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略
    SIGCONT
    当被stop的进程恢复运行的时候,自动发送
    SIGEMT
    和实现相关的硬件异常
    SIGFPE
    数学相关的异常,如被0除,浮点溢出,等等
    SIGFREEZE
    Solaris专用,Hiberate或者Suspended时候发送
    SIGHUP
    发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送
    SIGILL
    非法指令异常
    SIGINFO
    BSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground Group的进程
    SIGINT
    由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程
    SIGIO
    异步IO事件
    SIGIOT
    实现相关的硬件异常,一般对应SIGABRT
    SIGKILL
    无法处理和忽略。中止某个进程
    SIGLWP
    由Solaris Thread Libray内部使用
    SIGPIPE
    在reader中止之后写Pipe的时候发送
    SIGPOLL
    当某个事件发送给Pollable Device的时候发送
    SIGPROF
    Setitimer指定的Profiling Interval Timer所产生
    SIGPWR
    和系统相关。和UPS相关。
    SIGQUIT
    输入Quit Key的时候(CTRL+)发送给所有Foreground Group的进程
    SIGSEGV
    非法内存访问
    SIGSTKFLT
    Linux专用,数学协处理器的栈异常
    SIGSTOP
    中止进程。无法处理和忽略。
    SIGSYS
    非法系统调用
    SIGTERM
    请求中止进程,kill命令缺省发送
    SIGTHAW
    Solaris专用,从Suspend恢复时候发送
    SIGTRAP
    实现相关的硬件异常。一般是调试异常
    SIGTSTP
    Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程
    SIGTTIN
    当Background Group的进程尝试读取Terminal的时候发送
    SIGTTOU
    当Background Group的进程尝试写Terminal的时候发送
    SIGURG
    当out-of-band data接收的时候可能发送
    SIGUSR1
    用户自定义signal 1
    SIGUSR2
    用户自定义signal 2
    SIGVTALRM
    setitimer函数设置的Virtual Interval Timer超时的时候
    SIGWAITING
    Solaris Thread Library内部实现专用
    SIGWINCH
    当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程
    SIGXCPU
    当CPU时间限制超时的时候
    SIGXFSZ
    进程超过文件大小限制
    SIGXRES
    Solaris专用,进程超过资源限制的时候发送
     















  • 相关阅读:
    pexpect库学习之包装类详解
    spawn类expect方法详解
    spawn类参数command详解
    Django中如何实现数据库路由?
    Memcached中的存取命令详解
    Javascript的参数详解
    Python中Paramiko协程方式详解
    Greenlets间如何实现互相通信?
    Gevent中的同步与异步详解
    jQuery正则的使用方法步骤详解
  • 原文地址:https://www.cnblogs.com/Windeal/p/4284656.html
Copyright © 2020-2023  润新知