linux signal的作用是什么

86次阅读
没有评论

共计 2671 个字符,预计需要花费 7 分钟才能阅读完成。

本文丸趣 TV 小编为大家详细介绍“linux signal 的作用是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“linux signal 的作用是什么”文章能帮助大家解决疑惑,下面跟着丸趣 TV 小编的思路慢慢深入,一起来学习新知识吧。

linux signal 用来通知进程某个特定事件的发生或者是让进程执行某个特定的处理函数;signal 即信号,是 Unix 家族中一个古老的通信机制;信号可以来自终端的键盘字符输入,比如 control- C 触发的 SIGINIT,也可以来自与硬件或软件有关的异常,比如应用程序访问了无效地址触发的 SIGSEGV,定时器到期触发的 SIGALARM 等。

linux signal 用来做什么?

Linux 中的信号处理机制

信号 (Signal) 是 Unix 家族中一个古老的通信机制,主要用来通知进程某个特定事件的发生,或者是让进程执行某个特定的处理函数。说它古老,是因为它在第一代 Unix 系统中就已经存在了。

信号的发送

信号可以来自终端 (terminal) 的键盘字符输入,比如 control- C 触发的 SIGINIT;也可以来自与硬件或软件有关的异常,比如应用程序访问了无效地址触发的 SIGSEGV(segmentation fault),定时器到期触发的 SIGALARM 等。这些信号都是由内核发送给进程的。

进程收到的信号还可以来自于其他进程。但不是所有的进程都可以向其他任意一个进程发送信号,只有具有 root 权限的 super user 才可以这么做,对于普通 user 的进程,只能向属于同一 user 的进程发送信号。

那进程可以向内核发送信号吗?可以是可以,但内核线程是不会响应的,发了也白发,除非……你修改内核代码。

通常信号被认为是一种异步的机制,但是在 Linux 的代码中,以下由异常引起的信号也被称为 synchronous 的:

#define SYNCHRONOUS_MASK \ (sigmask(SIGSEGV) | sigmask(SIGBUS) | sigmask(SIGILL) | \ sigmask(SIGTRAP) | sigmask(SIGFPE) | sigmask(SIGSYS))

这里我们称接收信号的进程为“目标进程”。传统的信号发送函数是 kill(),这名字看起来好吓人,感觉好像是目标进程马上就要被“杀”掉了。事实上,虽然各路信号都用 kill()来发送,但真正要 kill 掉进程的,只占一小部分。当然,现在已经有了功能更强,名字也更友好的 sigqueue()了。

来看下两者的函数原型:

int kill(pid_t pid, int sig);int sigqueue(pid_t pid, int sig, const union sigval value);

pid 代表目标进程的 PID。Linux 中进程的 PID 都是正数,那参数 pid 的值如果是 0 或者负数,是不是就是非法的呢?非也,事实上,0 和负数在这里都是有其他用途的。Linux 里有个进程组的概念 (process group),表示一类进程的集合。在 kill() 中,参数 pid 为 0 说明信号是发给当前进程所在进程组的所有进程,小于 -1 则是向编号为 -pid 的进程组发送信号。

__kill_pgrp_info(sig, info, pid ? find_vpid(-pid) : task_pgrp(current));

pid 为 -1 表示发送给除 Init 进程和自身以外的所有进程,或者说是除自身以外的所有 pid 大于 1 的进程。

for_each_process(p) {
if (task_pid_vnr(p) 1 !same_thread_group(p, current))
           ...

如果这里 pid 是进程自己的 PID,那么就是进程给自身发送信号。为此,Linux 还提供了一个更简洁的接口:raise()函数。

kill(getpid(), sig) -- raise(sig)

需要注意的是,在 sigqueue()中,不能通过将参数 pid 的值设为负数来向整个进程组发送信号。

sig 代表要发送信号的编号,Linux 中的信号编号是从 1 开始的,那参数 sig 的值如果为 0 会怎样呢?这里的 0 也有妙用,sig 为 0 时并不会真正的往目标进程 (或进程组) 发送信号,而是用来检测目标进程 (或进程组) 是否存在。

至于 sigqueue 增加的第三个参数,其定义是这样的:

union sigval {
int sival_int;
void __user *sival_ptr;};

传统的信号是没有传递消息的功能的,sigval 算是稍微扩展了一下信号的通信能力。比如,通信双方可以事先约定某些事件为特定的 int 值,这个 sival_int 就可以用来保存具体的 int 值,目标进程可以据此来区分不同的事件,做出不同的响应。当然,这种方法传递的消息内容受限,且不易扩展,因而不适合用作常规的通信手段。

信号的内核处理

就算是进程之间发送信号,那也是要经过内核的,可以理解成是被内核“截获”了吧。

由于内核态和用户态的切换操作在不同的硬件体系架构上是不同的,而且有一些信号,比如 SIGCHLD 和 SIGSTOP,其实现也是和架构相关的,所以 Linux 中 signal 机制的很多代码都是放在架构相关的目录下的,比如 /arch/arm/kernel/signal.c。

一个信号的相关信息在内核中用 siginfo_t 结构体表示:

siginfo_t{
   int si_signo;
   int si_sicode;
   union __sifields _sifields;
   ...}

si_signo 是信号的编号,从 1 到 64 的值都是合法的。

si_sicode 记录了信号的来源,比如 SI_USER 表示信号是由进程调用 kill()发出的,SI_QUEUE 是由进程调用 sigqueue()发出的,SI_KERNEL 则说明该信号由内核产生的。

_sifields 对不同的信号会有不同的含义,通常包括信号发送进程的 si_pid,发送进程所属 user 的 si_uid 等。对于由 sigqueue()发送的信号,还包括 sigval 参数所携带的附加信息。

内核在截获到一个进程发送的信号后,会首先做一系列的检查,比如该信号的值是否合法啦,进程有没有发送这个信号的权限啦。如果检查通过,就调用 copy_from_user()将该信号的相关信息复制到 siginfo_t 结构体中。

读到这里,这篇“linux signal 的作用是什么”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注丸趣 TV 行业资讯频道。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-12发表,共计2671字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)