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

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

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

一文詳解Linux內(nèi)核-信號的產(chǎn)生過程

jf_0tjVfeJz ? 來源:嵌入式ARM和Linux ? 2024-01-13 13:48 ? 次閱讀

1 `specific_send_sig_info()`函數(shù)

2 send_signal()函數(shù)

3 group_send_sig_info()函數(shù)

許多內(nèi)核函數(shù)產(chǎn)生信號:它們完成信號處理的第一階段,也就是更新一個或多個進程描述符。她們不會直接執(zhí)行第二階段的信號處理,也就是傳遞信號;但是,依賴信號類型和目標進程的狀態(tài),可能會喚醒某些進程并強制它們接收信號。

信號的發(fā)送,可以是內(nèi)核,也可以是其它進程,內(nèi)核使用下表中的函數(shù)產(chǎn)生信號。

表11-9 為進程產(chǎn)生信號的內(nèi)核函數(shù)

函數(shù)名稱 描述
send_sig() 給單個進程發(fā)送信號
send_sig_info() 與send_sig()類似,在siginfo_t中帶有擴展信息
force_sig() 發(fā)送不能被進程顯式忽略或阻塞的信號
force_sig_info() 類似force_sig(),在siginfo_t中帶有擴展信息
force_sig_specific() 類似force_sig(),但是針對SIGSTOP和SIGKILL信號進行了優(yōu)化
sys_tkill() tkill()系統(tǒng)調(diào)用處理程序
sys_tgkill() tgkill()系統(tǒng)調(diào)用處理程序

表格11-9中所有函數(shù)最后都會調(diào)用specific_send_sig_info()函數(shù),后面會介紹。

發(fā)送到整個線程組的信號,可以來自內(nèi)核或其它進程,產(chǎn)生信號的函數(shù)如下表所示。

表11-10 為線程組產(chǎn)生信號的內(nèi)核函數(shù)

函數(shù)名稱 描述
send_group_sig_info() 發(fā)送信號給線程組,由線程組中的某個進程描述符標識
kill_pg() 發(fā)送信號給進程組中所有線程組(參加第1章的進程管理一節(jié))
kill_pg_info() 類似kill_pg(),只是siginfo_t帶有擴展信息
kill_proc() 發(fā)送信號給線程組,由線程組中的某個進程PID標識
kill_proc_info() 類似kill_proc(),只是siginfo_t帶有擴展信息
sys_kill() kill()系統(tǒng)調(diào)用的處理程序(參見后面與信號處理有關(guān)的系統(tǒng)調(diào)用)
sys_rt_sigqueueinfo() rt_sigqueueinfo()系統(tǒng)調(diào)用的處理程序

上表中的函數(shù)最終調(diào)用group_send_sig_info()更新進程描述符,函數(shù)會在group_send_sig_info()函數(shù)一節(jié)中介紹。

1 specific_send_sig_info()函數(shù)

specific_send_sig_info()函數(shù)可以發(fā)送信號到具體的進程。作用于3個參數(shù)

sig

信號編號。

info

既可以是siginfo_t表的地址,也可以是3個特殊值:0意味著用戶進程發(fā)送的信號;1意味著內(nèi)核發(fā)送的信號;2意味著內(nèi)核發(fā)送的信號,且信號是SIGSTOP或SIGKILL。

t

目標進程描述符的指針。

specific_send_sig_info()必須在本地中斷禁止且申請了t->sighand->siglock自旋鎖的情況下調(diào)用。執(zhí)行步驟如下:

檢查進程是否忽略信號;如果是,則返回0(不用產(chǎn)生信號)。滿足下面3個條件,信號即會被忽略:

進程沒有被跟蹤(t->ptrace中PT_PTRACED標志清除)

信號沒被阻塞sigismember(&t->blocked, sig) returns 0

信號顯式忽略(t->sighand->action[sig-1]中的sa_handler字段等于SIG_IGN)或隱式忽略(sa_handler字段等于SIG_DFL并且信號是SIGCONT、SIGCHLD、SIGWINCH或SIGURG)

檢查信號是否是非實時(sig<32),相同信號是否已經(jīng)在進程的私有掛起信號隊列(sigismember(&t->pending.signal,sig) returns 1):如果確定,什么也不做,然后返回0。

