信号安装
本节介绍信号安装的方法。
函数 signal
SylixOS 信号机制中最简单的接口是 signal 函数:
#include <signal.h>
void (*signal(int iSigNo, void (*pfuncHandler)(int)))(int);
函数 signal 原型分析:
- 此函数成功返回一个函数指针,失败返回 SIG_ERR,如下表所示。
- 这个函数指针指向的函数没有返回值。
- 参数是一个整型值。
- 参数 iSigNo 是下表中的任一信号名。
- 参数 pfuncHandler 是要安装的信号函数或常量 SIG_IGN、常量 SIG_DFL。
我们查看 <system/signal/signal.h> 会发现如下表形式的定义:
宏名 | 值 | 含义 |
---|---|---|
SIG_ERR | (PSIGNAL_HANDLE)-1 | 错误信号句柄 |
SIG_DFL | (PSIGNAL_HANDLE)0 | 默认信号句柄 |
SIG_IGN | (PSIGNAL_HANDLE)1 | 忽略的信号句柄 |
SIG_CATCH | (PSIGNAL_HANDLE)2 | 特殊的信号处理方式(SylixOS并未明确规定其行为) |
SIG_HOLD | (PSIGNAL_HANDLE)3 | 特殊的信号处理方式(SylixOS并未明确规定其行为) |
注意:
宏 PSIGNAL_HANDLE 可在 <kernel/include/k_ptype.h> 中发现:
typedef VOID (*PSIGNAL_HANDLE)(INT);
在之前的 UNIX 系统实现中,signal 函数安装的信号是不可靠的,因为安装的信号不是永久的,只要信号被递送,则信号动作将恢复成默认动作。值得庆幸的是,SylixOS 的信号机制支持 POSIX 实时扩展部分,保证了 signal 函数将永久安装一个信号。
函数 sigaction
sigaction 函数检查或修改与指定信号相关联的处理动作。此函数取代了 signal 函数,在 SylixOS 中 signal 函数通过调用 sigaction 函数实现。
#include <signal.h>
int sigaction(int iSigNo,
const struct sigaction *psigactionNew,
struct sigaction *psigactionOld);
函数 sigaction 原型分析:
- 此函数成功时返回 0,失败时返回 -1 并设置错误号。
- 参数 iSigNo 是下表中的任一信号名。
- 参数 psigactionNew 是新的信号处理结构。
- 输出参数 psigactionOld 保存之前的处理结构。
信号名 | 说明 |
---|---|
SIGHUP | 挂断控制终端或进程。通常用此通知守护进程再次读取它们的配置文件,因为守护进程不会有控制终端,通常决不会接收到这种信号 |
SIGINT | 来自键盘的中断。一般采用Ctrl + C来产生此信号。当一个进程在运行时失控,特别是他正在屏幕上产生大量不需要的输出时,常用此信号终止 |
SIGQUIT | 来自键盘的退出 |
SIGILL | 非法指令 |
SIGTRAP | 跟踪断点 |
SIGABRT | 异常结束 |
SIGUNUSED | 未使用 |
SIGFPE | 协处理出错,如除以0、浮点溢出等 |
SIGKILL | 强迫进程结束(不能被忽略或捕捉) |
SIGBUS | 总线错误,通常是指示一个实现定义的硬件故障 |
SIGSEGV | 无效内存引用 |
SIGUNUSED2 | 未使用2 |
SIGPIPE | 管道写错误,无读者 |
SIGALRM | 实时定时器报警 |
SIGTERM | 进程终止。这是kill命令的默认动作,由于这个信号是由应用程序捕获的,使用SIGTERM也让程序有机会在退出之前做好清理工作,从而优雅的终止 |
SIGCNCL | 线程取消 |
SIGSTOP | 停止进程执行。此信号不能被捕获和忽略(不能被忽略或捕捉) |
SIGTSTP | tty发出停止进程 |
SIGCONT | 恢复进程继续执行 |
SIGCHLD | 子进程停止或者被终止。系统默认是忽略此信号 |
SIGTTIN | 后台进程请求输入 |
SIGTTOU | 后台进程请求输出 |
SIGCANCEL | 同SIGTERM相同 |
SIGIO | 异步I/O事件 |
SIGXCPU | 进程超出了软CPU事件限制 |
SIGXFSZ | 进程超出了软文件长度限制 |
SIGVTALRM | 函数setitimer设置的虚拟间隔定时器已经超时 |
SIGPROF | 函数setitimer设置的梗概间隔定时器已经超时 |
SIGWINCH | 更改了窗口的大小 |
SIGINFO | 信息请求 |
SIGPOLL | 同SIGIO相同 |
SIGUSR1 | 用户定义信号1 |
SIGUSR2 | 用户定义信号2 |
SIGPWR | 电源失败重新开始 |
SIGSYS | 错误的系统调用 |
SIGURG | 网络连接上接到带外的数据时,可选择地产生此信号 |
SIGLOWMEM | 系统内存不足告警 |
SIGSTKSHOW | 打印任务栈信息和上下文信息 |
SIGRTMIN-SIGRTMAX | SylixOS实现SIGRTMIN = 48、SIGRTMAX = 63,系统没有指定明确的含义,由用户自定义,并且不应该使用某数值 |
sigaction 函数使用下面的结构来检查或修改指定信号相关联的处理动作:
struct sigaction {
union {
PSIGNAL_HANDLE _sa_handler;
PSIGNAL_HANDLE_ACT _sa_sigaction;
} _u; /* 信号服务函数句柄 */
sigset_t sa_mask; /* 执行时的信号屏蔽码 */
INT sa_flags; /* 该句柄处理标志 */
PSIGNAL_HANDLE sa_restorer; /* 恢复处理函数指针 */
};
#define sa_handler _u._sa_handler
#define sa_sigaction _u._sa_sigaction
当更改信号动作时,如果 sa_handler 成员包含一个信号捕捉函数的地址(不是常量 SIG_IGN 或 SIG_DFL),则 sa_mask 成员包含了一个信号集,在调用该信号捕捉函数之前,这一信号集要加到线程的信号屏蔽字中。sa_flags 成员指定对信号进行处理的各个选项。如下表所示 sa_flags 成员标志的意义。
选项 | 说明 |
---|---|
SA_NOCLDSTOP | 子进程被删除时不要产生信号 |
SA_NOCLDWAIT | 不产生僵尸进程 |
SA_SIGINFO | 信号句柄需要 siginfo 参数 |
SA_ONSTACK | 自定义栈 |
SA_RESTART | 执行信号句柄后重启调用 |
SA_INTERRUPT | 执行信号句柄后不重启调用 |
SA_NOMASK | 不阻止在指定信号处理句柄中再收到信号 |
SA_NODEFER | 不阻止在指定信号处理句柄中再收到信号 |
SA_ONESHOT | 信号句柄一旦被调用就恢复到默认状态 |
SA_RESETHAND | 执行句柄后,将信号句柄设置为默认动作 |
如果 sa_flags 包含 SA_NOCLDSTOP 标志,父进程不接收子进程的暂停信号,SIGCHLD 信号被忽略,SIGCHLD 信号在“进程与信号”做详细介绍。
指定 SA_NOCLDWAIT 标志,将由系统接管回收子进程的资源,因此不会产生僵尸进程。
指定 SA_NOMASK 标志,在执行信号处理函数时,如果收到相同信号则会被中断,如此将会形成递归。
指定 SA_RESETHAND 标志,执行一次信号处理函数后,信号动作将设置为默认动作,此标志兼容了之前不可靠的信号机制。
sa_sigaction 成员是一个替代的信号处理程序,在 sigaction 结构中如果使用了 SA_SIGINFO 标志,则使用该信号处理程序。在 SylixOS 中,sa_sigaction 成员和 sa_handler 成员使用了同一存储区,所有应用程序只能一次使用这两个成员中的一个。
通常,如果使用 sa_handler 成员,按下面方式调用信号处理程序:
void handler(int signo);
- 如果使用 sa_sigaction 成员,也就是设置了 SA_SIGINFO 标志,那么按下面方式调用信号处理程序:
void handler(int signo, siginfo_t *siginfo, void *arg);
- siginfo_t 结构包含了信号产生原因的有关信息,在 SylixOS 中该结构如下定义:
typedef struct siginfo {
INT si_signo;
INT si_errno;
INT si_code;
union {
struct {
INT _si_pid;
INT _si_uid;
} _kill;
struct {
INT _si_tid;
INT _si_overrun;
} _timer;
struct {
INT _si_pid;
INT _si_uid;
} _rt;
struct {
INT _si_pid;
INT _si_uid;
INT _si_status;
clock_t _si_utime;
clock_t _si_stime;
} _sigchld;
struct {
INT _si_band;
INT _si_fd;
} _sigpoll;
} _sifields;
#define si_pid _sifields._kill._si_pid
#define si_uid _sifields._kill._si_uid
#define si_timerid _sifields._timer._si_tid
#define si_overrun _sifields._timer._si_overrun
#define si_status _sifields._sigchld._si_status
#define si_utime _sifields._sigchld._si_utime
#define si_stime _sifields._sigchld._si_stime
#define si_band _sifields._sigpoll._si_band
#define si_fd _sifields._sigpoll._si_fd
union sigval si_value;
#define si_addr si_value.sival_ptr /* Faulting insn/memory ref */
#define si_int si_value.sival_int
#define si_ptr si_value.sival_ptr
……
} siginfo_t;
union sigval 将在“队列信号”小节做详细介绍,成员 si_code 指示了信号的产生原因,如下表所示 SylixOS 中各种信号的 si_code 值定义。信号处理函数的第三个参数在 SylixOS 中返回栈的地址或者 NULL。
sa_restorer 成员是被废弃的,不应该被使用。
信号 | 代码 | 说明 |
---|---|---|
ANY | SI_KILL SI_USER SI_QUEUE SI_TIMER SI_ASYNCIO SI_MESGQ SI_KERNEL | 使用 kill()发送的信号 同 SI_KILL 使用 sigqueue 发送的信号 POSIX 定时器发送的信号 异步 I/O 系统完成发送的信号 接收到一条消息产生的信号 SylixOS 内核内部使用 |
SIGILL | ILL_ILLOPC ILL_ILLOPN ILL_ILLADR ILL_ILLTRP ILL_PRVOPC ILL_PRVREG ILL_COPROC ILL_BADSTK | 非法操作码 非法操作数 非法地址模式 非法陷入 特权操作码 特权寄存器 协处理器出错 内部栈出错 |
SIGFPE | FPE_INTDIV FPE_INTOVF FPE_FLTDIV FPE_FLTOVF FPE_FLTUND FPE_FLTRES FPE_FLTINV FPE_FLTSUB | 整数除以 0 整数溢出 浮点除以 0 浮点向上溢出 浮点向下溢出 浮点不精确结果 无效浮点操作 下标超出范围 |
SIGSEGV | SEGV_MAPERR SEGV_ACCERR | 地址不映射至对象 对于映射对象的无效权限 |
SIGBUS | BUS_ADRALN BUS_ADRERR BUS_OBJERR | 无效地址对齐 不存在的物理地址 对象特定硬件错误 |
SIGTRAP | TRAP_BRKPT TRAP_TRACE | 进程断点陷入 进程跟踪陷入 |
SIGCHLD | CLD_EXITED CLD_KILLED CLD_DUMPED CLD_TRAPPED CLD_STOPPED CLD_CONTINUED | 子进程终止 子进程已异常终止(无 core) 子进程已异常终止(有 core,目前 SylixOS 不支持 core 文件) 被跟踪子进程已陷入 子进程已停止 停止的子进程已继续 |
SIGPOLL | POLL_IN POLL_OUT POLL_MSG POLL_ERR POLL_PRI POLL_HUP | 数据输入可用 输出缓冲区可用 输入消息可用 I/O 错误 高优先级输入可用 设备断开 |
下面实例展示了 sigaction 函数的使用方法,程序中 alarm 函数会在之后小节做详细介绍。
#include <stdio.h>
#include <signal.h>
void handler (int signum, siginfo_t *siginfo, void *arg)
{
fprintf(stdout, "alarm signal.\n");
}
int main (int argc, char *argv[])
{
struct sigaction newact, oldact;
int ret;
newact.sa_sigaction = handler;
newact.sa_flags = SA_SIGINFO;
sigemptyset(&newact.sa_mask);
ret = sigaction(SIGALRM, &newact, &oldact);
if (ret < 0) {
fprintf(stderr, "sigaction error.\n");
return (-1);
}
alarm(2);
sleep(5);
sigaction(SIGALRM, &oldact, NULL); /* 恢复之前的信号行为 */
return (0);
}
在 SylixOS Shell 下运行程序,结果如下:
# ./Sigaction
Alarm signal.