欧美性猛交xxxx免费看_牛牛在线视频国产免费_天堂草原电视剧在线观看免费_国产粉嫩高清在线观看_国产欧美日本亚洲精品一5区

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux內(nèi)核中信號詳解

jf_0tjVfeJz ? 來源:嵌入式ARM和Linux ? 作者:嵌入式ARM和Linux ? 2024-01-13 09:40 ? 次閱讀

  • 1 信號的角色

    • 1.1 x86/64架構(gòu)信號定義

    • 1.2 ARM架構(gòu)信號定義

    • 1.3 RISC-V架構(gòu)信號定義

    • 1.4 信號的系統(tǒng)調(diào)用

    • 1.5 信號工作原理

  • 2 信號的響應(yīng)行為

  • 3 POSIX信號和多線程程序

  • 4 與信號相關(guān)的數(shù)據(jù)結(jié)構(gòu)

    • 4.2.1 x86/Linux2.6.11的定義

    • 4.2.2 x86-64/Linux2.6.11的定義

    • 4.2.3 x86-64/linux5.18.18的定義

    • 4.2.4 ARM/linux5.18.18的定義

    • 4.2.5 RISC-V/linux6.7

    • 4.1 信號描述符和信號處理程序描述符

    • 4.2 sigaction數(shù)據(jù)結(jié)構(gòu)

    • 4.3 掛起信號隊列

  • 5 信號數(shù)據(jù)結(jié)構(gòu)的操作函數(shù)

    • 5.1 x86架構(gòu)

    • 5.2 ARM和RISC-V架構(gòu)

Unix最早引入了信號機制,允許用戶進程間進行交互;內(nèi)核也使用信號通知進程某些系統(tǒng)事件。信號機制已經(jīng)存在了30年,期間只有一些細(xì)微的變化。

我們首先介紹Linux內(nèi)核如何處理信號,其次討論允許進程交換信號的系統(tǒng)調(diào)用。

1 信號的角色

信號是發(fā)送給進程,或一組進程的非常短的消息。通常,可能僅發(fā)送一個表示信號的編碼。標(biāo)準(zhǔn)信號沒有參數(shù)等其它信息。

信號的編碼,在Linux中使用前綴SIG的宏表示。如前面提到的SIGCHLD宏,其展開的值是17,當(dāng)子進程停止或終止時,發(fā)送給父進程的信號。SIGSEGV,等于11,當(dāng)進程發(fā)生非法內(nèi)存引用時發(fā)送給進程的信號。

信號兩個主要作用:

  1. 使進程意識到發(fā)生了某個事件
  2. 讓進程執(zhí)行信號處理程序

當(dāng)然,這兩個目的不是相互排斥的,因為通常進程必須對某些事件做出響應(yīng)(如執(zhí)行服務(wù)例程)。

1.1 x86/64架構(gòu)信號定義

11-1列出了Linux/i386的前31個信號(Unix系統(tǒng)定義的信號,x86架構(gòu),Linux2.6.11,linux后續(xù)版本中32/64位的信號定義統(tǒng)一到了一個文件中)。某些信號,比如SIGCHLDSIGSTOP與架構(gòu)相關(guān);甚至,還有一些信號如SIGSTKFLT專門為某些架構(gòu)定義的。

# 信號 默認(rèn)動作 說明 POSIX
1 SIGHUP Terminate 掛起控制終端和進程 Yes
2 SIGINT Terminate 鍵盤中斷 Yes
3 SIGQUIT Dump 鍵盤退出 Yes
4 SIGILL Dump 非法指令 Yes
5 SIGTRAP Dump 調(diào)試斷點 No
6 SIGABRT Dump 異常終止 Yes
6 SIGIOT Dump 等價于SIGABRT No
7 SIGBUS Dump 總線錯誤 No
8 SIGFPE Dump 浮點異常 Yes
9 SIGKILL Terminate 殺死進程 Yes
10 SIGUSR1 Terminate 進程可用 Yes
11 SIGSEGV Dump 非法內(nèi)存引用 Yes
12 SIGUSR2 Terminate 進程可用 Yes
13 SIGPIPE Terminate 管道沒有讀進程使用 Yes
14 SIGALRM Terminate 實時時鐘 Yes
15 SIGTERM Terminate 進程終止 Yes
16 SIGSTKFLT Terminate 協(xié)處理器堆棧錯誤 No
17 SIGCHLD Ignore 子進程停止/終止/被跟蹤時的信號 Yes
18 SIGCONT Continue 恢復(fù)執(zhí)行 Yes
19 SIGSTOP Stop 停止進程執(zhí)行 Yes
20 SIGTSTP Stop 停止tty發(fā)起的進程 Yes
21 SIGTTIN Stop 后臺進程需要輸入 Yes
22 SIGTTOU Stop 后臺進程需要輸出 Yes
23 SIGURG Ignore 套接字上的緊急條件 No
24 SIGXCPU Dump 超出CPU時間限制 No
25 SIGXFSZ Dump 超出文件大小限制 No
26 SIGVTALRM Terminate 用戶態(tài)占用CPU時間定時器 No
27 SIGPROF Terminate 用戶態(tài)和內(nèi)核態(tài)占用CPU時間定時器 No
28 SIGWINCH Ignore 窗口大小改變 No
29 SIGIO Terminate 異步IO No
29 SIGPOLL Terminate 可輪詢事件(poll) No
30 SIGPWR Terminate 電源失效/重啟動 No
31 SIGSYS Dump 無效系統(tǒng)調(diào)用 No
31 SIGUNUSED Dump 等價于SIGSYS No