調(diào)用send_signal(sig, info, t, &t->pending)將信號添加到進程的掛起信號集中;詳細描述如下所示:

如果send_signal()成功終止,且信號也沒有被阻塞(sigismember(&t->blocked,sig) returns 0),然后調(diào)用signal_wake_up()函數(shù)通知進程新的掛起信號。因此,函數(shù)執(zhí)行如下步驟:

在t->thread_info->flags中設置TIF_SIGPENDING標志。

調(diào)用try_to_wake_up()喚醒進程(這些進程處于TASK_INTERRUPTIBLE或TASK_STOPPED狀態(tài),且信號是SIGKILL),具體可以參考第7章的try_to_wake_up()函數(shù)一節(jié)。

如果try_to_wake_up()返回0,進程喚醒并可運行:如果是,檢查該進程是否在其它CPU上正在運行,這種情況下,發(fā)送一個核間中斷給那個CPU,強制重新調(diào)度當前進程。(參考第4章的核間中斷處理一節(jié))因為當從schedule()函數(shù)返回時,每個進程都會檢查掛起信號,核間中斷確保目標進程快速注意到新的掛起信號。

信號產(chǎn)生成功,則返回1。

2 send_signal()函數(shù)

send_signal()負責插入掛起信號隊列中。接收參數(shù):信號sig,數(shù)據(jù)結(jié)構(gòu)siginfo_t中info的地址(或具體編碼值,參考前面的specific_send_sig_info()描述,目標進程描述符地址t,掛起信號隊列signals的地址。

函數(shù)執(zhí)行如下內(nèi)容:

如果info等于2,信號是SIGKILL或SIGSTOP且是內(nèi)核通過force_sig_specific()函數(shù)產(chǎn)生的:這種情況直接跳轉(zhuǎn)到第9步。與這些信號相對應的動作由內(nèi)核立即強制執(zhí)行,因此該函數(shù)可能會跳過將信號添加到掛起信號隊列中。(如果是特殊信號,比如殺死、停止進程,則直接執(zhí)行,不再走信號處理的通用流程)

如果進程擁有者的掛起信號的數(shù)量(t->user->sigpending)小于當前進程資源限制(t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur),函數(shù)就會為新信號分配sigqueue數(shù)據(jù)結(jié)構(gòu):

q=kmem_cache_alloc(sigqueue_cachep,GFP_ATOMIC);

如果掛起信號的數(shù)量太多或前一步內(nèi)存分配失敗,則跳轉(zhuǎn)第9步。

掛起信號數(shù)量(t->user->sigpending)和每個用戶數(shù)據(jù)結(jié)構(gòu)的引用計數(shù)器(t->user)增加。

添加sigqueue數(shù)據(jù)結(jié)構(gòu)到掛起信號隊列(signals)中:

list_add_tail(&q->list,&signals->list);

完善sigqueue數(shù)據(jù)結(jié)構(gòu)中的siginfo_t表:

if((unsignedlong)info==0){
q->info.si_signo=sig;
q->info.si_errno=0;
q->info.si_code=SI_USER;
q->info._sifields._kill._pid=current->pid;
q->info._sifields._kill._uid=current->uid;
}elseif((unsignedlong)info==1){
q->info.si_signo=sig;
q->info.si_errno=0;
q->info.si_code=SI_KERNEL;
q->info._sifields._kill._pid=0;
q->info._sifields._kill._uid=0;
}else
copy_siginfo(&q->info,info);

copy_siginfo()函數(shù)將調(diào)用者傳遞的siginfo_t表進行拷貝。

設置隊列位掩碼中信號對應的位:

sigaddset(&signals->signal,sig);

信號成功添加到掛起信號隊列中,返回0。

該步驟主要是處理信號無法添加到信號掛起隊列中的情況,比如,已經(jīng)有太多掛起信號,或沒有內(nèi)存分配sigqueue,或者信號由內(nèi)核立即強制執(zhí)行。如果信號是實時的,且是有內(nèi)核函數(shù)發(fā)送并明確要求添加到隊列中時,該函數(shù)返回錯誤碼-EAGAIN:

if(sig>=32&&info&&(unsignedlong)info!=1&&
info->si_code!=SI_USER)
return-EAGAIN;

設置隊列位掩碼中信號對應的位:

sigaddset(&signals->signal,sig);

返回0:即使信號沒有被添加到隊列中,對應的位也已經(jīng)在掛起信號隊列中位掩碼中設置相應位。

即使掛起的信號隊列中沒有空間容納相應的項,仍然讓目標進程接收信號是很重要的。例如,假設一個進程正在消耗過多的內(nèi)存。內(nèi)核必須確保kill()成功,即使沒有可用內(nèi)存;否則,系統(tǒng)管理員沒有任何機會通過終止違規(guī)進程來恢復系統(tǒng)。

3 group_send_sig_info()函數(shù)

group_send_sig_info()函數(shù)發(fā)送信號給整個線程組。它有三個參數(shù):信號sig,siginfo_t表地址(或者具體值0,1或2),和進程描述符的地址p。

該函數(shù)執(zhí)行的大概步驟如下:

檢查sig是否正確:

if(sig64)
return-EINVAL;

如果信號是由用戶進程發(fā)送的,則檢查該操作是否被允許。只有滿足以下條件之一,信號才會被發(fā)送:

如果用戶進程不被允許發(fā)送信號,則返回-EPERM。

發(fā)送進程的所有者具有適當?shù)臋?quán)限(通常,這僅僅意味著信號是由系統(tǒng)管理員發(fā)出的,參見第20章)。

