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

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

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

鴻蒙內(nèi)核源碼分析:如何安裝和發(fā)送信號?

鴻蒙系統(tǒng)HarmonyOS ? 來源:my.oschina ? 作者:鴻蒙內(nèi)核源碼分析 ? 2021-04-23 17:27 ? 次閱讀

信號生產(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)作.

#include 
int 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

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

    評論

    相關(guān)推薦

    鴻蒙內(nèi)核源碼Task/線程技術(shù)分析

    前言 在鴻蒙內(nèi)核中,廣義上可理解為一個(gè)Task就是一個(gè)線程 一、怎么理解Task 1. 官方文檔是怎么描述線程 基本概念 從系統(tǒng)的角度看,線程是競爭系統(tǒng)資源的最小運(yùn)行單元。線程可以使用或等待CPU
    的頭像 發(fā)表于 10-18 10:42 ?2164次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b>Task/線程技術(shù)<b class='flag-5'>分析</b>

    鴻蒙內(nèi)核源碼分析:用通俗易懂的語言告訴你鴻蒙內(nèi)核發(fā)生了什么?

    查看。內(nèi)存在內(nèi)核的比重極大內(nèi)存模塊占了鴻蒙內(nèi)核約15%代碼量, 近50個(gè)文件,非常復(fù)雜。鴻蒙源碼分析
    發(fā)表于 11-19 10:14

    鴻蒙內(nèi)核源碼分析源碼注釋篇):給HarmonyOS源碼逐行加上中文注釋

    都懂的概念去詮釋或者映射一個(gè)他們從沒聽過的概念.說別人能聽得懂的話這很重要!!! 一個(gè)沒學(xué)過計(jì)算機(jī)知識的賣菜大媽就不可能知道內(nèi)核的基本運(yùn)作了嗎? NO!,筆者在系列篇中試圖用 鴻蒙源碼分析
    發(fā)表于 11-19 10:32

    鴻蒙內(nèi)核源碼分析:給HarmonyOS源碼逐行加上中文注釋

    過計(jì)算機(jī)知識的賣菜大媽就不可能知道內(nèi)核的基本運(yùn)作了嗎? NO!,筆者在系列篇中試圖用 鴻蒙源碼分析系列篇|張大爺系列故事【 CSDN | OSCHINA】 去構(gòu)建這一層級的認(rèn)知,希望能
    發(fā)表于 11-19 15:06

    鴻蒙源碼分析系列(總目錄) | 給HarmonyOS源碼逐行加上中文注釋

    同步更新。鴻蒙源碼分析系列篇|- 鴻蒙內(nèi)核源碼分析
    發(fā)表于 11-20 11:24

    鴻蒙內(nèi)核源碼分析(內(nèi)存概念篇) :手眼通天的虛擬內(nèi)存

    內(nèi)存模塊占了 HarmonyOS 內(nèi)核約15%代碼量, 近20個(gè).c文件,很復(fù)雜。系列篇將用九篇來介紹HarmonyOS內(nèi)存部分,分別是 鴻蒙內(nèi)核源碼
    發(fā)表于 11-20 16:30

    鴻蒙內(nèi)核源碼分析(必讀篇):用故事說內(nèi)核

    本文基于開源鴻蒙內(nèi)核分析,官方源碼【kernel_liteos_a】官方文檔【docs】參考文檔【Huawei LiteOS】本文作者:鴻蒙
    發(fā)表于 11-23 10:15

    鴻蒙內(nèi)核源碼分析(必讀篇)

    本文基于開源鴻蒙內(nèi)核分析,官方源碼【kernel_liteos_a】官方文檔【docs】參考文檔【Huawei LiteOS】本文作者:鴻蒙
    發(fā)表于 11-25 09:28

    HarmonyOS內(nèi)核源碼分析(上)電子書-上線了

    `為方便大家開發(fā)鴻蒙系統(tǒng),小編為大家編輯整理了一本HarmonyOS內(nèi)核源碼分析系列電子書,需要參考學(xué)習(xí)的朋友快來下吧!本電子書主要介紹如何給鴻蒙
    發(fā)表于 11-25 17:13

    鴻蒙內(nèi)核源碼分析(百篇博客分析.挖透鴻蒙內(nèi)核)

    致敬內(nèi)核開發(fā)者感謝開放原子開源基金會,致敬鴻蒙內(nèi)核開發(fā)者??梢院敛豢鋸埖恼f鴻蒙內(nèi)核源碼可作為大學(xué)
    發(fā)表于 07-04 17:16

    為何要精讀鴻蒙內(nèi)核源碼?

    一個(gè)沒學(xué)過計(jì)算機(jī)知識的賣菜大媽就不可能知道內(nèi)核的基本運(yùn)作了嗎? 不一定!在系列篇中試圖用 鴻蒙內(nèi)核源碼分析(總目錄)之故事篇 去引導(dǎo)這一層級
    的頭像 發(fā)表于 04-26 15:00 ?1822次閱讀
    為何要精讀<b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b>?

    鴻蒙內(nèi)核源碼分析鴻蒙內(nèi)核的每段匯編代碼解析

    本篇說清楚CPU的工作模式 讀本篇之前建議先讀鴻蒙內(nèi)核源碼分析(總目錄)其他篇. 正如一個(gè)互聯(lián)網(wǎng)項(xiàng)目的后臺管理系統(tǒng)有權(quán)限管理一樣,CPU工作是否也有權(quán)限(模式)? 一個(gè)成熟的軟硬件架構(gòu)
    的頭像 發(fā)表于 03-02 09:56 ?4274次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b><b class='flag-5'>分析</b>:<b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b>的每段匯編代碼解析

    鴻蒙內(nèi)核源碼分析: 虛擬內(nèi)存和物理內(nèi)存是怎么管理的

    有了上篇鴻蒙內(nèi)核源碼分析(內(nèi)存概念篇)的基礎(chǔ),本篇講內(nèi)存管理部分,本章源碼超級多,很燒腦,但筆者關(guān)鍵處都加了注釋。廢話不多說,開始吧。內(nèi)存一
    發(fā)表于 11-23 11:45 ?19次下載
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b><b class='flag-5'>分析</b>: 虛擬內(nèi)存和物理內(nèi)存是怎么管理的

    鴻蒙內(nèi)核源碼分析內(nèi)核最重要結(jié)構(gòu)體

    為何鴻蒙內(nèi)核源碼分析系列開篇就說 LOS_DL_LIST ? 因?yàn)樗?b class='flag-5'>鴻蒙 LOS 內(nèi)核中無處
    發(fā)表于 11-24 17:54 ?35次下載
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>源碼</b><b class='flag-5'>分析</b> :<b class='flag-5'>內(nèi)核</b>最重要結(jié)構(gòu)體

    華為鴻蒙系統(tǒng)內(nèi)核源碼分析上冊

    鴻蒙內(nèi)核源碼注釋中文版【 Gitee倉】給 Harmoηy○S源碼逐行加上中文注解,詳細(xì)闡述設(shè)計(jì)細(xì)節(jié),助你快速精讀 Harmonyos內(nèi)核源碼
    發(fā)表于 04-09 14:40 ?17次下載