除了上表中的常規(guī)信號之外,POSIX標(biāo)準(zhǔn)還引入了一類新的信號,稱為實時信號Linux中信號范圍是32~64。實時信號具有以下特性:

  1. 增加了從SIGRTMINSIGRTMAX的實時信號,可以通過sysconf(_SC_RTSIG_MAX)系統(tǒng)函數(shù)獲得當(dāng)前操作系統(tǒng)支持的實時信號的個數(shù)。但是要注意,一般libc會對SIGRTMIN進行修改,保留幾個預(yù)設(shè)的值用于pthread內(nèi)部,比如glibc就保留了3個值。所以在使用實時信號的時候,應(yīng)該使用SIGRTMIN+n、SIGRTMAX-n的方式,而不是直接使用數(shù)值。

  2. 實時信號和常規(guī)信號不一樣,它沒有明確的含義,而是由使用者自己來決定如何使用。

  3. 進程可以接受多個相同的實時信號,而常規(guī)信號不能,在常規(guī)信號沒有得到處理的時候,多個常規(guī)信號會被合為一個。

  4. 實時信號使用sigqueue發(fā)送的時候,可以攜帶附加的數(shù)據(jù)(int或者pointer)。

  5. 實時信號有時間順序的概念,所以同樣的實時信號會按次序被處理。

  6. 信號實質(zhì)上是軟中斷,中斷有優(yōu)先級,信號也有優(yōu)先級。實時信號具有優(yōu)先的概念,數(shù)值越低的信號其優(yōu)先級越高,也就是數(shù)值低的實時信號優(yōu)先得到處理。實時信號和標(biāo)準(zhǔn)信號的優(yōu)先級,在POSIX中是未定義的,一般來說會優(yōu)先處理標(biāo)準(zhǔn)信號。

  7. 實時信號的默認(rèn)行為都一樣,都是結(jié)束當(dāng)前的進程,這個和標(biāo)準(zhǔn)信號是不一樣的。

盡管Linux內(nèi)核不使用實時信號,但是它通過幾個特殊的系統(tǒng)調(diào)用完整支持POSIX標(biāo)準(zhǔn)。

Ubuntu 18.04為例,查看Linux系統(tǒng)中使用的信號方法:

$kill-l
1)SIGHUP2)SIGINT3)SIGQUIT4)SIGILL5)SIGTRAP
6)SIGABRT7)SIGBUS8)SIGFPE9)SIGKILL10)SIGUSR1
11)SIGSEGV12)SIGUSR213)SIGPIPE14)SIGALRM15)SIGTERM
16)SIGSTKFLT17)SIGCHLD18)SIGCONT19)SIGSTOP20)SIGTSTP
21)SIGTTIN22)SIGTTOU23)SIGURG24)SIGXCPU25)SIGXFSZ
26)SIGVTALRM27)SIGPROF28)SIGWINCH29)SIGIO30)SIGPWR
31)SIGSYS34)SIGRTMIN35)SIGRTMIN+136)SIGRTMIN+237)SIGRTMIN+3
38)SIGRTMIN+439)SIGRTMIN+540)SIGRTMIN+641)SIGRTMIN+742)SIGRTMIN+8
43)SIGRTMIN+944)SIGRTMIN+1045)SIGRTMIN+1146)SIGRTMIN+1247)SIGRTMIN+13
48)SIGRTMIN+1449)SIGRTMIN+1550)SIGRTMAX-1451)SIGRTMAX-1352)SIGRTMAX-12
53)SIGRTMAX-1154)SIGRTMAX-1055)SIGRTMAX-956)SIGRTMAX-857)SIGRTMAX-7
58)SIGRTMAX-659)SIGRTMAX-560)SIGRTMAX-461)SIGRTMAX-362)SIGRTMAX-2
63)SIGRTMAX-164)SIGRTMAX

1.2 ARM架構(gòu)信號定義

下圖右邊是ARM架構(gòu)與x86架構(gòu)信號定義的比較圖(左邊是x86架構(gòu),右邊是ARM架構(gòu))。通過對比發(fā)現(xiàn),ARM架構(gòu)比x86架構(gòu)多了一個SIGSWI信號。在對內(nèi)核源代碼進行進一步調(diào)查后,發(fā)現(xiàn)唯一提到SIGSWI(不包括聲明本身)的是文件Linux 5.18.18中,位于tools/perf/trace/beauty/signum.c。具體代碼中只有在打印信號的時候用,貌似已經(jīng)從內(nèi)核中移除。)。一些奇怪的基于ARM 的操作系統(tǒng)(RISCOS)使用這種方式與其模擬器進行通信。它被稱為Arthur OS。

ae816fa2-b13a-11ee-8b88-92fbcf53809c.png

1.3 RISC-V架構(gòu)信號定義

RISC-V架構(gòu)信號定義如下面所示,Linux 6.7內(nèi)核,文件位于/include/uapi/asm-generic/signal.h。信號的定義直接使用了標(biāo)準(zhǔn)的接口規(guī)范。

