共計 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 行業資訊頻道。