POSIX 实时信号

本文深入解析了可靠信号机制的工作原理,包括信号的触发、发送、处理流程,以及如何通过信号集合进行操作。介绍了如何为进程指定信号处理函数,并探讨了信号在多线程环境中的行为。此外,还详细说明了各种信号相关的系统调用及其使用方法。

有些乱。。。凑合看把

0. 可靠信号机制原理:
    0.0 当一个信号的出现时, 我们说信号"触发"了.
    0.1 我们可以为一个进程指定某个信号相应的 handler.
        有三种特殊的handler : SIG_DFL SIG_IGN SIG_ERR 分别代表: 默认, 忽略, 报错 这三种handler    
    0.2 当进程收到信号并执行完相应 handler 之后意味着信号已经"发送完毕".
    0.3 信号处于"触发"后到"发送完毕"之前的时期, 叫做pending .
    0.4 对于某个信号,我们可以进行 block 和 unblock 操作, 注意这跟忽略该信号是不一样的.
    0.5 在进程 block 某个信号之后, 该进程的这个信号可以由进程自身或其他进程"触发"一次或多次, 在实时信号被触发多次的情况下, 这些实时信号在内核维护下形成一个多级优先队列按先后顺序转发至接受进程:
             0.5.1 按照信号的 signal number 大小排序:
                     小的优先级高排前面, 大的优先级低排后面.
             0.5.2 其次按照信号产生的时间先后对具有相同 signal number 的多个不同信号排序:
                     先产生的在前面, 后产生的在后面.
             0.5.3 但同一时间某一进程既有传统信号又有实时信号时, 他们由内核发送的顺序是未定的.   
    0.6 一个信号一旦产生, 除非目标进程终止或退出之前一直被block, 否则迟早被接受.不存在传统 UNIX 信号系统的不可靠的缺点.
    0.7 可以通过调用 sigqueue() 触发信号, 同时还可以在首发进程之间传递一个整型数或一个指针
    0.8 注意信号 handler 可以中断系统调用和库函数调用, 而且可以在是内核态中断.  

 