#define_NSIG64
//...省略
#defineSIGHUP1
#defineSIGINT2
//...省略
#defineSIGPWR30
#defineSIGSYS31
#defineSIGUNUSED31

/*用戶進程不能認(rèn)為這些是常數(shù)*/
#defineSIGRTMIN32
#ifndefSIGRTMAX
#defineSIGRTMAX_NSIG
#endif

所以說,對于Linux信號來說,不管是x86架構(gòu),ARM架構(gòu),還是RISC-V,都是統(tǒng)一的,沒有什么變化。

1.4 信號的系統(tǒng)調(diào)用

Linux提供了一些系統(tǒng)調(diào)用,允許編程者發(fā)送信號,并決定如何響應(yīng)接收到的信號。下表列出了這些系統(tǒng)調(diào)用:

系統(tǒng)調(diào)用 描述
kill() 發(fā)送信號給線程組
tkill() 發(fā)送信號給進程
tgkill() 發(fā)送信號給特定線程組中的進程
sigaction() 設(shè)定信號的行為
signal() 與sigaction()類似
sigpending() 檢查是否為掛起信號
sigprocmask() 修改阻塞信號
sigsuspend() 等待信號
rt_sigaction() 設(shè)定實時信號的行為
rt_sigpending() 檢查是否為掛起的實時信號
rt_sigprocmask() 修改阻塞的實時信號
rt_sigqueueinfo() 發(fā)送實時信號給線程組
rt_sigsuspend() 等待實時信號
rt_sigtimedwait() 與rt_sigsuspend()類似

1.5 信號工作原理

信號的一個重要特性是,可能會在任何時候傳遞給進程。發(fā)送給沒有在執(zhí)行狀態(tài)的進程,就需要保存該信號,以便進程恢復(fù)執(zhí)行時處理它。阻塞信號要求在解除阻塞之前延緩信號的傳遞。

因此,Linux將內(nèi)核的傳遞分為了兩個階段:

  • 信號產(chǎn)生

    內(nèi)核更新目標(biāo)進程的數(shù)據(jù)結(jié)構(gòu),表達一個新信號要被發(fā)送。

  • 信號傳遞

    內(nèi)核通過改變目標(biāo)進程的狀態(tài),且執(zhí)行指定信號處理程序,以強制其響應(yīng)信號,

每個信號最多傳遞一次。信號是消耗性資源:一旦它們被傳遞,所有進程描述符中跟信號有關(guān)的數(shù)據(jù)引用都將取消。

產(chǎn)生還沒有傳遞的信號,稱為掛起信號。任何時候,一個進程只能存在一個給定類型的掛起信號;同一個進程的同類掛起信號會被拋棄。但是,實時信號與此不同:可以同時存在多個同類型的掛起信號。

信號產(chǎn)生還沒有被傳遞這段時間,通常存在于以下時間段:

  • 信號通常只傳遞給當(dāng)前正在運行的進程(current)。

  • 進程可以有選擇地阻塞信號。這種情況下,進程不會接收信號,除非解除阻塞。

  • 執(zhí)行信號處理程序時,進程通常屏蔽掉響應(yīng)的信號(例如,在信號處理程序執(zhí)行完之前自動阻塞該信號)。也就是說,信號處理程序不會被正在處理的信號打斷,所以,信號處理程序不需要考慮可重入的問題。

盡管信號的概念非常簡單,內(nèi)核實現(xiàn)卻相當(dāng)復(fù)雜。內(nèi)核必須:

  • 記住哪些信號被哪個進場阻塞。

  • 當(dāng)從內(nèi)核態(tài)切換到用戶態(tài)時,檢查該進程是否有信號需要處理。這通常發(fā)生在每次定時器中斷時(大約幾個毫秒一次)。

  • 判斷該信號是否被忽略。滿足忽略的條件如下:

    • 目標(biāo)進程沒有被其它進程追蹤(也就是進程描述符中的PT_PTRACED標(biāo)志等于0)。
    • 信號沒有被目標(biāo)進程阻塞。
    • 信號正在被目標(biāo)忽略。(可以是進程顯式忽略,也可以是信號的默認(rèn)行為是忽略且進程沒有更改它)
  • 處理信號,可能涉及到在進程執(zhí)行的任何時候切換到信號處理程序,且需要在處理程序返回時恢復(fù)其原始執(zhí)行上下文。

此外,Linux必須考慮到BSDSystem V信號采用的不同語義,它必須遵循相當(dāng)復(fù)雜的POSIX要求。

2 信號的響應(yīng)行為

信號的響應(yīng)方式有3種:

  1. 忽略信號
  • Terminate

    殺死進程。

  • Dump

    殺死進程,如果可能的話創(chuàng)建一個包含其上下文的核心轉(zhuǎn)儲文件。該文件主要用于調(diào)試目的。

  • Ignore

    忽略信號。

  • Stop

    停止進程。例如將進程置為TASK_STOPPED狀態(tài)。

  • Continue

    繼續(xù)進程(TASK_STOPPED),將其置于TASK_RUNNING狀態(tài)。

  1. 執(zhí)行信號的默認(rèn)行為。默認(rèn)行為是內(nèi)核預(yù)定義好的,如下所示:
  1. 捕獲信號,執(zhí)行自定義的信號處理程序。

注意,阻塞信號不同于忽略信號。只要信號被阻塞,就不會傳遞它。而忽略信號總是傳遞它,只是不執(zhí)行響應(yīng)動作。

