Linux信号处理函数详解
一下内容引述《Linux/Unix系统编程详解》
设计信号处理函数
为了尽可能简化信号处理模块的设计流程,并减少引发竞争条件的可能性。
- 信号处理模块配置了一个全局标志变量并停止运行;每当该标志被置位时, 主程序会随机做出相应的反应;
- 信号处理器负责执行特定类型的清洁操作. 当任务终止时, 它会通过非本地跳转解开栈并将控制权返回到预先确定的位置.
可重入函数与异步信号安全函数
可重入函数
首先明确单线与多线程程序的区别。典型的UNIX系统结构是由一条持续运行的核心指令序列构成,在整个运行期间中央处理器按顺序处理每一条指令。而针对一个多核处理器环境而言,则会同时存在多个这样的核心指令序列(即子进程)。对于一个多核处理器环境中的一个子进程而言,则会有多条这样的核心指令序列并行工作以提高系统吞吐量。
当一个信号处理机制被激活时,在任意时刻可能触发相关终端任务的执行,并将其状态信息传递给主任务流程(main program flow)。这种情况下实际上形成了两个相互关联但又各自独立的任务流:其中一条是主任务流程(main program flow),另一条则由信号处理机制维护(signal processing stream)。
当同一进程中有多条独立且互不干扰的任务模块均能安全地调用某个特定函数时,则称此函数具有重入性(reentrant property)。这种特性使得相关的代码可以在不同的上下文中重复使用而不导致功能冲突或数据损坏。
SUSv3对可重入函数进行了明确说明:当多个线程同时被调用来执行该函数时,并且按照交错顺序运行时(即各线程以非确定顺序依次被调用),其行为结果与各个线程独立地按照任意顺序依次被调用时所呈现的效果是一致的。
某些更新全局变量或静态数据结构的操作可能无法实现重入机制。(仅依赖本地变量的操作必定是可重入的操作)当两条独立执行路径(如两条不同的执行线程)同时尝试修改同一个全局变量或数据类型时,这种情况可能导致相互干扰并引发错误的结果。
SUSv3规定,在只有在以下条件下被视为异常:首先,在被测功能运行期间发生故障的情况下;其次,在触发故障后立即中断对该功能的操作,并且同时该处理器自身也进行了被测功能的操作。
因此,设计信号处理函数有如下两种选择:
- 通过优化设计使该信号处理器函数代码具备重入能力,并仅允许使用异步信号安全的函数;
- 若主程序运行异常功能或可能操作并修改全局数据结构,则会阻止了信号传输。
全局变量
尽管存在重入问题是存在的,在大多数嵌入式系统设计中仍然需要实现主程序与信号处理器函数之间的安全通信机制以实现对共享资源的有效管理。为了确保系统的安全性,在这种情况下采用以下措施:允许每个信号处理函数专注于单一任务并设置相应的全局标志位。当主程序定期检查该标志位的状态时就能及时采取相应的动作以响应特定的信号传递请求。为了进一步保障系统的安全性建议在声明变量时始终使用volatile关键字以避免编译器进行优化干扰并且采用sig_atomic_t类型的引用确保了读写操作的原子性
终止信号处理处理器
以下是从喜好处理器函数中终止的其他一些方法:
- 采用_exit_来结束进程,在处理程序开始前可执行一些清洁操作;然而不应采用该方法来终结信号处理程序。
- 通过发射特定的信号杀死进程。
- 在执行跨进程操作时可以从信号处理函数中进行跳跃。
- 由abort_函数引发的中断会产生核心文件的转储。
SA_SIGINFO标志
如果使用sigaction创建处理器函数并配置了SA_SIGINFO标志,则当 processor function响应该信号时它可以获取关于该信号的一些额外信息。
void handler(int sig, siginfo_t *siginfo, void *ucontext);
代码解释
第一个参数标识信号编号,第二个参数为siginfo,则用于提供信号附加信息的一个结构。
由于上述信号处理器函数的原型与标准处理器函数存在差异,在遵循C语言类型规则的前提下,
无法通过sigaction结构中的sa_handler字段来指定相应的处理器函数地址。
struct sigaction {
union {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t*, void*);
}__sigaction_handler;
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
#define sa_handler __sigaction_handler.sa_handler
#define sa_sigaction __sigaction_handler.sa_sigaction
代码解释
结构sigaction通过复合体整合了sa_sigaction与sa_handler。由于对sigaction的特殊操作仅涉及其中的一个字段。
使用SA_SIGINFO的一个例子
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
if(sigaction(SIGINFO, &act, NULL) == -1)
errExit("sigaction")
代码解释
由带有SA_SIGINFO标志的信号处理器函数所支持的上下文管理机制中定义了一个关键参数ucontext。该参数是一个指向特定结构(定义于<ucontext.h>中的ucontext_t)的指针。该结构存储了描述调用前进程状态的重要信息,其中包括上一个进程信号掩码、寄存器保存值以及如程序计数器(cp)和栈指针寄存器(sp)等关键数据项。这些信息通常仅在特定情况下被引用。
