信号相关

阅读(28)
评论(0)
Beginning

信号#

信号是软件中断,提供了一种处理异步事件的方法,如在终端按下Ctrl+C会产生SIGINT中断信号。

信号产生#

  • 用户键入:必须在终端按下Ctrl+CCtrl+X等会产生对应信号
  • 硬件异常产生信号:除数为0、无效的内存引用等,如对执行一个无效内存引用的进程产生SIGSEGV信号
  • 进程调用kill函数将信号发送给另一个进程或进程组,接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的所有者是超级用户
  • 用户可用kill命令将信号发送给其他进程
  • 当检测到某种软件条件已经发生,并将其通知有关进程时也产生信号。如管道的读进程已终止,此时向管道写会产生SIGPIPE信号

信号处理#

  1. 忽略。大多数信号都可使用该方式,担忧两种信号不能被忽略(SIGKILL和SIGSTOP)。
  2. 捕捉信号。需提前通知内核在某种信号发生时调用一个用户函数,在该函数中用户处理这个信号事件。
  3. 执行系统默认动作。大多数信号的系统默认动作是终止进程。

信号列举(以Linux2.4.22为例)#

名字 说明 默认动作 名字 说明 默认动作
SIGABRT 异常终止(abort) 终止+core SIGALRM 超时(alarm) 终止
SIGBUS 硬件故障 终止+core SIGCHLD 子进程状态改变 忽略
SIGCONT 使暂停进程继续 继续/忽略 SIGEMT 硬件故障 终止+core
SIGFPE 算术异常 终止+core SIGHUP 连接断开 终止
SIGILL 非法硬件指令 终止+core SIGINT 终端中断符 终止
SIGIO 异步IO 终止/忽略 SIGIOT 硬件故障 终止+core
SIGKILL 终止 终止 SIGPIPE 写至无读进程的管道 终止
SIGPOLL 可轮询事件(poll) 终止 SIGPROF 梗概事件超时 终止
SIGPWR 电源失效/重启动作 终止/忽略 SIGQUIT 终端退出符 终止+core
SIGSEGV 无效内存引用 终止+core SIGSTKFLT 协处理器栈故障 终止
SIGSTOP 停止 暂停进程 SIGSYS 无效系统调用 终止+core
SIGTERM 终止 终止 SIGTRAP 硬件故障 终止+core
SIGTSTP 终端停止符 暂停进程 SIGTTIN 后台读控制tty 暂停进程
SIGTTOU 后台写至控制tty 暂停进程 SIGURG 紧急情况(套接字) 忽略
SIGUSR1 用户定义的信号 终止 SIGUSR2 用户定义的信号 终止
SIGVTALRM 虚拟时间闹钟 终止 SIGWINCH 终端窗口大小改变 忽略
SIGXCPU 超过CPU限制 终止+core SIGXFSZ 超过文件长度限制 终止+core

信号相关函数#

#include <signal.h>
// 成功返回信号以前的处理配置,出错返回SIG_ERR
// signo是上表的信号名
// func是常量SIG_IGN、SIG_DFL或当接收此信号后要调用的函数地址
void (*signal(int signo, void (*func)(int)))(int);

signal函数原型说明此函数需要两个参数,返回一个函数指针。

static void sig_usr(int signo) {
  printf("received SIGUSR1\n");
}
int main()
{
  if(signal(SIGUSR1, sig_usr) == SIG_ERR)
    printf("can't catch SIGUSR1");
}

当执行一个程序时,所有信号的状态都是系统默认或忽略,当一个进程fork时,其子进程继承父进程的信号处理方式。

信号集#

一个能表示多个信号的数据类型。

// 初始化set指向的信号集,清除其中所有信号
int sigemptyset(sigset_t *set);
// 初始化set,使其包括所有信号
int sigfillset(sigset_t *set);
// 添加
int sigaddset(sigset_t *set, int signo);
// 删除
int sigdelset(sigset_t *set, int signo);
// 用于判断是否包括指定信号
int sigismember(const sigset_t *set, int signo);

调用sigprocmask可以检测或更改其信号屏蔽字,用于阻塞指定信号传递到当前进程。

// how指示怎样用set修改当前信号屏蔽字
//    SIG_BLOCK:当前信号屏蔽字并上set
//    SIG_UNBLOCK:解除存在set中的当前信号屏蔽字
//    SIG_SETMASK:用set替代当前信号屏蔽字
// oset非空则当前信号屏蔽字通过oset返回
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
// 返回当前信号集
int sigpending(sigset_t *set);

sigaction函数用于检查或修改与指定信号相关联的处理动作,用以取代signal函数

// signo是要检测或修改的信号编号;act指向要修改的动作,oact用于接收信号之前的动作
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
/* 其中sigaction结构:
struct sigaction {
  void (*sa_handler)(int);  /*信号处理函数地址*/
  sigset_t sa_mask;         /*阻塞的信号*/
  int sa_flags;             
  /*可替代的信号处理函数地址*/
  void (*sa_sigaction)(int, siginfo_t *, void *);
};
// 其中sa_mask会在嗲用信号捕捉函数前加到进程的信号屏蔽字中,当从信号捕捉函数返回时会将信号屏蔽字恢复
*/

可以阻塞选择的信号以保护不希望被信号中断的代码临界区,之后再解除阻塞,然后等待之前被阻塞的信号发生,但我们无法控制信号发生的时间窗口,可能在解除阻塞调用返回前就触发了信号,这是不可靠的,我们需要在一个原子操作中先恢复信号屏蔽字然后使进程休眠,可由sigsuspend函数提供该功能。

// 将进程屏蔽字设置为sigmask指向的值,在捕捉到一个信号或发生了一个会终止该进程的信号前,该进程被挂起
// 如果捕捉到信号且从信号处理程序返回,则sigsuspend返回,并将进程信号屏蔽字恢复
int sigsuspend(const sigset_t *sigmask);
posted @   songlh424  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
more_horiz
keyboard_arrow_up light_mode menu palette
选择主题
点击右上角即可分享
微信分享提示