SIGKILLSIGSTOP信號不能被忽略,捕獲或阻塞。它們的默認(rèn)動作總是會被執(zhí)行。因此,SIGKILLSIGSTOP信號給予合適權(quán)限的用戶可以終止、停止每個進程(這兒有兩個例外:不能向進程0-swapper發(fā)送信號,并且發(fā)送給進程1-init的信號總是被丟棄。因此,進程0永遠(yuǎn)不會死亡,而進程1只有在init終止時才會死亡。)。

如果信號造成內(nèi)核殺死進程,那么對于給定進程是非常致命的,比如SIGKILL。默認(rèn)行為為Terminate的信號且未被進程捕獲,對于該進程來說也是致命的。但是,如果信號被捕獲,而其處理程序終止進程則不是致命的,因為進程自己選擇的終止,不是內(nèi)核殺死的。

3 POSIX信號和多線程程序

POSIX 1003.1標(biāo)準(zhǔn)對多線程應(yīng)用程序的信號處理有嚴(yán)格的要求:

  • 多線程應(yīng)用程序中所有線程共享信號處理程序;但是,每個線程必須具有自己的掛起信號和阻塞信號的位數(shù)組。

  • kill()sigqueue()POSIX庫函數(shù)必須發(fā)送信號給整個多線程應(yīng)用,而不是某個特定的線程。內(nèi)核生成的所有信號(如SIGCHLD、SIGINTSIGQUIT)都是如此。

  • 發(fā)送給多線程應(yīng)用的信號只被傳遞給一個線程,由內(nèi)核在沒有阻塞該信號的線程中任意選擇。

  • 如果致命信號發(fā)送給多線程應(yīng)用,內(nèi)核將殺死應(yīng)用程序的所有線程,而不僅僅是信號傳遞給的那個線程。

為了遵循POSIX標(biāo)準(zhǔn),Linux 2.6內(nèi)核將多線程應(yīng)用程序?qū)崿F(xiàn)為屬于同一線程組的一組輕量級進程。

本文中的線程組是廣義的,甚至可以使傳統(tǒng)意義上的單進程。術(shù)語進程表示傳統(tǒng)意義上的進程或輕量級進程(線程組中的某個成員)。

此外,如果信號被發(fā)送給一個特定的進程,則是私有的;如果發(fā)送給整個線程組,則是共享的。

4 與信號相關(guān)的數(shù)據(jù)結(jié)構(gòu)

為了追蹤進程或線程組的信號狀態(tài),內(nèi)核在進程描述符中提供了幾個可訪問的數(shù)據(jù)結(jié)構(gòu)。重要的數(shù)據(jù)結(jié)構(gòu)如下所示:

ae9ee96a-b13a-11ee-8b88-92fbcf53809c.png

其中,進程描述符中與信號處理相關(guān)的數(shù)據(jù)字段如下所示:

數(shù)據(jù)類型 名稱 描述
struct signal_struct * signal 指向進程的信號描述符
struct sighand_struct * sighand 指向進程的信號處理程序描述符
sigset_t blocked 阻塞信號掩碼
sigset_t real_blocked 阻塞信號臨時掩碼(rt_sigtimedwait())系統(tǒng)調(diào)用使用
struct sigpending pending 私有掛起信號
unsigned long sas_ss_sp 備選信號處理程序堆棧的地址

blocked存儲了被進程屏蔽掉的信號。數(shù)據(jù)類型為sigset_t,是一個位數(shù)組,每一位代表一類信號:

typedefstruct{
unsignedlongsig[2];
}sigset_t;

因為32位系統(tǒng)的unsigned long32位,信號最大數(shù)量是64(用_NSIG宏表示)。因為沒有信號是0,所以,信號值等于sigset_t中位索引1。具體可以參考前面列出的表。

4.1 信號描述符和信號處理程序描述符

進程描述符中的signal字段指向信號描述符,類型為signal_struct,用來記錄共享掛起信號。此外,信號描述符還有一些與信號處理不太相關(guān)的字段,如rlim(進程資源限制),或pgrpsession字段,分別存儲線程組領(lǐng)導(dǎo)者的PID和進程中會話領(lǐng)導(dǎo)者的PID。事實上,我們在學(xué)習(xí)clone(),fork(),和vfork()系統(tǒng)調(diào)用一節(jié)時了解到,同一線程組中的所有進程共享信號描述符,也就是說,通過clone()系統(tǒng)調(diào)用,并設(shè)置CLONE_THREAD標(biāo)志,創(chuàng)建的所有進程,其信號描述符中所有字段必須相同。

信號描述符中與信號處理相關(guān)的字段,如下表所示:

類型 變量 描述
atomic_t count 信號描述符的使用計數(shù)器
atomic_t live 線程組中活動進程的數(shù)量
wait_queue_head_t wait_chldexit wait4()系統(tǒng)調(diào)用中休眠進程的等待隊列
struct task_struct * curr_target 線程組中接收到信號的最后一個進程的描述符
struct sigpending shared_pending 共享掛起信號的數(shù)據(jù)結(jié)構(gòu)
int group_exit_code 線程組的進程終止碼
struct task_struct * group_exit_task 殺死整個線程組時使用
int notify_count 殺死整個線程組時使用
int group_stop_count 停止整個線程組時使用
unsigned int flags 傳遞修改進程狀態(tài)的信號時使用的標(biāo)志

