linux signal用來通知進程某個特定事件的發生或是讓進程執行某個特定的處理函數;signal即訊號,是Unix家族中一個古老的通訊機制;訊號可以來自終端的鍵盤字元輸入,例如control-C觸發的SIGINIT,也可以來自與硬體或軟體有關的異常,例如應用程式存取了無效位址觸發的SIGSEGV,計時器到期觸發的SIGALARM等。
本教學操作環境:linux5.9.8系統、Dell G3電腦。
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; ...}
内核在截获到一个进程发送的信号后,会首先做一系列的检查,比如该信号的值是否合法啦,进程有没有发送这个信号的权限啦。如果检查通过,就调用copy_from_user()将该信号的相关信息复制到siginfo_t结构体中。
相关推荐:《Linux视频教程》
以上是linux signal用來做什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!