Linux信号
发布日期:2021-05-08 23:07:56 浏览次数:20 分类:精选文章

本文共 2972 字,大约阅读时间需要 9 分钟。

Linux信号机制详解

1. 信号的概念及机制

信号在计算机科学中扮演着重要角色,就像人类社会中的信息传递一样。Linux系统中的信号用于通知进程某些事件发生,进程接收到信号后会暂停当前执行任务,处理信号,等处理完毕后再继续执行。信号的核心特征包括四个要素:信号编号、信号名、产生事件以及默认处理动作。

2. 信号的产生与处理方式

信号可以通过多种方式产生,主要有以下几种:

  • 按键产生:如Ctrl+cCtrl+z,这些组合键常用于终止进程。
  • 系统调用产生:如killraiseabort,这些函数用于强制终止进程。
  • 硬件异常产生:如段错误、除零错误、总线错误等,这些错误会触发相应的信号。
  • 软件条件产生:如定时器超时(alarm函数)。
  • 命令产生:如shell中的kill命令。

信号的处理方式主要有三种:

  • 执行默认动作:如终止进程、生成核心文件等。
  • 忽略信号:不采取任何特殊处理。
  • 捕获并执行自定义操作:通过注册信号处理函数来实现。

需要注意的是,SIGKILLSIGSTOP信号无法被捕捉、阻塞或忽略,只能执行默认动作。

3. 内核处理信号的机制

信号的产生和处理主要由内核完成。当内核检测到某个信号被触发时,并不会立即处理,而是在用户模式返回时(如系统调用、中断或异常返回)才会进行处理。这种机制确保了信号处理的正确性和稳定性。

4. 信号的编号

在Linux系统中,信号编号从131,其中1~31为普通信号,32~63为实时信号(主要用于硬件驱动编程)。可以通过kill -l命令查看系统支持的所有信号。

5. 信号的默认动作

信号的默认动作根据具体信号类型有所不同,常见的有:

  • SIGTERM:终止进程。
  • SIGCORE:终止进程并生成核心文件。
  • SIGIGNORE:忽略信号。
  • SIGCONT:继续执行进程。
  • SIGSTOP:暂停进程。

通过man 7 signal命令可以查看各信号的详细描述。

6. Linux系统提供的信号操作函数

6.1 信号的产生

可以通过kill函数向指定进程发送指定信号。函数原型如下:

int kill(pid_t pid, int sig);

其中sig为信号名,需使用宏定义,如SIGKILL

6.2 信号捕捉
  • 注册信号捕捉函数
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum, sighandler_t handler);
  • 更健壮的接口
    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

    结构体struct sigaction包含以下成员:

    • sa_handler:信号捕捉后的处理函数。
    • sa_mask:屏蔽信号集。
    • sa_flags:信号处理标志。
6.3 信号集操作

进程控制块维护两个信号集:阻塞信号集和未决信号集。可以通过以下函数进行操作:

  • 初始化与清空信号集
    int sigemptyset(sigset_t *set);
    int sigfillset(sigset_t *set);
  • 信号集操作
    int sigaddset(sigset_t *set, int signum);
    int sigdelset(sigset_t *set, int signum);
    int sigismember(const sigset_t *set, int signum);
  • 设置与解除信号屏蔽
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • 读取未决信号集
    int sigpending(sigset_t *set);
  • 替换屏蔽信号集并挂起进程
    int sigsuspend(const sigset_t *sigmask);
6.4 捕捉默认信号处理

通过修改默认信号处理函数,可以改变信号的行为。例如,捕捉SIGINT信号并打印进程ID。

7. 信号接口函数测试

7.1 修改SIGINT默认动作
#include 
#include
#include
void sigint_handler(int sig) {
printf("Current Process Id = %d\n", getpid());
}
int main(int argc, char **argv) {
signal(SIGINT, sigint_handler);
while (1) {
sleep(10);
}
return 0;
}

运行此程序时,按下Ctrl+c组合键,程序不会终止,而是打印当前进程ID。

7.2 实现自定义的mysleep函数
#include 
#include
#include
void sig_alrm(int signo) {
/* nothing to do */
}
unsigned int mysleep(unsigned int nsecs) {
struct sigaction newact, oldact;
sigset_t newmask, oldmask, suspmask;
unsigned int unslept;
newact.sa_handler = sig_alrm;
sigemptyset(&newact.sa_mask);
newact.sa_flags = 0;
sigaction(SIGALRM, &newact, &oldact);
sigemptyset(&newmask);
sigaddset(&newmask, SIGALRM);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
alarm(nsecs);
suspmask = oldmask;
sigdelset(&suspmask, SIGALRM);
sigsuspend(&suspmask);
unslept = alarm(0);
sigaction(SIGALRM, &oldact, NULL);
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return unslept;
}
int main(void) {
while (1) {
mysleep(2);
printf("Two seconds passed\n");
}
return 0;
}

通过mysleep函数,可以实现自定义的睡眠功能,忽略SIGALRM信号,默认动作为终止进程。

上一篇:Linux的scandir函数
下一篇:Vi编辑器的基本用法

发表评论

最新留言

感谢大佬
[***.8.128.20]2025年05月08日 21时05分38秒