实时信号"/>
UNPV2 学习:POSIX 实时信号
POSIX 实时信号
信号可以被分为两组:
- 信号值在 SIGRTMIN 与 SIGRTMAX 之间的实时信号
- 其它所有的信号,如 SIGALRM,SIGINT,SIGKILL 等
实时信号的工作特点:
- 信号被入队。同一个信号多次触发时,事件以 FIFO 的方式入队。
- 当多个在 SIGRTMIN 到 SIGRTMAX 之间未屏蔽的信号被入队时,数值小的信号会在数值大的信号前发送,这意味着 SIGRTMIN 信号的优先级最高。
- 当一个非实时信号触发时,信号处理函数的唯一参数是信号值。实时信号比这些信号携带更多的信息,实时信号的信号处理函数支持带一个 siginfo_t 结构体类型的信息。
- 一些新的函数被定义以支持实时信号,这些新函数允许发送者传输一个 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 causedhardware-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 inglibc 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 violationoccurred (since Linux 3.19) */void *si_upper; /* Upper bound when address violationoccurred (since Linux 3.19) */int si_pkey; /* Protection key on PTE that causedfault (since Linux 4.6) */void *si_call_addr; /* Address of system call instructionint 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. Onsome 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 insi_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 处理。
更多推荐
UNPV2 学习:POSIX 实时信号
发布评论