信號是SIGCONT,目標進程與發(fā)送進程處于相同的登錄會話中。

兩個進程屬于同一個用戶。

如果sig等于0,則立即返回,不會產(chǎn)生任何信號:

if(!sig||!p->sighand)
return0;

因為0不是有效的信號數(shù)字,所以它用于允許發(fā)送進程檢查它是否具有向目標線程組發(fā)送信號所需的特權(quán)。如果目標進程被殺死(通過檢查其信號處理程序是否被釋放進行判斷),該函數(shù)也會返回。

申請p->sighand->siglock自旋鎖并禁止本地中斷。

調(diào)用handle_stop_signal()函數(shù),檢查某些類型的信號,這些信號可能使目標線程組中的其它掛起信號失效。

該函數(shù)執(zhí)行如下步驟:

a. 如果線程組被殺死(信號描述符中flags字段的SIGNAL_GROUP_EXIT標志被設置),立即返回。

b. 如果sig是SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU信號,該函數(shù)會調(diào)用rm_from_queue()函數(shù)從共享掛起信號隊列p->signal->shared_pending和線程組中所有成員的私有隊列中移除SIGCONT信號。

c. 如果sig是SIGCONT,該函數(shù)會調(diào)用rm_from_queue()函數(shù)從共享掛起信號隊列p->signal->shared_pending中移除;然后,將相同的信號從線程組進程的私有掛起信號隊列中移除,并喚醒他們:

rm_from_queue(0x003c0000,&p->signal->shared_pending);
t=p;
do{
rm_from_queue(0x003c0000,&t->pending);
try_to_wake_up(t,TASK_STOPPED,0);
t=next_thread(t);
}while(t!=p);

掩碼0x003c0000選擇了四個停止信號。每次迭代,next_thread宏返回線程組中一個不同的輕量級進程的描述符地址(參考第3章的進程之間的關(guān)系)。

實際的代碼要遠比上面的代碼片段復雜,因為handle_stop_signal()還處理捕獲SIGCONT信號的異常情況,以及在線程組中的所有進程都停止時由于SIGCONT信號發(fā)生而導致的競態(tài)條件。

檢查線程組是否忽略該信號,如果忽略,則返回成功(0)。滿足忽略信號的三個條件即可,可以參考specific_send_sig_info()函數(shù)的介紹。

檢查信號是否為非實時信號,且同一個信號是否已經(jīng)在線程組的共享掛起信號隊列中掛起:如果掛起,什么也不用做,返回成功即可(0):

if(sig<32?&&?sigismember(&p->signal->shared_pending.signal,sig))
return0;

通過以上檢查,則調(diào)用send_signal()將信號添加到共享掛起信號隊列中。如果send_signal()返回非零錯誤碼,則將該錯誤碼返回并終止執(zhí)行。

調(diào)用__group_complete_signal()函數(shù)喚醒線程組中的一個輕量級進程。

釋放p->sighand->siglock自旋鎖,且使能本地中斷。

返回成功(0)。

__group_complete_signal()函數(shù)會掃描線程組中的進程,查找可以接受新信號的進程。前提是滿足一下條件:

該進程不會阻塞信號

該進程沒有處于EXIT_ZOMBIE、EXIT_DEAD、TASK_TRACED或TASK_STOPPED(特例是,如果該信號是SIGKILL,則進程可以處于TASK_TRACED和TASK_STOPPED狀態(tài)中)

進程沒有被殺死,也就是沒有設置PF_EXITING標志

進程當前正在某個CPU核上執(zhí)行,或者它的TIF_SIGPENDING標志尚未設置。(事實上,喚醒一個有掛起信號的進程沒有意義的:一般來說,這個操作已經(jīng)由設置了TIF_SIGPENDING標志的內(nèi)核控制路徑執(zhí)行了。另一方面,如果進程當前處于執(zhí)行中,它應該收到新的掛起信號的通知。

線程組可能包含許多滿足條件的進程。該函數(shù)選擇其中一個:

如果進程(由函數(shù)group_send_sig_info()傳遞的進程描述符參數(shù)p標識)滿足前述所有條件并可以接收信號,則函數(shù)選擇它。

否則,該函數(shù)從接收到最后一個線程組信號的進程開始(p->signal->curr_target),通過掃描線程組的成員選擇一個合適的進程。

如果__group_complete_signal()成功找到一個合適的進程,它將設置信號傳遞到的進程。首先,該函數(shù)會檢查信號是否致命:這種情況下,向線程組中的每個輕量級進程發(fā)送SIGKILL信號來殺死整個線程組。如果信號不是致命的:則該函數(shù)調(diào)用signal_wake_up()來通知所選進程它有一個新的掛起信號(參見前面章節(jié)的specific_send_sig_info()函數(shù)中的第4步)。







審核編輯:劉清

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

    關(guān)注

    48

    文章

    968

    瀏覽量

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

    關(guān)注

    1

    文章

    315

    瀏覽量

    21556

原文標題:Linux內(nèi)核-信號的產(chǎn)生過程

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

收藏 人收藏

    評論

    相關(guān)推薦

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

    本文簡單介紹下Linux信號處理機制,為介紹二進制翻譯下信號處理機制做個鋪墊。 本文主要參考書目《Linux
    的頭像 發(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>是如何處理的?看完全懂了……

    TTL和CMOS中△I噪聲的產(chǎn)生過程與基本特點分析

    隨著數(shù)字電路向高集成度、高性能、高速度、低工作電壓、低功耗等方向發(fā)展,數(shù)字電路中的△I噪聲正逐步成為數(shù)字系統(tǒng)的主要噪聲源之,因此研究△I噪聲的產(chǎn)生過程與基本特點,對認識△I噪聲特性進而抑制△I噪聲具有實際意義。
    發(fā)表于 09-19 09:52 ?4983次閱讀
    TTL和CMOS中△I噪聲的<b class='flag-5'>產(chǎn)生過程</b>與基本特點分析

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

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

    Linux內(nèi)核的編譯主要過程

    Linux內(nèi)核的編譯主要過程: 配置、編譯、安裝 。
    發(fā)表于 08-08 16:02 ?659次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>的編譯主要<b class='flag-5'>過程</b>

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

    信號和多線程程序 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/
    的頭像 發(fā)表于 01-13 09:40 ?1208次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>中<b class='flag-5'>信號</b><b class='flag-5'>詳解</b>

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

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

    信號振鈴的產(chǎn)生過程

    前面講過,如果信號傳輸過程中感受到阻抗的變化,就會發(fā)生信號的反射。這個信號可能是驅(qū)動端發(fā)出的信號,也可能是遠端反射回來的反射
    發(fā)表于 05-29 08:18

    詳解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設備驅(qū)動開發(fā)詳解:基于最新的Linux 4.0內(nèi)核

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

    linux內(nèi)核啟動內(nèi)核解壓過程分析

    linux啟動時內(nèi)核解壓過程分析,份不錯的文檔,深入了解內(nèi)核必備
    發(fā)表于 03-09 13:39 ?1次下載

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

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

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

    詳解Linux內(nèi)核測試現(xiàn)狀

    is going on there? Is stable kernel really stable? 剛好今年9月在洛杉磯舉辦的《Linux Plumbers Conference》有個BOF(birds
    的頭像 發(fā)表于 01-01 09:06 ?3094次閱讀

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

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