除了信號描述符,每個進程還有一個信號處理描述符,數(shù)據(jù)結(jié)構(gòu)為sighand_struct,其描述了線程組怎樣處理信號。其字段如下所示:

類型 變量 描述
atomic_t count 信號處理程序描述符的使用計數(shù)器
struct k_sigaction[64] action 指定傳遞信號時要執(zhí)行的動作的結(jié)構(gòu)數(shù)組
spinlock_t siglock 包含信號和信號處理程序等描述符的自旋鎖

正如先前提到的,使用clone()CLONE_SIGHAND標(biāo)志創(chuàng)建的進程們共享信號處理描述符。所以,count字段記錄了共享信號處理描述符的進程數(shù)。在POSIX多線程應(yīng)用中,線程組中的所有輕量級進程引用相同的信號描述符和相同的信號處理描述符。

4.2 sigaction數(shù)據(jù)結(jié)構(gòu)

有些架構(gòu)可能會將信號的某些屬性僅對內(nèi)核可見。因此,存儲在k_sigaction中的信號屬性,既包含了對用戶態(tài)隱藏的屬性,也包含了用戶態(tài)所有可見的屬性。事實上,在x86平臺上,所有的信號屬性對用戶態(tài)都是可見的。

因此,k_sigaction結(jié)構(gòu)簡化為一個類型為sigactionsa結(jié)構(gòu),它包含如下字段*:

用戶態(tài)應(yīng)用程序用來給signal()sigaction()系統(tǒng)調(diào)用傳遞參數(shù)的sigaction數(shù)據(jù)結(jié)構(gòu)與內(nèi)核使用的數(shù)據(jù)結(jié)構(gòu)略有不同。

  • sa_handler

    該字段指定要執(zhí)行的動作類型;可以是信號處理程序的指針,SIG_DFL(值為0,執(zhí)行默認(rèn)行為),或SIG_IGN(值為1,忽略信號)。

  • sa_flags

    如何處理處理信號的標(biāo)志,下表列出了其中的一些。

    因為歷史原因,這些標(biāo)志和irqaction具有一樣的前綴SA_;然而,兩組標(biāo)志沒有任何關(guān)系。

  • sa_mask

    類型為sigset_t,用來指定在運行信號處理程序時屏蔽掉的信號。

sa_flags值和意義

