UNPV2 学习:POSIX 实时信号

POSIX 实时信号

信号可以被分为两组:

  1. 信号值在 SIGRTMIN 与 SIGRTMAX 之间的实时信号
  2. 其它所有的信号,如 SIGALRM,SIGINT,SIGKILL 等

实时信号的工作特点:

  1. 信号被入队。同一个信号多次触发时,事件以 FIFO 的方式入队。
  2. 当多个在 SIGRTMIN 到 SIGRTMAX 之间未屏蔽的信号被入队时,数值小的信号会在数值大的信号前发送,这意味着 SIGRTMIN 信号的优先级最高。
  3. 当一个非实时信号触发时,信号处理函数的唯一参数是信号值。实时信号比这些信号携带更多的信息,实时信号的信号处理函数支持带一个 siginfo_t 结构体类型的信息。
  4. 一些新的函数被定义以支持实时信号,这些新函数允许发送者传输一个 sigval 结构。例如 sigqueue 函数替代 kill 函数来发送信号到某些进程。

实时信号由下列 Posix.1 特性产生 ,它们由包含在传递给信号处理程序的 siginfo_t 结构中的 si_code 值标识。

  • SI_ASYNCIO 异步 I/O 请求完成
  • SI_MESGQ 新的消息被投递到空的 Posix 消息队列中
  • SI_QUEUE 使用 sigqueue 函数发送信号
  • SI_TIMER 一个 timer_settime 函数设置的定时器时间耗尽
  • SI_USER 此信号使用 kill 函数发送

linux sigaction 函数

linux 中可以使用 sigaction 函数注册实时信号的处理函数,非实时的信号处理函数也可以使用此函数注册并携带更多的信息,其原型如下:

int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

signum 为待设定的信号值,act 为信号处理函数属性的封装结构,oldact 用于返回旧的配置。sigaction 结构体的定义如下:

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

sa_handler 用以兼容旧的只以信号值为参数的信号处理函数注册,sa_sigaction 为新的信号处理函数原型,sa_mask 设定信号掩码,sa_flags 设置一些 flags,sa_restorer 用于设定从信号处理函数中恢复旧的现场需要执行的函数。在 linux 内核信号机制需要解决的两个关键问题 这篇博文中有对 sa_restorer 的相关描述,libc 中的实现是一个调用 rt_sigreturn 系统调用的函数。

siginfo_t 结构的定义如下:

siginfo_t {
               int      si_signo;     /* Signal number */
               int      si_errno;     /* An errno value */
               int      si_code;      /* Signal code */
               int      si_trapno;    /* Trap number that caused
                                         hardware-generated signal
                                         (unused on most architectures) */
               pid_t    si_pid;       /* Sending process ID */
               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 */
               union sigval si_value; /* Signal value */
               int      si_int;       /* POSIX.1b signal */
               void    *si_ptr;       /* POSIX.1b signal */
               int      si_overrun;   /* Timer overrun count;
                                         POSIX.1b timers */
               int      si_timerid;   /* Timer ID; POSIX.1b timers */
               void    *si_addr;      /* Memory location which caused fault */
               long     si_band;      /* Band event (was int in
                                         glibc 2.3.2 and earlier) */
               int      si_fd;        /* File descriptor */
               short    si_addr_lsb;  /* Least significant bit of address
                                         (since Linux 2.6.32) */
               void    *si_lower;     /* Lower bound when address violation
                                         occurred (since Linux 3.19) */
               void    *si_upper;     /* Upper bound when address violation
                                         occurred (since Linux 3.19) */
               int      si_pkey;      /* Protection key on PTE that caused
                                         fault (since Linux 4.6) */
               void    *si_call_addr; /* Address of system call instruction
							 int      si_syscall;   /* Number of attempted system call
                                         (since Linux 3.5) */
               unsigned int si_arch;  /* Architecture of attempted system call
                                         (since Linux 3.5) */

不同的信号会填充 siginfo_t 结构中的不同字段,例如 sigaction manual 中对 SIGBUS、SIGSEGV 等信号填充的字段的描述信息如下:

* SIGILL,  SIGFPE, SIGSEGV, SIGBUS, and SIGTRAP fill in si_addr with the address of the fault.  On
         some architectures, these signals also fill in the si_trapno field.

         Some  suberrors  of  SIGBUS,  in  particular  BUS_MCEERR_AO  and  BUS_MCEERR_AR,  also  fill  in
         si_addr_lsb.   This field indicates the least significant bit of the reported address and there‐
         fore the extent of the corruption.  For example, if a full page was corrupted, si_addr_lsb  con‐
         tains  log2(sysconf(_SC_PAGESIZE)).   When SIGTRAP is delivered in response to a ptrace(2) event
         (PTRACE_EVENT_foo), si_addr is not populated, but si_pid and si_uid are populated with  the  re‐
         spective process ID and user ID responsible for delivering the trap.  In the case of seccomp(2),
         the tracee will be shown as delivering the event.  BUS_MCEERR_* and si_addr_lsb  are  Linux-spe‐
         cific extensions.

         The SEGV_BNDERR suberror of SIGSEGV populates si_lower and si_upper.

         The SEGV_PKUERR suberror of SIGSEGV populates si_pkey.

这些信号会填充 si_addr 字段以命名异常地址的位置,在一些结构上还可能会填充其它的字段。

siginfo_t 的一个使用场景

dpdk 支持网卡热插拔技术需要解决的几个关键问题 这篇文章中,我描述了使用 sigbus 信号携带的 siginfo_t 结构中的 si_addr 字段来处理网卡热拔出时程序仍旧访问网卡 bar 空间触发总线异常的问题。

dpdk 中注册 sigbus 信号处理函数的代码如下:

				struct sigaction action;

			  .................................

        sigemptyset(&mask);
        sigaddset(&mask, SIGBUS);
        action.sa_flags = SA_SIGINFO;
        action.sa_mask = mask;
        action.sa_sigaction = sigbus_handler;
        sigbus_need_recover = !sigaction(SIGBUS, &action, &sigbus_action_old);

可以看到它设置了 SA_SIGINFO 标志以支持 SIGBUS 信号携带一个 siginfo_t 结构,在 sigbus_handler 函数中使用如下代码访问 si_addr 字段:

ret = rte_bus_sigbus_handler(info->si_addr);

rte_bus_sigbus_handler 中负责查询异常的地址是否属于某个总线,并调用总线注册的 sigbus_handler 处理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值