1. 数据结构: 
    1.1 信号集合: bit vector, 每一个位对应一个信号是否 enable
        # define _SIGSET_NWORDS        (1024 / (8 * sizeof (unsigned long int)))
        typedef struct
          {
            unsigned long int __val[_SIGSET_NWORDS];
          } __sigset_t;
        
          typedef __sigset_t sigset_t;

    1.2 信号集合操作:

       #include <signal.h>
        sigset_t set;
       int sigemptyset(sigset_t *set);
       int sigfillset(sigset_t *set);
       int sigaddset(sigset_t *set, int signum);
       int sigdelset(sigset_t *set, int signum);
       int sigismember(const sigset_t *set, int signum);
        返回值: 
               除sigismember外:
                       成功: 0 
                       出错: -1
               sigismember:
                        是 : 1  
                        否 : 0
                       出错: -1        
       1.2.1 sigset_t 类型应该在初次使用之前调用sigemptyset() 或 sigfillset() 先初始化.

    1.3 操作进程当前使用信号集合 ( 进程忽略在 mask 集合中的信号 )
       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
            how: {SIG_BLOCK,  SIG_UNBLOCK,  SIG_SETMASK}
       
       返回值: 
                        成功: 0 
                       出错: -1              
                       
    1.4 为特定信号绑定 handler 
       int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
       返回值: 
                        成功: 0 
                         出错: -1                                   
        1.4.1 数据结构:
               struct sigaction {
               void     (*sa_handler)(int);            
               void     (*sa_sigaction)(int , siginfo_t *, void *); 
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

        注意:
            1.4.1.0 signum 为除SIGKILL和SIGSTOP外的所有信号.
            1.4.1.1 不可同时指定元素sa_handler 和sa_sigaction(某些架构机器上, 这俩者属于一个union ).
            1.4.1.2 sa_handler 可为宏 SIG_DFL:绑定该信号的系统默认handler, SIG_IGN: 忽落该信号.
            1.4.1.3 sa_restorer 现已过时, 不应再使用. POSIX 中并未定义此元素.
            1.4.1.4 可通过第2个参数为NULL来查询当前使用的 handler.
            1.4.1.5 可通过指定第2和第3个参数为NULL来校验指定的信号在该机器上是否可以使用. 
            1.4.1.6 sa_mask 指定在handler处理信号期间,需要block的信号集合.此外触发该handler 的
            信号将在handler处理信号期间被屏蔽, 除非在sa_flags中使用了SA_NODEFER 标志.
                         注意不可屏蔽 
            1.4.1.7 当在元素sa_flags中使用了 SA_SIGINFO 时, 注册 sa_sigaction 为信号处理 handler, 
                       而不是 sa_handler
                            sa_sigaction 使用三个参数:
                                     第1个参数,整型, 该信号的整数值;
                                     第2个参数, siginfo_t 指针类型, ;
                                    第3个参数,ucontext_t(转换为void 指针)类型.
                            注意: siginfo_t 包含许多可能的属性,需要针对不同的信号选取对其有意义的属性. 
                            属性如下:        
                            siginfo_t {
                                                  /* 对于所有类型信号: */
                                               int      si_signo;    /* Signal number */   
                                               int      si_errno;    /* An errno value ( Linux 中通常未使用此属性 )*/   
                                               int      si_code;     /*  Signal 的整型值, 而非 bit mask , 表示发生信号的原因 */
                                                                                      /*见 1.4.1.8 描述可能的取值, 以及相应的原因   */
                                                                                      
                                           union /* 针对不同的信号产生方式, 传递不同信息给 sa_sigaction */
                                               {
                                                        int _pad[__SI_PAD_SIZE];
                                                
                                                /* 进程调用 kill() 发送.  */
                                                        struct
                                                          {
                                                            __pid_t si_pid;        /* Sending process ID.  */
                                                            __uid_t si_uid;        /* Real user ID of sending process.  */
                                                          } _kill;
                                                
                                                /*计时器 timers (POSIX.1b) .  */
                                                        struct
                                                          {
                                                            int si_tid;                /* Timer ID.  */
                                                            int si_overrun;        /* Timer Overrun count.  */
                                                            sigval_t si_sigval;        /* Signal value.  */
                                                          } _timer;
                                                
                                                /*实时信号 (POSIX.1b).  */
                                                        struct
                                                          {
                                                            __pid_t si_pid;        /* Sending process ID.  */
                                                            __uid_t si_uid;        /* Real user ID of sending process.  */
                                                            sigval_t si_sigval;        /* 发送信号的进程传来的数据. 参考 sigqueue(3p)  */
                                                                                            /* 子类型:                           */
                                                                                            /*   typedef union sigval           */
                                                                                                         /*    {                                         */
                                                                                                        /*         int   sival_int;  //             */
                                                                                                        /*         void *sival_ptr;  //          */ 
                                                                                                          /*     } sigval_t;                          */ 
                                                          } _rt;
                                                
                                                /* SIGCHLD(通常由子进程退出引起) */
                                                        struct
                                                          {
                                                            __pid_t si_pid;        /* Which child.  */
                                                            __uid_t si_uid;        /* Real user ID of sending process.  */
                                                            int si_status;        /* Exit value or signal.  */
                                                            __clock_t si_utime; /* User time consumed */
                                                            __clock_t si_stime; /* System time consumed */
                                                          } _sigchld;
                                                
                                                /* SIGILL, SIGFPE, SIGSEGV, SIGBUS.  */
                                                        struct
                                                          {
                                                            void *si_addr;        /* Memory location which caused fault */
                                                          } _sigfault;          
                                                
                                                /* SIGPOLL.  */
                                                        struct
                                                          {
                                                            long int si_band;        /* Band event for SIGPOLL.  */
                                                            int si_fd;              /* File descriptor */
                                                            } _sigpoll;
                                               } _sifields;
                                   }
            
            1.4.1.8 关于siginfo_t 类型中  si_code 可取的值, 以及原因.        
         下面是对于一个任何类型的信号,  si_code 可取的值及产生该信号的原因:  
                       SI_USER        kill(2) or raise(3)
                       SI_KERNEL      Sent by the kernel.
                       SI_QUEUE       sigqueue(2)
                       SI_TIMER       POSIX timer expired
                       SI_MESGQ       POSIX 消息队列状态改变 (since Linux 2.6.6); see mq_notify(3)
                       SI_ASYNCIO     AIO 完成
                       SI_SIGIO       queued SIGIO
                       SI_TKILL       tkill(2) or tgkill(2) (since Linux 2.4.19)
        
        下面是对于一个 SIGCHLD 信号,  si_code 中可取的值和原因:
                       CLD_EXITED     child has exited
                       CLD_KILLED     child was killed
                       CLD_DUMPED     child terminated abnormally
                       CLD_TRAPPED    traced child has trapped
                       CLD_STOPPED    child has stopped
                       CLD_CONTINUED  stopped child has continued (since Linux 2.6.9)

        下面是对于一个 SIGPOLL 信号,  si_code 中可取的值和原因:
                       POLL_IN        data input available
                       POLL_OUT       output buffers available
                       POLL_MSG       input message available
                       POLL_ERR       i/o error
                       POLL_PRI       high priority input available
                       POLL_HUP       device disconnected

        下面是对于一个 SIGILL 信号,  si_code 中可取的值和原因:
                       ILL_ILLOPC     illegal opcode
                       ILL_ILLOPN     illegal operand
                       ILL_ILLADR     illegal addressing mode
                       ILL_ILLTRP     illegal trap
                       ILL_PRVOPC     privileged opcode
                       ILL_PRVREG     privileged register
                       ILL_COPROC     coprocessor error
                       ILL_BADSTK     internal stack error

        下面是对于一个 SIGFPE 信号,  si_code 中可取的值和原因:
                       FPE_INTDIV     integer divide by zero
                       FPE_INTOVF     integer overflow/
                       FPE_FLTDIV     floating-point divide by zero
                       FPE_FLTOVF     floating-point overflow
                       FPE_FLTUND     floating-point underflow
                       FPE_FLTRES     floating-point inexact result
                       FPE_FLTINV     floating-point invalid operation
                       FPE_FLTSUB     subscript out of range
                       
        下面是对于一个 SIGSEGV  信号,  si_code 中可取的值和原因:
                       SEGV_MAPERR    address not mapped to object
                       SEGV_ACCERR    invalid permissions for mapped object
                                          
        下面是对于一个 SIGBUS 信号,  si_code 中可取的值和原因:
                       BUS_ADRALN     invalid address alignment
                       BUS_ADRERR     nonexistent physical address
                       BUS_OBJERR     object-specific hardware error

        下面是对于一个 SIGTRAP 信号,  si_code 中可取的值和原因:
                       TRAP_BRKPT     process breakpoint
                       TRAP_TRACE     process trace trap

    1.4.2 注意事项:
    根据POSIX标准, 进程在忽略不是由 kill(2) 或raise(3) 产生的 SIGFPE, SIGILL, 或 SIGSEGV 
    信号时, 其行为是未定义的 .  整数除零将产生未定义结果. 在某些架构机器上将产生一个
         SIGFPE 信号 (同样大多负整型数除 -1 也可能产生 SIGFPE.)忽略该信号可能导致死循环.


    1.5 发送信号:
            int sigqueue(pid_t pid, int signo, const union sigval value);      
                成功: 0 
                       出错: -1                                                     
        1.5.1 sigqueue 函数会立即返回. 
           1.5.1.1如果 pid 进程调用sigaction函数为 signo 指定的信号绑定handler 时使用了 
                    SA_SIGINFO 标志,并且系统有相应的资源, 则该信号将加入系统的信号队列中, 并可
          以被pid 号进程访问. 
           1.5.1.2若未使用 SA_SIGINFO 标志, 则至少向 pid 号进程发送一次 signo 信号. 而且此时对
          于是否会向 pid 进程发送 value 参数是未定的.
          1.5.1.3 注意:
        在多线程环境中, ?一个线程调用 sigqueue 向所属进程发送信号, 
        并且该线程中并未block该信号,
        还有同一进程内也没有其他的线程要处理这个信号(其它线程block掉了该信号, 或虽
        然没有block该信号,但也没有使用 sigwait()来等待该信号), 
     则:
        在本次 sigqueue 调用返回前, 就会向调用线程发送该信号.
    
    1.6 检查是否有 pending 的信号 
        int sigpending(sigset_t *set);
          成功: 返回0 , 并将 pending 信号集合的 mask 写入地址 set 中. 
                       出错: -1
                       
        1.6.1 因为 set 为 mask ,而不是直接的集合.
        所以, 在判断一个具体的信号是否有pending 的信号时应该如下使用,以SIGSTOP 为例:
                    sigset_t set;
                    Hsigemptyset(&set);
                    Hsigpending(&set);
                    if(  !sigismember( &set, signum) ) printf("There is pending SIGSTOP signals" );

     1.7 等待信号, 挂起线程:
           int sigwait(const sigset_t *set, int *sig);
      返回值:
          成功: 0 . 
                       出错:大于零的一个错误值. 

        1.7.1 挂起调用线程并等待, 直到收到一个在指定集合 set 中的信号时返回, 同时从pending 的
        信号?列表中移除本次收到的这个信号, 并返回该信号数值到第二个参数 sig 中.
        1.7.2 sigwait() 和 sigwaitinfo() 的区别在于:
                    sigwait()只根据 signal number 来识别需要等待的信号并返回本次信号的signal number. 
                    sigwaitinfo() 则可以根据 siginfo_t 来识别需要等待的信号并返回本次信号的siginfo_t.
        就是说 sigwaitinfo()  可以精确的指定需要等待的信号类型, 但也更加重量级   

     1.8 等待信号, 挂起调用进程:
           int sigwaitinfo(const sigset_t *set, siginfo_t *info);
           int sigtimedwait(const sigset_t *set, siginfo_t *info,
                        const struct timespec *timeout);
      返回值:
          成功: 本次信号的 signal number (大于零).   并从pending 信号列表中移除本次信号.        
                       出错: -1.                         
                 而且当sigtimedwait() 在设定时间内未接受到信号会设置 errono 为 EAGAIN .
        1.8.1   sigtimedwait() 除了多使用了一个超时参数 timeout 外, 其他都和sigwaitinfo 一样.
                    struct timespec {
                       long    tv_sec;         /* seconds */
                       long    tv_nsec;        /* nanoseconds */
                   }
        当参数timeout 中两个参数都指定为 0 时,进行 poll 操作, 即 sigtimedwait()立即返回,并且:
            在检查信号queue之前, 其中已有要等待的信号, 立即返回该信号的 siginto_t 信息.
                         queue 中一个也没有所要等待的信号时, 立即返回error.
                        
     1.9 等待信号, 挂起调用进程,:
            int sigsuspend(const sigset_t *mask);
      返回值:
          成功: -1, 通常 errno 还被置为 EINTR . 
                若该进程挂起后, 接受的是终止类型的信号则该进程被终止.
                接受的是非终止类型的信号, 则该进程执行相应的handler.
                所以这个函数返回值很怪异.
                       出错: errno  被置为 EFAULT  (  参考 sigsuspend(3p) );
                       
        1.8.1 注意:
        通常 sigsuspend 会和sigprocmask 一起使用. 这样可以预防在程序的关键代码段执行期
        间受某些信号影响:
                  1.8.1.1 首先进程在执行关键代码之前调用sigprocmask 来blocks 某些信号. 并且将
               执行block 操作之前的信号集合保存到 sigprocmask 的 oldset 中.
                  1.8.1.2 其次在关键代码执行完后, 该进程调用 sigsuspend  并传递之前调用
                               sigprocmask时保存在它的第三个参数 oldset 中的信号集合给 sigsuspend.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值