信號(hào)生產(chǎn)
關(guān)于信號(hào)篇,本只想寫(xiě)一篇,但發(fā)現(xiàn)把它想簡(jiǎn)單了,內(nèi)容不多,難度極大.整理了好長(zhǎng)時(shí)間,理解了為何<<深入理解linux內(nèi)核>>要單獨(dú)為它開(kāi)一章,原因有二
信號(hào)相關(guān)的結(jié)構(gòu)體多,而且還容易搞混.所以看本篇要注意結(jié)構(gòu)體的名字和作用.
系統(tǒng)調(diào)用太多了,涉及面廣,信號(hào)的來(lái)源分硬件和軟件.相當(dāng)于軟中斷和硬中斷,這就會(huì)涉及到匯編代碼,但信號(hào)的處理函數(shù)又在用戶空間,CPU是禁止內(nèi)核態(tài)執(zhí)行用戶態(tài)代碼的,所以運(yùn)行過(guò)程需在用戶空間和內(nèi)核空間來(lái)回的折騰,頻繁的切換上下文.
信號(hào)思想來(lái)自Unix,在發(fā)展了50年之后,許多方面都沒(méi)有發(fā)生太大的變化.信號(hào)可以由內(nèi)核產(chǎn)生,也可以由用戶進(jìn)程產(chǎn)生,并由內(nèi)核傳送給特定的進(jìn)程或線程(組),若這個(gè)進(jìn)程定義了自己的信號(hào)處理程序,則調(diào)用這個(gè)程序去處理信號(hào),否則則執(zhí)行默認(rèn)的程序或者忽略.
信號(hào)為系統(tǒng)提供了一種進(jìn)程間異步通訊的方式,一個(gè)進(jìn)程不必通過(guò)任何操作來(lái)等待信號(hào)的到達(dá)。事實(shí)上,進(jìn)程也不可能知道信號(hào)到底什么時(shí)候到達(dá)。一般來(lái)說(shuō),只需用戶進(jìn)程提供信號(hào)處理函數(shù),內(nèi)核會(huì)想方設(shè)法調(diào)用信號(hào)處理函數(shù),網(wǎng)上查閱了很多的關(guān)于信號(hào)的資料.個(gè)人想換個(gè)視角去看信號(hào).把異步過(guò)程理解為生產(chǎn)者(安裝和發(fā)送信號(hào))和消費(fèi)者(捕捉和處理信號(hào))兩個(gè)過(guò)程.鑒于此,系列篇將分成兩篇說(shuō)明,本篇為信號(hào)生產(chǎn)篇
信號(hào)分類(lèi)
每個(gè)信號(hào)都有一個(gè)名字和編號(hào),這些名字都以SIG開(kāi)頭,例如SIGQUIT、SIGCHLD等等。 信號(hào)定義在signal.h頭文件中,信號(hào)名都定義為正整數(shù)。 具體的信號(hào)名稱(chēng)可以使用kill -l來(lái)查看信號(hào)的名字以及序號(hào),信號(hào)是從1開(kāi)始編號(hào)的,不存在0號(hào)信號(hào)。不過(guò)kill對(duì)于信號(hào)0有特殊的應(yīng)用。啥用呢? 可用來(lái)查詢進(jìn)程是否還在. 敲下kill 0 pid就知道了.
信號(hào)分為兩大類(lèi):可靠信號(hào)與不可靠信號(hào),前32種信號(hào)為不可靠信號(hào),后32種為可靠信號(hào)。
不可靠信號(hào): 也稱(chēng)為非實(shí)時(shí)信號(hào),不支持排隊(duì),信號(hào)可能會(huì)丟失, 比如發(fā)送多次相同的信號(hào), 進(jìn)程只能收到一次. 信號(hào)值取值區(qū)間為1~31;
可靠信號(hào): 也稱(chēng)為實(shí)時(shí)信號(hào),支持排隊(duì), 信號(hào)不會(huì)丟失, 發(fā)多少次, 就可以收到多少次. 信號(hào)值取值區(qū)間為32~64
#define SIGHUP 1 //終端掛起或者控制進(jìn)程終止 #define SIGINT 2 //鍵盤(pán)中斷(ctrl + c) #define SIGQUIT 3 //鍵盤(pán)的退出鍵被按下 #define SIGILL 4 //非法指令 #define SIGTRAP 5 //跟蹤陷阱(trace trap),啟動(dòng)進(jìn)程,跟蹤代碼的執(zhí)行 #define SIGABRT 6 //由abort(3)發(fā)出的退出指令 #define SIGIOT SIGABRT //abort發(fā)出的信號(hào) #define SIGBUS 7 //總線錯(cuò)誤 #define SIGFPE 8 //浮點(diǎn)異常 #define SIGKILL 9 //常用的命令 kill 9 123 | 不能被忽略、處理和阻塞 #define SIGUSR1 10 //用戶自定義信號(hào)1 #define SIGSEGV 11 //無(wú)效的內(nèi)存引用, 段違例(segmentation violation),進(jìn)程試圖去訪問(wèn)其虛地址空間以外的位置 #define SIGUSR2 12 //用戶自定義信號(hào)2 #define SIGPIPE 13 //向某個(gè)非讀管道中寫(xiě)入數(shù)據(jù) #define SIGALRM 14 //由alarm(2)發(fā)出的信號(hào),默認(rèn)行為為進(jìn)程終止 #define SIGTERM 15 //終止信號(hào) #define SIGSTKFLT 16 //棧溢出 #define SIGCHLD 17 //子進(jìn)程結(jié)束信號(hào) #define SIGCONT 18 //進(jìn)程繼續(xù)(曾被停止的進(jìn)程) #define SIGSTOP 19 //終止進(jìn)程 | 不能被忽略、處理和阻塞 #define SIGTSTP 20 //控制終端(tty)上 按下停止鍵 #define SIGTTIN 21 //進(jìn)程停止,后臺(tái)進(jìn)程企圖從控制終端讀 #define SIGTTOU 22 //進(jìn)程停止,后臺(tái)進(jìn)程企圖從控制終端寫(xiě) #define SIGURG 23 //I/O有緊急數(shù)據(jù)到達(dá)當(dāng)前進(jìn)程 #define SIGXCPU 24 //進(jìn)程的CPU時(shí)間片到期 #define SIGXFSZ 25 //文件大小的超出上限 #define SIGVTALRM 26 //虛擬時(shí)鐘超時(shí) #define SIGPROF 27 //profile時(shí)鐘超時(shí) #define SIGWINCH 28 //窗口大小改變 #define SIGIO 29 //I/O相關(guān) #define SIGPOLL 29 // #define SIGPWR 30 //電源故障,關(guān)機(jī) #define SIGSYS 31 //系統(tǒng)調(diào)用中參數(shù)錯(cuò),如系統(tǒng)調(diào)用號(hào)非法 #define SIGUNUSED SIGSYS//不使用 #define _NSIG 65
信號(hào)來(lái)源
信號(hào)來(lái)源分為硬件類(lèi)和軟件類(lèi):
硬件類(lèi)
用戶輸入:比如在終端上按下組合鍵ctrl+C,產(chǎn)生SIGINT信號(hào);
硬件異常:CPU檢測(cè)到內(nèi)存非法訪問(wèn)等異常,通知內(nèi)核生成相應(yīng)信號(hào),并發(fā)送給發(fā)生事件的進(jìn)程;
軟件類(lèi)
通過(guò)系統(tǒng)調(diào)用,發(fā)送signal信號(hào):kill(),raise(),sigqueue(),alarm(),setitimer(),abort()
kill命令就是一個(gè)發(fā)送信號(hào)的工具,用于向進(jìn)程或進(jìn)程組發(fā)送信號(hào).例如:kill 9 PID(SIGKILL)來(lái)殺死PID進(jìn)程.
sigqueue():只能向一個(gè)進(jìn)程發(fā)送信號(hào),不能向進(jìn)程組發(fā)送信號(hào);主要針對(duì)實(shí)時(shí)信號(hào)提出,與sigaction()組合使用,當(dāng)然也支持非實(shí)時(shí)信號(hào)的發(fā)送;
alarm():用于調(diào)用進(jìn)程指定時(shí)間后發(fā)出SIGALARM信號(hào);
setitimer():設(shè)置定時(shí)器,計(jì)時(shí)達(dá)到后給進(jìn)程發(fā)送SIGALRM信號(hào),功能比alarm更強(qiáng)大;
abort():向進(jìn)程發(fā)送SIGABORT信號(hào),默認(rèn)進(jìn)程會(huì)異常退出。
raise():用于向進(jìn)程自身發(fā)送信號(hào);
信號(hào)與進(jìn)程的關(guān)系
主要是通過(guò)系統(tǒng)調(diào)用sigaction將用戶態(tài)信號(hào)處理函數(shù)注冊(cè)到PCB保存.所有進(jìn)程的任務(wù)都共用這個(gè)信號(hào)注冊(cè)函數(shù)sigHandler,在信號(hào)的消費(fèi)階段內(nèi)核用一種特殊的方式'回調(diào)'它.
typedef struct ProcessCB {//PCB中關(guān)于信號(hào)的信息 UINTPTR sigHandler; /**< signal handler */ //捕捉信號(hào)后的處理函數(shù) sigset_t sigShare; /**< signal share bit */ //信號(hào)共享位,64個(gè)信號(hào)各站一位 }LosProcessCB; typedef unsigned _Int64 sigset_t; //一個(gè)64位的變量,每個(gè)信號(hào)代表一位. struct sigaction {//信號(hào)處理機(jī)制結(jié)構(gòu)體 union { void (*sa_handler)(int); //信號(hào)處理函數(shù)——普通版 void (*sa_sigaction)(int, siginfo_t *, void *);//信號(hào)處理函數(shù)——高級(jí)版 } __sa_handler; sigset_t sa_mask;//指定信號(hào)處理程序執(zhí)行過(guò)程中需要阻塞的信號(hào); int sa_flags; //標(biāo)示位 // SA_RESTART:使被信號(hào)打斷的syscall重新發(fā)起。 // SA_NOCLDSTOP:使父進(jìn)程在它的子進(jìn)程暫停或繼續(xù)運(yùn)行時(shí)不會(huì)收到 SIGCHLD 信號(hào)。 // SA_NOCLDWAIT:使父進(jìn)程在它的子進(jìn)程退出時(shí)不會(huì)收到SIGCHLD信號(hào),這時(shí)子進(jìn)程如果退出也不會(huì)成為僵 尸進(jìn)程。 // SA_NODEFER:使對(duì)信號(hào)的屏蔽無(wú)效,即在信號(hào)處理函數(shù)執(zhí)行期間仍能發(fā)出這個(gè)信號(hào)。 // SA_RESETHAND:信號(hào)處理之后重新設(shè)置為默認(rèn)的處理方式。 // SA_SIGINFO:使用sa_sigaction成員而不是sa_handler作為信號(hào)處理函數(shù)。 void (*sa_restorer)(void); }; typedef struct sigaction sigaction_t;
解讀
每個(gè)信號(hào)都對(duì)應(yīng)一個(gè)位. 信號(hào)從1開(kāi)始編號(hào) [1 ~ 64] 對(duì)應(yīng)sigShare的[0 ~ 63]位,所以中間會(huì)差一個(gè).記住這點(diǎn),后續(xù)代碼會(huì)提到.
sigHandler信號(hào)處理函數(shù)的注冊(cè)過(guò)程,由系統(tǒng)調(diào)用sigaction(用戶空間) ->OsSigAction(內(nèi)核空間)完成綁定動(dòng)作.
#includeint sigaction(int signo, const struct sigaction *act, struct sigaction *oact); int OsSigAction(int sig, const sigaction_t *act, sigaction_t *oact) { UINTPTR addr; sigaction_t action; if (!GOOD_SIGNO(sig) || sig < 1 || act == NULL) { return -EINVAL; } //將數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間 if (LOS_ArchCopyFromUser(&action, act, sizeof(sigaction_t)) != LOS_OK) { return -EFAULT; } if (sig == SIGSYS) {//鴻蒙此處通過(guò)錯(cuò)誤的系統(tǒng)調(diào)用 來(lái)安裝信號(hào)處理函數(shù),有點(diǎn)巧妙. addr = OsGetSigHandler();//獲取進(jìn)程信號(hào)處理函數(shù) if (addr == 0) {//進(jìn)程沒(méi)有設(shè)置信號(hào)處理函數(shù)時(shí) OsSetSigHandler((unsigned long)(UINTPTR)action.sa_handler);//設(shè)置進(jìn)程信號(hào)處理函數(shù)——普通版 return LOS_OK; } return -EINVAL; } return LOS_OK; }
sigaction(...)第一個(gè)參數(shù)是要安裝的信號(hào); 第二個(gè)參數(shù)與sigaction函數(shù)同名的結(jié)構(gòu)體,這里會(huì)讓人很懵,函數(shù)名和結(jié)構(gòu)體一直,沒(méi)明白為毛要這么搞? 結(jié)構(gòu)體內(nèi)定義了信號(hào)處理方法;第三個(gè)為輸出參數(shù),將信號(hào)的當(dāng)前的sigaction結(jié)構(gòu)帶回.但鴻蒙顯然沒(méi)有認(rèn)真對(duì)待第三個(gè)參數(shù).把musl實(shí)現(xiàn)給閹割了.
對(duì)結(jié)構(gòu)體的sigaction鴻蒙目前只支持信號(hào)處理函數(shù)——普通版,sa_handler表示自定義信號(hào)處理函數(shù),該函數(shù)返回值為void,可以帶一個(gè)int參數(shù),通過(guò)參數(shù)可以得知當(dāng)前信號(hào)的編號(hào),這樣就可以用同一個(gè)函數(shù)處理多種信號(hào)。
sa_mask指定信號(hào)處理程序執(zhí)行過(guò)程中需要阻塞的信號(hào)。
sa_flags字段包含一些選項(xiàng),具體看注釋
sa_sigaction是實(shí)時(shí)信號(hào)的處理函數(shù),union二選一.鴻蒙暫時(shí)不支持這種方式.
信號(hào)與任務(wù)的關(guān)系
typedef struct {//TCB中關(guān)于信號(hào)的信息 sig_cb sig; //信號(hào)控制塊,用于異步通信,類(lèi)似于 linux singal模塊 } LosTaskCB; typedef struct {//信號(hào)控制塊(描述符) sigset_t sigFlag; //不屏蔽的信號(hào)標(biāo)簽集 sigset_t sigPendFlag; //信號(hào)阻塞標(biāo)簽集,記錄因哪些信號(hào)被阻塞 sigset_t sigprocmask; /* Signals that are blocked */ //進(jìn)程屏蔽了哪些信號(hào) sq_queue_t sigactionq; //信號(hào)捕捉隊(duì)列 LOS_DL_LIST waitList; //等待鏈表,上面掛的是等待信號(hào)到來(lái)的任務(wù), 可查找 OsTaskWait(&sigcb->waitList, timeout, TRUE) 理解 sigset_t sigwaitmask; /* Waiting for pending signals */ //任務(wù)在等待阻塞信號(hào) siginfo_t sigunbinfo; /* Signal info when task unblocked */ //任務(wù)解鎖時(shí)的信號(hào)信息 sig_switch_context context; //信號(hào)切換上下文, 用于保存切換現(xiàn)場(chǎng), 比如發(fā)生系統(tǒng)調(diào)用時(shí)的返回,涉及同一個(gè)任務(wù)的兩個(gè)棧進(jìn)行切換 } sig_cb;
解讀
系列篇已多次說(shuō)過(guò),進(jìn)程只是管理資源的容器,真正讓cpu干活的是任務(wù)task,所以發(fā)給進(jìn)程的信號(hào)最終還是需要分發(fā)給具體任務(wù)來(lái)處理.所以能想到的是關(guān)于任務(wù)部分會(huì)更復(fù)雜.
context信號(hào)處理很復(fù)雜的原因在于信號(hào)的發(fā)起在用戶空間,發(fā)送需要系統(tǒng)調(diào)用,而處理信號(hào)的函數(shù)又是用戶空間提供的, 所以需要反復(fù)的切換任務(wù)上下文.而且還有硬中斷的問(wèn)題,比如 ctrl + c ,需要從硬中斷中回調(diào)用戶空間的信號(hào)處理函數(shù),處理完了再回到內(nèi)核空間,最后回到用戶空間.沒(méi)聽(tīng)懂吧,我自己都說(shuō)暈了,所以需要專(zhuān)門(mén)的一篇來(lái)說(shuō)清楚信號(hào)的處理問(wèn)題.本篇不展開(kāi)說(shuō).
sig_cb結(jié)構(gòu)體是任務(wù)處理信號(hào)的結(jié)構(gòu)體,要響應(yīng),屏蔽哪些信號(hào)等等都由它完成,這個(gè)結(jié)構(gòu)體雖不復(fù)雜,但是很繞,很難搞清楚它們之間的區(qū)別.筆者是經(jīng)過(guò)一番痛苦的閱讀理解后才明白各自的含義.并想通過(guò)用打比方的例子試圖讓大家明白.
以下用追女孩打比方理解.任務(wù)相當(dāng)于某個(gè)男,沒(méi)錯(cuò)說(shuō)的就是屏幕前的你,除了苦逼的碼農(nóng)誰(shuí)會(huì)有耐心能堅(jiān)持看到這里.64個(gè)信號(hào)對(duì)應(yīng)64個(gè)女孩.允許一男同時(shí)追多個(gè)女孩,女孩也可同時(shí)被多個(gè)男追.女孩也可以主動(dòng)追男的.理解如下:
waitList等待信號(hào)的任務(wù)鏈表,上面掛的是因等待信號(hào)而被阻塞的任務(wù).眾男在排隊(duì)追各自心愛(ài)的女孩們,處于無(wú)所事事的掛起的狀態(tài),等待女孩們的出現(xiàn).
sigwaitmask任務(wù)在等待的信號(hào)集合,只有這些信號(hào)能喚醒任務(wù).相當(dāng)于列出喜歡的各位女孩,只要出現(xiàn)一位就能讓你滿血復(fù)活.
sigprocmask指任務(wù)對(duì)哪些信號(hào)不感冒.來(lái)了也不處理.相當(dāng)于列出不喜歡的各位女孩,請(qǐng)她們別來(lái)騷擾你,嘚瑟.
sigPendFlag信號(hào)到達(dá)但并未喚醒任務(wù).相當(dāng)于喜歡你的女孩來(lái)追你,但她不在你喜歡的列表內(nèi),結(jié)果是不搭理人家繼續(xù)等喜歡的出現(xiàn).
sigFlag記錄不屏蔽的信號(hào)集合,相當(dāng)于你并不反感的女孩們.記錄來(lái)過(guò)的那些女孩(除掉你不喜歡的).
信號(hào)發(fā)送過(guò)程
用戶進(jìn)程調(diào)用kill()的過(guò)程如下:
kill(pid_t pid, int sig) - 系統(tǒng)調(diào)用 | 用戶空間 --------------------------------------------------------------------------------------- | 內(nèi)核空間 SysKill(...) |---> OsKillLock(...) |---> OsKill(.., OS_USER_KILL_PERMISSION) |---> OsDispatch() //鑒權(quán),向進(jìn)程發(fā)送信號(hào) |---> OsSigProcessSend() //選擇任務(wù)發(fā)送信號(hào) |---> OsSigProcessForeachChild(..,ForEachTaskCB handler,..) |---> SigProcessKillSigHandler() //處理 SIGKILL |---> OsTaskWake() //喚醒所有等待任務(wù) |---> OsSigEmptySet() //清空信號(hào)等待集 |---> SigProcessSignalHandler() |---> OsTcbDispatch() //向目標(biāo)任務(wù)發(fā)送信號(hào) |---> OsTaskWake() //喚醒任務(wù) |---> OsSigEmptySet() //清空信號(hào)等待集
流程
通過(guò) 系統(tǒng)調(diào)用kill陷入內(nèi)核空間
因?yàn)槭怯脩魬B(tài)進(jìn)程,使用OS_USER_KILL_PERMISSION權(quán)限發(fā)送信號(hào)
#define OS_KERNEL_KILL_PERMISSION 0U //內(nèi)核態(tài) kill 權(quán)限 #define OS_USER_KILL_PERMISSION 3U //用戶態(tài) kill 權(quán)限
鑒權(quán)之后進(jìn)程輪詢?nèi)蝿?wù)組,向目標(biāo)任務(wù)發(fā)送信號(hào).這里分三種情況:
SIGKILL信號(hào),將所有等待任務(wù)喚醒,拉入就緒隊(duì)列等待被調(diào)度執(zhí)行,并情況信號(hào)等待集
非SIGKILL信號(hào)時(shí),將通過(guò)sigwaitmask和sigprocmask過(guò)濾,找到一個(gè)任務(wù)向它發(fā)送信號(hào)OsTcbDispatch.
代碼細(xì)節(jié)
int OsKill(pid_t pid, int sig, int permission) { siginfo_t info; int ret; /* Make sure that the para is valid */ if (!GOOD_SIGNO(sig) || pid < 0) {//有效信號(hào) [0,64] return -EINVAL; } if (OsProcessIDUserCheckInvalid(pid)) {//檢查參數(shù)進(jìn)程 return -ESRCH; } /* Create the siginfo structure */ //創(chuàng)建信號(hào)結(jié)構(gòu)體 info.si_signo = sig; //信號(hào)編號(hào) info.si_code = SI_USER; //來(lái)自用戶進(jìn)程信號(hào) info.si_value.sival_ptr = NULL; /* Send the signal */ ret = OsDispatch(pid, &info, permission);//發(fā)送信號(hào) return ret; } //信號(hào)分發(fā) int OsDispatch(pid_t pid, siginfo_t *info, int permission) { LosProcessCB *spcb = OS_PCB_FROM_PID(pid);//找到這個(gè)進(jìn)程 if (OsProcessIsUnused(spcb)) {//進(jìn)程是否還在使用,不一定是當(dāng)前進(jìn)程但必須是個(gè)有效進(jìn)程 return -ESRCH; } #ifdef LOSCFG_SECURITY_CAPABILITY //啟用能力安全模式 LosProcessCB *current = OsCurrProcessGet();//獲取當(dāng)前進(jìn)程 /* If the process you want to kill had been inactive, but still exist. should return LOS_OK */ if (OsProcessIsInactive(spcb)) {//如果要終止的進(jìn)程處于非活動(dòng)狀態(tài),但仍然存在,應(yīng)該返回OK return LOS_OK; } /* Kernel process always has kill permission and user process should check permission *///內(nèi)核進(jìn)程總是有kill權(quán)限,用戶進(jìn)程需要檢查權(quán)限 if (OsProcessIsUserMode(current) && !(current->processStatus & OS_PROCESS_FLAG_EXIT)) {//用戶進(jìn)程檢查能力范圍 if ((current != spcb) && (!IsCapPermit(CAP_KILL)) && (current->user->userID != spcb->user->userID)) { return -EPERM; } } #endif if ((permission == OS_USER_KILL_PERMISSION) && (OsSignalPermissionToCheck(spcb) < 0)) { return -EPERM; } return OsSigProcessSend(spcb, info);//給參數(shù)進(jìn)程發(fā)送信號(hào) } //給參數(shù)進(jìn)程發(fā)送參數(shù)信號(hào) int OsSigProcessSend(LosProcessCB *spcb, siginfo_t *sigInfo) { int ret; struct ProcessSignalInfo info = { .sigInfo = sigInfo, //信號(hào)內(nèi)容 .defaultTcb = NULL, //以下四個(gè)值將在OsSigProcessForeachChild中根據(jù)條件完善 .unblockedTcb = NULL, .awakenedTcb = NULL, .receivedTcb = NULL }; //總之是要從進(jìn)程中找個(gè)至少一個(gè)任務(wù)來(lái)接受這個(gè)信號(hào),優(yōu)先級(jí) //awakenedTcb > receivedTcb > unblockedTcb > defaultTcb /* visit all taskcb and dispatch signal */ //訪問(wèn)所有任務(wù)和分發(fā)信號(hào) if ((info.sigInfo != NULL) && (info.sigInfo->si_signo == SIGKILL)) {//需要干掉進(jìn)程時(shí) SIGKILL = 9, #linux kill 9 14 (void)OsSigProcessForeachChild(spcb, SigProcessKillSigHandler, &info);//進(jìn)程要被干掉了,通知所有task做善后處理 OsSigAddSet(&spcb->sigShare, info.sigInfo->si_signo); OsWaitSignalToWakeProcess(spcb);//等待信號(hào)喚醒進(jìn)程 return 0; } else { ret = OsSigProcessForeachChild(spcb, SigProcessSignalHandler, &info);//進(jìn)程通知所有task處理信號(hào) } if (ret < 0) { return ret; } SigProcessLoadTcb(&info, sigInfo); return 0; } //讓進(jìn)程的每一個(gè)task執(zhí)行參數(shù)函數(shù) int OsSigProcessForeachChild(LosProcessCB *spcb, ForEachTaskCB handler, void *arg) { int ret; /* Visit the main thread last (if present) */ LosTaskCB *taskCB = NULL;//遍歷進(jìn)程的 threadList 鏈表,里面存放的都是task節(jié)點(diǎn) LOS_DL_LIST_FOR_EACH_ENTRY(taskCB, &(spcb->threadSiblingList), LosTaskCB, threadList) {//遍歷進(jìn)程的任務(wù)列表 ret = handler(taskCB, arg);//回調(diào)參數(shù)函數(shù) OS_RETURN_IF(ret != 0, ret);//這個(gè)宏的意思就是只有ret = 0時(shí),啥也不處理.其余就返回 ret } return LOS_OK; }
如果是SIGKILL信號(hào),讓spcb的所有任務(wù)執(zhí)行SigProcessKillSigHandler函數(shù),查看旗下的所有任務(wù)是否又在等待這個(gè)信號(hào)的,如果有就將任務(wù)喚醒,放在就緒隊(duì)列等待被調(diào)度執(zhí)行.
//進(jìn)程收到 SIGKILL 信號(hào)后,通知任務(wù)tcb處理. static int SigProcessKillSigHandler(LosTaskCB *tcb, void *arg) { struct ProcessSignalInfo *info = (struct ProcessSignalInfo *)arg;//轉(zhuǎn)參 if ((tcb != NULL) && (info != NULL) && (info->sigInfo != NULL)) {//進(jìn)程有信號(hào) sig_cb *sigcb = &tcb->sig; if (!LOS_ListEmpty(&sigcb->waitList) && OsSigIsMember(&sigcb->sigwaitmask, info->sigInfo->si_signo)) {//如果任務(wù)在等待這個(gè)信號(hào) OsTaskWake(tcb);//喚醒這個(gè)任務(wù),加入進(jìn)程的就緒隊(duì)列,并不申請(qǐng)調(diào)度 OsSigEmptySet(&sigcb->sigwaitmask);//清空信號(hào)等待位,不等任何信號(hào)了.因?yàn)檫@是SIGKILL信號(hào) } } return 0; }
非SIGKILL信號(hào),讓spcb的所有任務(wù)執(zhí)行SigProcessSignalHandler函數(shù)
static int SigProcessSignalHandler(LosTaskCB *tcb, void *arg) { struct ProcessSignalInfo *info = (struct ProcessSignalInfo *)arg;//先把參數(shù)解出來(lái) int ret; int isMember; if (tcb == NULL) { return 0; } /* If the default tcb is not setted, then set this one as default. */ if (!info->defaultTcb) {//如果沒(méi)有默認(rèn)發(fā)送方的任務(wù),即默認(rèn)參數(shù)任務(wù). info->defaultTcb = tcb; } isMember = OsSigIsMember(&tcb->sig.sigwaitmask, info->sigInfo->si_signo);//任務(wù)是否在等待這個(gè)信號(hào) if (isMember && (!info->awakenedTcb)) {//是在等待,并尚未向該任務(wù)時(shí)發(fā)送信號(hào)時(shí) /* This means the task is waiting for this signal. Stop looking for it and use this tcb. * The requirement is: if more than one task in this task group is waiting for the signal, * then only one indeterminate task in the group will receive the signal. */ ret = OsTcbDispatch(tcb, info->sigInfo);//發(fā)送信號(hào),注意這是給其他任務(wù)發(fā)送信號(hào),tcb不是當(dāng)前任務(wù) OS_RETURN_IF(ret < 0, ret);//這種寫(xiě)法很有意思 /* set this tcb as awakenedTcb */ info->awakenedTcb = tcb; OS_RETURN_IF(info->receivedTcb != NULL, SIG_STOP_VISIT); /* Stop search */ } /* Is this signal unblocked on this thread? */ isMember = OsSigIsMember(&tcb->sig.sigprocmask, info->sigInfo->si_signo);//任務(wù)是否屏蔽了這個(gè)信號(hào) if ((!isMember) && (!info->receivedTcb) && (tcb != info->awakenedTcb)) {//沒(méi)有屏蔽,有喚醒任務(wù)沒(méi)有接收任務(wù). /* if unblockedTcb of this signal is not setted, then set it. */ if (!info->unblockedTcb) { info->unblockedTcb = tcb; } ret = OsTcbDispatch(tcb, info->sigInfo);//向任務(wù)發(fā)送信號(hào) OS_RETURN_IF(ret < 0, ret); /* set this tcb as receivedTcb */ info->receivedTcb = tcb;//設(shè)置這個(gè)任務(wù)為接收任務(wù) OS_RETURN_IF(info->awakenedTcb != NULL, SIG_STOP_VISIT); /* Stop search */ } return 0; /* Keep searching */ }
函數(shù)的意思是,當(dāng)進(jìn)程中有多個(gè)任務(wù)在等待這個(gè)信號(hào)時(shí),發(fā)送信號(hào)給第一個(gè)等待的任務(wù)awakenedTcb.
如果沒(méi)有任務(wù)在等待信號(hào),那就從不屏蔽這個(gè)信號(hào)的任務(wù)集中隨機(jī)找一個(gè)receivedTcb接受信號(hào).
只要不屏蔽unblockedTcb就有值,隨機(jī)的.
如果上面的都不滿足,信號(hào)發(fā)送給defaultTcb.
尋找發(fā)送任務(wù)的優(yōu)先級(jí)是awakenedTcb>receivedTcb>unblockedTcb>defaultTcb
信號(hào)相關(guān)函數(shù)
信號(hào)集操作函數(shù)
sigemptyset(sigset_t *set):信號(hào)集全部清0;
sigfillset(sigset_t *set): 信號(hào)集全部置1,則信號(hào)集包含linux支持的64種信號(hào);
sigaddset(sigset_t *set, int signum):向信號(hào)集中加入signum信號(hào);
sigdelset(sigset_t *set, int signum):向信號(hào)集中刪除signum信號(hào);
sigismember(const sigset_t *set, int signum):判定信號(hào)signum是否存在信號(hào)集中。
信號(hào)阻塞函數(shù)
sigprocmask(int how, const sigset_t *set, sigset_t *oldset)); 不同how參數(shù),實(shí)現(xiàn)不同功能
SIG_BLOCK:將set指向信號(hào)集中的信號(hào),添加到進(jìn)程阻塞信號(hào)集;
SIG_UNBLOCK:將set指向信號(hào)集中的信號(hào),從進(jìn)程阻塞信號(hào)集刪除;
SIG_SETMASK:將set指向信號(hào)集中的信號(hào),設(shè)置成進(jìn)程阻塞信號(hào)集;
sigpending(sigset_t *set)):獲取已發(fā)送到進(jìn)程,卻被阻塞的所有信號(hào);
sigsuspend(const sigset_t *mask)):用mask代替進(jìn)程的原有掩碼,并暫停進(jìn)程執(zhí)行,直到收到信號(hào)再恢復(fù)原有掩碼并繼續(xù)執(zhí)行進(jìn)程。
編輯:hfy
-
cpu
+關(guān)注
關(guān)注
68文章
10827瀏覽量
211174 -
實(shí)時(shí)信號(hào)
+關(guān)注
關(guān)注
0文章
4瀏覽量
5187 -
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2634瀏覽量
66221
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論