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