標(biāo)志名稱 描述
SA_NOCLDSTOP 僅適用于SIGCHLD;當(dāng)進程停止時不向父進程發(fā)送SIGCHLD
SA_NOCLDWAIT 僅適用于SIGCHLD;當(dāng)進程終止時不會創(chuàng)建zombie僵尸進程
SA_SIGINFO 向信號處理程序提供額外的信息(查看稍后的改變信號動作)
SA_ONSTACK 為信號處理程序使用替代堆棧(查看稍后的捕捉信號一節(jié))
SA_RESTART 中斷的系統(tǒng)調(diào)用自動重啟(查看稍后的`系統(tǒng)調(diào)用的重新執(zhí)行)
SA_NODEFER,
SA_NOMASK
在執(zhí)行信號處理程序時不屏蔽信號
SA_RESETHAND,
SA_ONESHOT
在執(zhí)行信號處理程序后重置為默認(rèn)操作
4.2.1 x86/Linux2.6.11的定義
#ifdef__KERNEL__
structold_sigaction{
__sighandler_tsa_handler;
old_sigset_tsa_mask;
unsignedlongsa_flags;
__sigrestore_tsa_restorer;
};

structsigaction{
__sighandler_tsa_handler;
unsignedlongsa_flags;
__sigrestore_tsa_restorer;
sigset_tsa_mask;/*masklastforextensibility*/
};

structk_sigaction{
structsigactionsa;
};
#else
/*這是為了迎合libc庫的實現(xiàn)*/
structsigaction{
union{
__sighandler_t_sa_handler;
void(*_sa_sigaction)(int,structsiginfo*,void*);
}_u;
sigset_tsa_mask;
unsignedlongsa_flags;
void(*sa_restorer)(void);
};

#definesa_handler_u._sa_handler
#definesa_sigaction_u._sa_sigaction
#endif/*__KERNEL__*/
4.2.2 x86-64/Linux2.6.11的定義
structsigaction{
__sighandler_tsa_handler;
unsignedlongsa_flags;
__sigrestore_tsa_restorer;
sigset_tsa_mask;/*masklastforextensibility*/
};

structk_sigaction{
structsigactionsa;
};
4.2.3 x86-64/linux5.18.18的定義

較高版本中的內(nèi)核中,將k_sigaction定義到了一個統(tǒng)一的文件中(include/linux/signal_types.h

structsigaction{
#ifndef__ARCH_HAS_IRIX_SIGACTION
__sighandler_tsa_handler;
unsignedlongsa_flags;
#else
unsignedintsa_flags;
__sighandler_tsa_handler;
#endif
#ifdef__ARCH_HAS_SA_RESTORER
__sigrestore_tsa_restorer;
#endif
sigset_tsa_mask;/*masklastforextensibility*/
};

structk_sigaction{
structsigactionsa;
#ifdef__ARCH_HAS_KA_RESTORER
__sigrestore_tka_restorer;
#endif
};

為了兼容libc庫,需要根據(jù)架構(gòu)進行一些定義:

#ifndef__KERNEL__
/*這是為了迎合libc庫的實現(xiàn)*/
#ifdef__i386__

structsigaction{
union{
__sighandler_t_sa_handler;
void(*_sa_sigaction)(int,structsiginfo*,void*);
}_u;
sigset_tsa_mask;
unsignedlongsa_flags;
void(*sa_restorer)(void);
};

#definesa_handler_u._sa_handler
#definesa_sigaction_u._sa_sigaction

#else/*__i386__*/

structsigaction{
__sighandler_tsa_handler;
unsignedlongsa_flags;
__sigrestore_tsa_restorer;
sigset_tsa_mask;/*masklastforextensibility*/
};

#endif/*!__i386__*/
#endif/*!__KERNEL__*/
4.2.4 ARM/linux5.18.18的定義

ARM架構(gòu)下內(nèi)核中數(shù)據(jù)結(jié)構(gòu)與x86架構(gòu)相同,但是,為了兼容libc,不得不定義一些特殊的結(jié)構(gòu):

#ifndef__KERNEL__
/*這是為了迎合libc庫的實現(xiàn)*/
structsigaction{
union{
__sighandler_t_sa_handler;
void(*_sa_sigaction)(int,structsiginfo*,void*);
}_u;
sigset_tsa_mask;
unsignedlongsa_flags;
void(*sa_restorer)(void);
};

#definesa_handler_u._sa_handler
#definesa_sigaction_u._sa_sigaction

#endif/*__KERNEL__*/
4.2.5 RISC-V/linux6.7

最新版本內(nèi)核中沒有變化,RISC-V相關(guān)實現(xiàn)與ARM架構(gòu)相同,只是,取消了聯(lián)合體復(fù)雜的實現(xiàn)(這就是后發(fā)優(yōu)勢):

#ifndef__KERNEL__
structsigaction{
__sighandler_tsa_handler;
unsignedlongsa_flags;
#ifdefSA_RESTORER
__sigrestore_tsa_restorer;
#endif
sigset_tsa_mask;/*masklastforextensibility*/
};
#endif

4.3 掛起信號隊列

正如前面所述,某些系統(tǒng)可以產(chǎn)生信號:kill()rt_sigqueueinfo()發(fā)送信號到整個線程組,而tkill()tgkill()發(fā)送信號到某個特定的進程。

為了記錄當(dāng)前哪些信號被掛起,內(nèi)核給每個進程提供了兩個掛起信號隊列:

  • 共享掛起信號隊列,掛載到信號描述符的shared_pending字段,存儲整個線程組的掛起信號。

  • 私有掛起信號隊列,掛載到進程描述符的pending字段,存儲進程(輕量級)自己的掛起信號。

掛起信號隊列的元素是類型為sigpending的數(shù)據(jù)結(jié)構(gòu),定義如下:

structsigpending{
structlist_headlist;
sigset_tsignal;
}

signal字段是一個位數(shù)組,每一位代表一個掛起信號,而list字段是雙向鏈表的頭,該表頭指向sigqueue數(shù)據(jù)結(jié)構(gòu)組成的鏈表,sigqueue字段定義如下表所示:

類型 變量 描述
struct list_head list 掛起信號隊列的鏈表鏈接
spinlock_t * lock 信號處理描述符的siglock字段的指針
int flags sigqueue數(shù)據(jù)結(jié)構(gòu)中的標(biāo)志
siginfo_t info 描述發(fā)送信號的事件信息
struct user_struct * user 指向進程擁有者的用戶數(shù)據(jù)結(jié)構(gòu)

其中,siginfo_t的大小為128字節(jié),描述特定信號事件的信息;它包含以下字段:

  • si_signo

    信號編碼。

  • si_errno

    產(chǎn)生信號的指令的錯誤編碼,如果是0則沒有錯誤。

  • si_code

    標(biāo)識發(fā)送信號方的編碼(參加表11-8

    11-8。最重要的信號發(fā)送方編碼

    編碼名稱 發(fā)送方
    SI_USER kill()raise()(查看稍后的與信號處理相關(guān)的系統(tǒng)調(diào)用)
    SI_KERNEL 通用內(nèi)核函數(shù)產(chǎn)生的信號
    SI_QUEUE sigqueue()(查看稍后的與信號處理相關(guān)的系統(tǒng)調(diào)用)
    SI_TIMER 定時器到時
    SI_ASYNCIO 異步IO完成
    SI_TKILL tkill()tgkill()(查看稍后的與信號處理相關(guān)的系統(tǒng)調(diào)用)
  • _sifields

    一個聯(lián)合體數(shù)據(jù)類型,根據(jù)信號類型存儲信息。例如是SIGKILL信號,siginfo_t記錄發(fā)送進程的PIDUID;如果是SIGSEGV,則記錄產(chǎn)生信號時訪問的內(nèi)存地址。

5 信號數(shù)據(jù)結(jié)構(gòu)的操作函數(shù)

為了方便處理信號,內(nèi)核提供了幾個函數(shù)和宏,如下所示。其中,set是指向sigset_t變量的指針,nsig是信號值,mask是一個unsigned long類型的位掩碼。

  • sigemptyset(set)/sigfillset(set)

    設(shè)置sigset_t變量的位為01

  • sigaddset(set,nsig)/sigdelset(set,nsig)

    設(shè)置sigset_t變量中指定信號nsig10。事實上,sigaddset()可以簡化為

    set->sig[(nsig-1)/32]|=1UL<1)%32);
    

    sigdelset()簡化為

    set->sig[(nsig-1)/32]&=~(1UL<1)%32));
    
  • sigaddsetmask(set,mask)/sigdelsetmask(set,mask)

    設(shè)置sigset_t類型變量的位掩碼為10。

    set->sig[0]|=mask;
    

    and to:

    set->sig[0]&=~mask;
    
  • sigismember(set,nsig)

    返回信號nsigsigset_t變量中的對應(yīng)位。實際可以簡化為:

    return1&(set->sig[(nsig-1)/32]>>((nsig-1)%32));
    
  • sigmask(nsig)

    產(chǎn)生信號nsig的位索引。換句話說,如果內(nèi)核需要設(shè)置、清除或測試sigset_t類型變量中的信號對應(yīng)位,可以通過該宏可以導(dǎo)出正確的位。

  • sigandsets(d,s1,s2)/sigorsets(d,s1,s2)/signandsets(d,s1,s2)

    s1s2執(zhí)行邏輯ANDORNAND操作,結(jié)果保存到d中。

  • sigtestsetmask(set,mask)

    如果變量的相應(yīng)位掩碼為1,則返回1;否則返回0。只有在信號1~32之間使用。

  • siginitset(set,mask)

    mask的位初始化sigset_t變量中1 ~ 32信號對應(yīng)的低位,清除33 ~ 63信號對應(yīng)位。

  • siginitsetinv(set,mask)

    mask的補碼初始化sigset_t變量1~32信號對應(yīng)的低位,并設(shè)置33~63信號對應(yīng)的位。

  • signal_pending(p)

    判斷由p指向的進程描述符是否具有非阻塞的掛起信號,如果有,返回1true);如果沒有,則返回0false)。該函數(shù)是通過對進程的TIF_SIGPENDING標(biāo)志進行檢查實現(xiàn)的。

  • recalc_sigpending_tsk(t)/recalc_sigpending()

    第一個函數(shù)檢查t指向的進程描述符中是否有掛起信號(通過檢查t->pending->signal字段實現(xiàn)),或者檢查該進程所屬線程組是否有掛起信號(通過檢查t-> signal->shared_pending->signal實現(xiàn))。該函數(shù)隨后設(shè)置t->thread_info->flags中的TIF_SIGPENDING標(biāo)志位。recalc_sigpending()等價于recalc_sigpending_tsk(current)。

  • rm_from_queue(mask,q)

    從掛起信號隊列q中移除mask位掩碼中對應(yīng)的掛起信號。

  • flush_sigqueue(q)

    從掛起信號隊列q中移除所有掛起信號。

  • flush_signals(t)

    刪除發(fā)送給進程的所有信號(t指向進程描述符)。實現(xiàn)方式是清除t->thread_info->flagsTIF_SIGPENDING標(biāo)志,并分別對t->pendingt->signal->shared_ pending隊列調(diào)用flush_sigqueue()

5.1 x86架構(gòu)

較新的內(nèi)核版本(比如,v5.18.18v6.7)中,這些函數(shù)都已經(jīng)作了統(tǒng)一處理,位于文件include/linux/signal.h中。但是,x86架構(gòu)體系的i386它的實現(xiàn)使用了匯編指令(為了效率),比如:

staticinlineint__gen_sigismember(sigset_t*set,int_sig)
{
boolret;
asm("btl%2,%1"CC_SET(c)
:CC_OUT(c)(ret):"m"(*set),"Ir"(_sig-1));
returnret;
}

__gen_sigismembersigismember的一個底層實現(xiàn),其中用到了匯編指令btl(將寄存器的位進行比較,如果該位被設(shè)置則置為1,則CC_SET(c)條件碼會被設(shè)置為真,否則為假)。所以,i386有一部分設(shè)置信號的函數(shù)定義獨自一個文件(/arch/x86/include/asm/signal.h)。

5.2 ARM和RISC-V架構(gòu)

x86-64、ARMRISC-V架構(gòu)的函數(shù)定義都位于include/linux/signal.h文件中,是統(tǒng)一實現(xiàn)。


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 嵌入式
    +關(guān)注

    關(guān)注

    5094

    文章

    19189

    瀏覽量

    307943
  • 內(nèi)核
    +關(guān)注

    關(guān)注

    3

    文章

    1383

    瀏覽量

    40442
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11351

    瀏覽量

    210493
  • 信號
    +關(guān)注

    關(guān)注

    11

    文章

    2808

    瀏覽量

    77155

原文標(biāo)題:Linux內(nèi)核-信號的基本理解

文章出處:【微信號:嵌入式ARM和Linux,微信公眾號:嵌入式ARM和Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    linux內(nèi)核信號是如何處理的?看完全懂了……

    本文簡單介紹下Linux信號處理機制,為介紹二進制翻譯下信號處理機制做一個鋪墊。 本文主要參考書目《Linux內(nèi)核源代碼情景分析》《獨辟蹊徑
    的頭像 發(fā)表于 11-16 05:11 ?1.4w次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>信號</b>是如何處理的?看完全懂了……

    一文詳解Linux內(nèi)核源碼組織結(jié)構(gòu)

    概要:本文內(nèi)容包含Linux源碼樹結(jié)構(gòu)分析、Linux Makefile分析、Kconfig文件分析、Linux內(nèi)核配置選項分析。這些知識是為了理解
    的頭像 發(fā)表于 05-10 19:28 ?5826次閱讀

    Linux內(nèi)核中信號的傳遞過程

    前面我們已經(jīng)介紹了內(nèi)核注意到信號的到來,調(diào)用相關(guān)函數(shù)更新進程描述符以便進程接收處理信號。但是,如果目標(biāo)進程此時沒有運行,內(nèi)核則推遲傳遞信號
    的頭像 發(fā)表于 01-17 09:51 ?1271次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>中信號</b>的傳遞過程

    Linux內(nèi)核地址映射模型與Linux內(nèi)核高端內(nèi)存詳解

    Linux 操作系統(tǒng)和驅(qū)動程序運行在內(nèi)核空間,應(yīng)用程序運行在用戶空間,兩者不能簡單地使用指針傳遞數(shù)據(jù),因為Linux使用的虛擬內(nèi)存機制,用戶空間的數(shù)據(jù)可能被換出,當(dāng)內(nèi)核空間使用用戶空間
    發(fā)表于 05-08 10:33 ?3485次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>地址映射模型與<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>高端內(nèi)存<b class='flag-5'>詳解</b>

    詳解Linux內(nèi)核搶占實現(xiàn)機制

    本文詳解Linux內(nèi)核搶占實現(xiàn)機制。首先介紹了內(nèi)核搶占和用戶搶占的概念和區(qū)別,接著分析了不可搶占內(nèi)核的特點及實時系統(tǒng)中實現(xiàn)
    發(fā)表于 08-06 06:16

    Linux設(shè)備驅(qū)動開發(fā)詳解:基于最新的Linux 4.0內(nèi)核

    Linux設(shè)備驅(qū)動開發(fā)詳解:基于最新的Linux 4.0內(nèi)核
    發(fā)表于 08-31 12:29

    linux2.6內(nèi)核設(shè)備驅(qū)動模型精華

    linux 內(nèi)核驅(qū)動部分詳解
    發(fā)表于 04-27 10:43 ?20次下載

    Linux設(shè)備驅(qū)動開發(fā)詳解》第4章、Linux內(nèi)核模塊

    Linux設(shè)備驅(qū)動開發(fā)詳解》第4章、Linux內(nèi)核模塊
    發(fā)表于 10-27 14:15 ?0次下載
    《<b class='flag-5'>Linux</b>設(shè)備驅(qū)動開發(fā)<b class='flag-5'>詳解</b>》第4章、<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>模塊

    Linux內(nèi)核配置系統(tǒng)詳解

    隨著 Linux 操作系統(tǒng)的廣泛應(yīng)用,特別是 Linux 在嵌入式領(lǐng)域的發(fā)展,越來越多的人開始投身到 Linux 內(nèi)核級的開發(fā)中。面對日益龐大的 L
    發(fā)表于 11-01 15:45 ?4次下載

    Linux內(nèi)核編譯過程詳解

    Linux內(nèi)核編譯過程詳解(kernel2.6.7) 花了幾天才編譯成功kernel2.6.7,其過程真可謂艱辛。古語有云:苦盡甘來!現(xiàn)在終于可以樂上一陣了。由于許多朋友對操作的順序及某些重要的配置
    發(fā)表于 11-07 11:16 ?4次下載

    linux內(nèi)核rcu機制詳解

    Linux內(nèi)核源碼當(dāng)中,關(guān)于RCU的文檔比較齊全,你可以在 /Documentation/RCU/ 目錄下找到這些文件。Paul E. McKenney 是內(nèi)核中RCU源碼的主要實現(xiàn)者,他也寫了很多RCU方面的文章。今天我們而主
    發(fā)表于 11-13 16:47 ?8809次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>內(nèi)核</b>rcu機制<b class='flag-5'>詳解</b>

    LINUX內(nèi)核信號量設(shè)計與實現(xiàn)

    控制路徑可以睡眠。我們從 LINUX內(nèi)核信號量最直觀的設(shè)計/實現(xiàn)出發(fā),通過一步步改進,揭示在x86平臺上完整的信號量設(shè)計/實現(xiàn),然后探討在不同平臺上通用的
    發(fā)表于 01-14 16:55 ?18次下載

    LINUX內(nèi)核信號量設(shè)計與實現(xiàn)

    控制路徑可以睡眠。我們從 LINUX內(nèi)核信號量最直觀的設(shè)計/實現(xiàn)出發(fā),通過一步步改進,揭示在x86平臺上完整的信號量設(shè)計/實現(xiàn),然后探討在不同平臺上通用的
    發(fā)表于 01-14 16:55 ?5次下載

    Linux內(nèi)核GPIO操作函數(shù)的詳解分析

    本文檔的主要內(nèi)容詳細(xì)介紹的是Linux內(nèi)核GPIO操作函數(shù)的詳解分析免費下載。
    發(fā)表于 01-22 16:58 ?28次下載

    linux內(nèi)核源代碼詳解

     在安裝好的Linux系統(tǒng)中,內(nèi)核的源代碼位于/ust/src/linux.如果是從GNU網(wǎng)站下載的Linux內(nèi)核的tar文件,則展開以后在
    發(fā)表于 09-06 17:01 ?4次下載