當(dāng)前位置:首頁(yè) > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 學(xué)習(xí)筆記 > 嵌入式學(xué)習(xí)筆記:linux進(jìn)程通信之信號(hào)函數(shù)
嵌入式學(xué)習(xí)筆記:linux進(jìn)程通信之信號(hào)函數(shù)
時(shí)間:2018-09-26 來(lái)源:未知
1、信號(hào)注冊(cè)函數(shù): signal
#include
void (*signal(int signum, void (*sighandler_t)(int))) (int); typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
入?yún)ⅲ?/p>
signum 哪個(gè)信號(hào)
handle 信號(hào)所對(duì)應(yīng)的處理函數(shù);SIG_IGN:忽略此信號(hào);SIG_DFL:按系統(tǒng)默認(rèn)
方式處理
該函數(shù)由ANSI定義,由于歷史原因在不同版本的Unix和不同版本的Linux中可能有不同的行為。因此應(yīng)該盡量避免使用它,取而代之使用sigaction函數(shù)。
2、修改信號(hào)處理動(dòng)作(通常在Linux用其來(lái)注冊(cè)一個(gè)信號(hào)的捕捉函數(shù))
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
成功:0;失�。�-1,設(shè)置errno
參數(shù):
act:傳入?yún)?shù),新的處理方式。
oldact:傳出參數(shù),舊的處理方式。
struct sigaction結(jié)構(gòu)體
struct sigaction {
void
void
sigset_t
int
void
(*sa_handler)(int);
(*sa_sigaction)(int, siginfo_t *, void *);
sa_mask;
sa_flags;
(*sa_restorer)(void);
};
sa_restorer:該元素是過(guò)時(shí)的,不應(yīng)該使用,POSIX.1標(biāo)準(zhǔn)將不指定該元素。(棄用)
sa_sigaction:當(dāng)sa_flags被指定為SA_SIGINFO標(biāo)志時(shí),使用該信號(hào)處理程序。(很少使用)
重點(diǎn)掌握:
① sa_handler:指定信號(hào)捕捉后的處理函數(shù)名(即注冊(cè)函數(shù))。也可賦值為SIG_IGN表忽略 或 SIG_DFL表執(zhí)行默認(rèn)動(dòng)作
② sa_mask: 調(diào)用信號(hào)處理函數(shù)時(shí),所要屏蔽的信號(hào)集合(信號(hào)屏蔽字)。注意:僅在處理函數(shù)被調(diào)用期間屏蔽生效,是臨時(shí)性設(shè)置。
③ sa_flags:通常設(shè)置為0,表使用默認(rèn)屬性。
內(nèi)核實(shí)現(xiàn)信號(hào)捕捉過(guò)程:

信號(hào)捕捉特性:
1、進(jìn)程正常運(yùn)行時(shí),默認(rèn)PCB中有一個(gè)信號(hào)屏蔽字,假定為☆,它決定了進(jìn)程自動(dòng)屏蔽哪些信號(hào)。當(dāng)注冊(cè)了某個(gè)信號(hào)捕捉函數(shù),捕捉到該信號(hào)以后,要調(diào)用該函數(shù)。而該函數(shù)有可能執(zhí)行很長(zhǎng)時(shí)間,在這期間所屏蔽的信號(hào)不由☆來(lái)指定。而是用sa_mask來(lái)指定。調(diào)用完信號(hào)處理函數(shù),再恢復(fù)為☆。
2、XXX信號(hào)捕捉函數(shù)執(zhí)行期間,XXX信號(hào)自動(dòng)被屏蔽。
3、阻塞的常規(guī)信號(hào)不支持排隊(duì),產(chǎn)生多次只記錄一次。(后32個(gè)實(shí)時(shí)信號(hào)支持排
隊(duì))
3、pause函數(shù) : 掛起當(dāng)前的進(jìn)程,直到收到一個(gè)信號(hào),才會(huì)接著執(zhí)行
調(diào)用該函數(shù)可以造成進(jìn)程主動(dòng)掛起,等待信號(hào)喚醒。調(diào)用該系統(tǒng)調(diào)用的進(jìn)程將處于阻塞狀態(tài)(主動(dòng)放棄cpu) 直到有信號(hào)遞達(dá)將其喚醒。
int pause(void);
返回值:-1 并設(shè)置errno為EINTR
① 如果信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程,則進(jìn)程終止,pause函數(shù)么有機(jī)會(huì)返回。 ② 如果信號(hào)的默認(rèn)處理動(dòng)作是忽略,進(jìn)程繼續(xù)處于掛起狀態(tài),pause函數(shù)不返回。 ③ 如果信號(hào)的處理動(dòng)作是捕捉,則【調(diào)用完信號(hào)處理函數(shù)之后,pause返回-1】errno設(shè)置為 EINTR,表示“被信號(hào)中斷”。想想我們還有哪個(gè)函數(shù)只有出錯(cuò)返回
值。
④ pause收到的信號(hào)不能被屏蔽,如果被屏蔽,那么pause就不能被喚醒。
4、alarm函數(shù)
設(shè)置定時(shí)器(鬧鐘)。在指定seconds后,內(nèi)核會(huì)給當(dāng)前進(jìn)程發(fā)送14)SIGALRM信號(hào)。進(jìn)程收到該信號(hào),默認(rèn)動(dòng)作終止。
每個(gè)進(jìn)程都有且只有唯一個(gè)定時(shí)器。
unsigned int alarm(unsigned int seconds);
返回0或剩余的秒數(shù),無(wú)失敗。
常用:取消定時(shí)器alarm(0),返回舊鬧鐘余下秒數(shù)。
例:alarm(5) → 3sec → alarm(4) → 5sec → alarm(5) → alarm(0)
定時(shí),與進(jìn)程狀態(tài)無(wú)關(guān)(自然定時(shí)法)!就緒、運(yùn)行、掛起(阻塞、暫停)、終止、僵尸...無(wú)論進(jìn)程處于何種狀態(tài),alarm都計(jì)時(shí)。
使用time命令查看程序執(zhí)行的時(shí)間。程序運(yùn)行的瓶頸在于IO,優(yōu)化程序,首選優(yōu)化IO。
實(shí)際執(zhí)行時(shí)間 = 系統(tǒng)時(shí)間 + 用戶時(shí)間 + 等待時(shí)間
5、raise函數(shù): 自己給自己發(fā)送信號(hào) raise(signo) == kill(getpid(), signo);
int raise(int sig);
成功:0,失敗非0值
入?yún)ⅲ?發(fā)送的信號(hào)
6、abort 函數(shù):給自己發(fā)送異常終止信號(hào) 6) SIGABRT 信號(hào),終止并產(chǎn)生core文件 void abort(void); //該函數(shù)無(wú)返回
kill函數(shù)/命令產(chǎn)生信號(hào)
1、kill命令產(chǎn)生信號(hào):kill -SIGKILL pid
2、kill函數(shù):給指定進(jìn)程發(fā)送指定信號(hào)(不一定殺死)
7、int kill(pid_t pid, int sig);
成功:0;失�。�-1 (ID非法,信號(hào)非法,普通用戶殺init進(jìn)程等權(quán)級(jí)問(wèn)題),設(shè)置errno sig:不推薦直接使用數(shù)字,應(yīng)使用宏名,因?yàn)椴煌僮飨到y(tǒng)信號(hào)編號(hào)可能不同,但名稱一致。
pid > 0: 發(fā)送信號(hào)給指定的進(jìn)程。
pid = = 0: 發(fā)送信號(hào)給 與調(diào)用kill函數(shù)進(jìn)程屬于同一進(jìn)程組的所有進(jìn)程。
pid < 0: 取|pid|發(fā)給對(duì)應(yīng)進(jìn)程組。
pid = = -1:發(fā)送給進(jìn)程有權(quán)限發(fā)送的系統(tǒng)中所有進(jìn)程。
進(jìn)程組:每個(gè)進(jìn)程都屬于一個(gè)進(jìn)程組,進(jìn)程組是一個(gè)或多個(gè)進(jìn)程集合,他們相互關(guān)聯(lián),共同完成一個(gè)實(shí)體任務(wù),每個(gè)進(jìn)程組都有一個(gè)進(jìn)程組長(zhǎng),默認(rèn)進(jìn)程組ID與進(jìn)程組長(zhǎng)ID
相同。
權(quán)限保護(hù):super用戶(root)可以發(fā)送信號(hào)給任意用戶,普通用戶是不能向系統(tǒng)用戶發(fā)送信號(hào)的。 kill -9 (root用戶的pid) 是不可以的。同樣,普通用戶也不能向其他
普通用戶發(fā)送信號(hào),終止其進(jìn)程。 只能向自己創(chuàng)建的進(jìn)程發(fā)送信號(hào)。普通用戶基本規(guī)則是:發(fā)送者實(shí)際或有效用戶ID == 接收者實(shí)際或有效用戶ID
時(shí)序競(jìng)態(tài)
設(shè)想如下場(chǎng)景:
欲睡覺(jué),定鬧鐘10分鐘,希望10分鐘后鬧鈴將自己?jiǎn)拘选?/p>
正常:定時(shí),睡覺(jué),10分鐘后被鬧鐘喚醒。
異常:鬧鐘定好后,被喚走,外出勞動(dòng),20分鐘后勞動(dòng)結(jié)束�;貋�(lái)繼續(xù)睡覺(jué)計(jì)劃,但勞動(dòng)期間鬧鐘已經(jīng)響過(guò),不會(huì)再將我喚醒。
時(shí)序問(wèn)題分析
回顧,借助pause和alarm實(shí)現(xiàn)的mysleep函數(shù)。設(shè)想如下時(shí)序:
1. 注冊(cè)SIGALRM信號(hào)處理函數(shù) (sigaction...)
2. 調(diào)用alarm(1) 函數(shù)設(shè)定鬧鐘1秒。
3. 函數(shù)調(diào)用剛結(jié)束,開(kāi)始倒計(jì)時(shí)1秒。當(dāng)前進(jìn)程失去cpu,內(nèi)核調(diào)度優(yōu)先級(jí)高的進(jìn)程(有多個(gè))取代當(dāng)前進(jìn)程。當(dāng)前進(jìn)程無(wú)法獲得cpu,進(jìn)入就緒態(tài)等待cpu。
4. 1秒后,鬧鐘超時(shí),內(nèi)核向當(dāng)前進(jìn)程發(fā)送SIGALRM信號(hào)(自然定時(shí)法,與進(jìn)程狀態(tài)無(wú)
關(guān)),高優(yōu)先級(jí)進(jìn)程尚未執(zhí)行完,當(dāng)前進(jìn)程仍處于就緒態(tài),信號(hào)無(wú)法處理(未決)
5. 優(yōu)先級(jí)高的進(jìn)程執(zhí)行完,當(dāng)前進(jìn)程獲得cpu資源,內(nèi)核調(diào)度回當(dāng)前進(jìn)程執(zhí)行。
SIGALRM信號(hào)遞達(dá),信號(hào)設(shè)置捕捉,執(zhí)行處理函數(shù)sig_alarm。
6. 信號(hào)處理函數(shù)執(zhí)行結(jié)束,返回當(dāng)前進(jìn)程主控流程,pause()被調(diào)用掛起等待。(欲
等待alarm函數(shù)發(fā)送的SIGALRM信號(hào)將自己?jiǎn)拘?
7. SIGALRM信號(hào)已經(jīng)處理完畢,pause不會(huì)等到。
解決時(shí)序問(wèn)題
可以通過(guò)設(shè)置屏蔽SIGALRM的方法來(lái)控制程序執(zhí)行邏輯,但無(wú)論如何設(shè)置,程序都有可能在“解除信號(hào)屏蔽”與“掛起等待信號(hào)”這個(gè)兩個(gè)操作間隙失去cpu資源。除非將這兩步驟合并成一個(gè)“原子操作”。sigsuspend函數(shù)具備這個(gè)功能。在對(duì)時(shí)序要求嚴(yán)格的場(chǎng)
合下都應(yīng)該使用sigsuspend替換pause。
int sigsuspend(const sigset_t *mask);//掛起等待信號(hào)。
sigsuspend函數(shù)調(diào)用期間,進(jìn)程信號(hào)屏蔽字由其參數(shù)mask指定。
可將某個(gè)信號(hào)(如SIGALRM)從臨時(shí)信號(hào)屏蔽字mask中刪除,這樣在調(diào)用sigsuspend時(shí)將解除對(duì)該信號(hào)的屏蔽,然后掛起等待,當(dāng)sigsuspend返回時(shí),進(jìn)程的信號(hào)屏蔽字恢復(fù)為原來(lái)的值。如果原來(lái)對(duì)該信號(hào)是屏蔽態(tài),sigsuspend函數(shù)返回后仍然屏蔽該信號(hào)。
競(jìng)態(tài)條件,跟系統(tǒng)負(fù)載有很緊密的關(guān)系,體現(xiàn)出信號(hào)的不可靠性。系統(tǒng)負(fù)載越嚴(yán)重,信號(hào)不可靠性越強(qiáng)。
不可靠由其實(shí)現(xiàn)原理所致。信號(hào)是通過(guò)軟件方式實(shí)現(xiàn)(跟內(nèi)核調(diào)度高度依賴,延時(shí)性強(qiáng)),每次系統(tǒng)調(diào)用結(jié)束后,或中斷處理處理結(jié)束后,需通過(guò)掃描PCB中的未決信號(hào)集,來(lái)判斷是否應(yīng)處理某個(gè)信號(hào)。當(dāng)系統(tǒng)負(fù)載過(guò)重時(shí),會(huì)出現(xiàn)時(shí)序混亂。
這種意外情況只能在編寫(xiě)程序過(guò)程中,提早預(yù)見(jiàn),主動(dòng)規(guī)避,而無(wú)法通過(guò)gdb程序調(diào)試等其他手段彌補(bǔ)。且由于該錯(cuò)誤不具規(guī)律性,后期捕捉和重現(xiàn)十分困難。
8、setitimer函數(shù)
設(shè)置定時(shí)器(鬧鐘)。 可代替alarm函數(shù)。精度微秒us,可以實(shí)現(xiàn)周期定時(shí)。
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
成功:0;失�。�-1,設(shè)置errno
參數(shù):which:指定定時(shí)方式
① 自然定時(shí):ITIMER_REAL → 14)SIGLARM 計(jì)算自然時(shí)間
② 虛擬空間計(jì)時(shí)(用戶空間):ITIMER_VIRTUAL → 26)SIGVTALRM 只計(jì)算進(jìn)程占
用cpu的時(shí)間
③ 運(yùn)行時(shí)計(jì)時(shí)(用戶+內(nèi)核):ITIMER_PROF → 27)SIGPROF 計(jì)算占用cpu及執(zhí)行系
統(tǒng)調(diào)用的時(shí)間
信號(hào)集操作函數(shù):
內(nèi)核通過(guò)讀取未決信號(hào)集來(lái)判斷信號(hào)是否應(yīng)被處理。信號(hào)屏蔽字mask可以影響未決信號(hào)集。而我們可以在應(yīng)用程序中自定義set來(lái)改變mask。已達(dá)到屏蔽指定信號(hào)的目的。
信號(hào)集設(shè)定:
sigset_t set; // typedef unsigned long sigset_t;
int sigemptyset(sigset_t *set); //將某個(gè)信號(hào)集清0
成功:0;失敗:-1
int sigfillset(sigset_t *set); //將某個(gè)信號(hào)集置
1
成功:0;失�。�-1
int sigaddset(sigset_t *set, int signum); //將某個(gè)信號(hào)加入信號(hào)集
成功:0;失敗:-1
int sigdelset(sigset_t *set, int signum); //將某個(gè)信號(hào)清出信號(hào)集
成功:0;失�。�-1
int sigismember(const sigset_t *set, int signum);//判斷某個(gè)信號(hào)是否在信號(hào)
集中
返回值:在集合:1;不在:0;出錯(cuò):-1
sigset_t類型的本質(zhì)是位圖。但不應(yīng)該直接使用位操作,而應(yīng)該使用上述函數(shù),保證
跨系統(tǒng)操作有效。
對(duì)比認(rèn)知select 函數(shù)。
9、sigprocmask函數(shù)
用來(lái)屏蔽信號(hào)、解除屏蔽也使用該函數(shù)。其本質(zhì),讀取或修改進(jìn)程的信號(hào)屏蔽字(PCB
中)
嚴(yán)格注意,屏蔽信號(hào):只是將信號(hào)處理延后執(zhí)行(延至解除屏蔽);而忽略表示將信號(hào)
丟處理。
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 成功:0;失敗:-1,設(shè)置errno
參數(shù):
set:傳入?yún)?shù),是一個(gè)位圖,set中哪位置1,就表示當(dāng)前進(jìn)程屏蔽哪個(gè)信號(hào)。
oldset:傳出參數(shù),保存舊的信號(hào)屏蔽集。
how參數(shù)取值:假設(shè)當(dāng)前的信號(hào)屏蔽字為mask
SIG_BLOCK: 當(dāng)how設(shè)置為此值,set表示需要屏蔽的信號(hào)。相當(dāng)于 mask = mask|set
SIG_UNBLOCK: 當(dāng)how設(shè)置為此,set表示需要解除屏蔽的信號(hào)。相當(dāng)于 mask = mask & ~set
SIG_SETMASK: 當(dāng)how設(shè)置為此,set表示用于替代原始屏蔽及的新屏蔽集。相當(dāng)于 mask = set若,調(diào)用sigprocmask解除了對(duì)當(dāng)前若干個(gè)信號(hào)的阻塞,則在sigprocmask返回
前,至少將其中一個(gè)信號(hào)遞達(dá)。
10、sigpending函數(shù)
讀取當(dāng)前進(jìn)程的未決信號(hào)集
int sigpending(sigset_t *set); //set傳出參數(shù)。
返回值:成功:0;失�。�-1,設(shè)置errno
華清遠(yuǎn)見(jiàn)90+項(xiàng)目獲批!教育部2021最新協(xié)同育人項(xiàng)目名
華清遠(yuǎn)見(jiàn)榮獲2021騰訊教育“年度口碑影響力職業(yè)教育品
華清遠(yuǎn)見(jiàn)受邀參加2021年武漢民辦高校信息學(xué)科合作聯(lián)盟
溫暖同行共創(chuàng)佳績(jī) 2019華清遠(yuǎn)見(jiàn)北京總部年會(huì)大曝光
助力高校AI人工智能學(xué)科建設(shè) 華清遠(yuǎn)見(jiàn)人工智能師資班
華清遠(yuǎn)見(jiàn)受邀參加四川省物聯(lián)網(wǎng)年會(huì),榮獲優(yōu)秀企業(yè)專家
