Linux C編程】第十二章 信號(hào)1 【Linux C編程】第十二章 信號(hào)2

      網(wǎng)友投稿 977 2025-03-31

      一、整體大綱


      二、信號(hào)詳解

      1. 信號(hào)的概念

      信號(hào)在我們的生活中隨處可見, 如:古代戰(zhàn)爭(zhēng)中摔杯為號(hào);現(xiàn)代戰(zhàn)爭(zhēng)中的信號(hào)彈;體育比賽中使用的信號(hào)槍......他們都有共性:

      1) 簡(jiǎn)單

      2)不能攜帶大量信息

      3)滿足某個(gè)特設(shè)條件才發(fā)送。

      信號(hào)是信息的載體,Linux/UNIX 環(huán)境下,古老、經(jīng)典的通信方式, 現(xiàn)下依然是主要的通信手段。

      Unix早期版本就提供了信號(hào)機(jī)制,但不可靠,信號(hào)可能丟失。Berkeley 和 AT&T都對(duì)信號(hào)模型做了更改,增加了可靠信號(hào)機(jī)制。但彼此不兼容。POSIX.1對(duì)可靠信號(hào)例程進(jìn)行了標(biāo)準(zhǔn)化。

      2. 信號(hào)的機(jī)制

      A 給 B 發(fā)送信號(hào),B收到信號(hào)之前執(zhí)行自己的代碼,收到信號(hào)后,不管執(zhí)行到程序的什么位置,都要暫停運(yùn)行,去處理信號(hào),處理完畢再繼續(xù)執(zhí)行。與硬件中斷類似——異步模式。但信號(hào)是軟件層面上實(shí)現(xiàn)的中斷,早期常被稱為“軟中斷”。

      信號(hào)的特質(zhì):由于信號(hào)是通過軟件方法實(shí)現(xiàn),其實(shí)現(xiàn)手段導(dǎo)致信號(hào)有很強(qiáng)的延時(shí)性。但對(duì)于用戶來說,這個(gè)延遲時(shí)間非常短,不易察覺。

      每個(gè)進(jìn)程收到的所有信號(hào),都是由內(nèi)核負(fù)責(zé)發(fā)送的,內(nèi)核處理。

      3. 與信號(hào)相關(guān)的事件和狀態(tài)

      (1)產(chǎn)生信號(hào)

      1)按鍵產(chǎn)生,如:Ctrl+c、Ctrl+z、Ctrl+\

      2)系統(tǒng)調(diào)用產(chǎn)生,如:kill、raise、abort

      3)軟件條件產(chǎn)生,如:定時(shí)器alarm

      4)硬件異常產(chǎn)生,如:非法訪問內(nèi)存(段錯(cuò)誤)、除0(浮點(diǎn)數(shù)例外)、內(nèi)存對(duì)齊出錯(cuò)(總線錯(cuò)誤)

      5)命令產(chǎn)生,如:kill命令

      遞達(dá):遞送并且到達(dá)進(jìn)程。

      未決:產(chǎn)生和遞達(dá)之間的狀態(tài)。主要由于阻塞(屏蔽)導(dǎo)致該狀態(tài)。

      如下圖所示,當(dāng) ctl+c 2號(hào)信號(hào)到達(dá),如果設(shè)置為阻塞,則在阻塞信號(hào)集和未決信號(hào)集中狀態(tài)都為 1,當(dāng)解阻塞該信號(hào)時(shí),則該信號(hào)才會(huì)被處理,同時(shí)設(shè)置未決信號(hào)集該信號(hào)狀態(tài)為0。

      4. 信號(hào)的處理方式

      1)執(zhí)行默認(rèn)動(dòng)作

      2)忽略(丟棄)

      3)捕捉(調(diào)用戶處理函數(shù))

      Linux內(nèi)核的進(jìn)程控制塊PCB是一個(gè)結(jié)構(gòu)體,task_struct, 除了包含進(jìn)程id,狀態(tài),工作目錄,用戶id,組id,文件描述符表,還包含了信號(hào)相關(guān)的信息,主要指阻塞信號(hào)集和未決信號(hào)集。

      阻塞信號(hào)集(信號(hào)屏蔽字): 將某些信號(hào)加入集合,對(duì)他們?cè)O(shè)置屏蔽,當(dāng)屏蔽x信號(hào)后,再收到該信號(hào),該信號(hào)的處理將推后(解除屏蔽后)

      未決信號(hào)集:

      1)信號(hào)產(chǎn)生,未決信號(hào)集中描述該信號(hào)的位立刻翻轉(zhuǎn)為1,表示信號(hào)處于未決狀態(tài)。當(dāng)信號(hào)被處理對(duì)應(yīng)位翻轉(zhuǎn)回為0。這一時(shí)刻往往非常短暫。

      2)信號(hào)產(chǎn)生后由于某些原因(主要是阻塞)不能抵達(dá)。這類信號(hào)的集合稱之為未決信號(hào)集。在屏蔽解除前,信號(hào)一直處于未決狀態(tài)。

      5. 信號(hào)的編號(hào)

      可以使用kill –l命令查看當(dāng)前系統(tǒng)可使用的信號(hào)有哪些。

      [root@centos 09-linux-day07]# kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX

      不存在編號(hào)為0的信號(hào)。其中1-31號(hào)信號(hào)稱之為常規(guī)信號(hào)(也叫普通信號(hào)或標(biāo)準(zhǔn)信號(hào)),34-64稱之為實(shí)時(shí)信號(hào),驅(qū)動(dòng)編程與硬件相關(guān)。名字上區(qū)別不大。而前32個(gè)名字各不相同。

      6. 信號(hào)4要素

      與變量三要素類似的,每個(gè)信號(hào)也有其必備4要素,分別是:

      1)編號(hào)

      2)名稱

      3)事件

      4)默認(rèn)處理動(dòng)作

      可通過man 7 signal查看幫助文檔獲取:

      Signal Value Action Comment ────────────────────────────────────────────────────────────────────── SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process SIGINT 2 Term Interrupt from keyboard SIGQUIT 3 Core Quit from keyboard SIGILL 4 Core Illegal Instruction SIGABRT 6 Core Abort signal from abort(3) SIGFPE 8 Core Floating point exception SIGKILL 9 Term Kill signal SIGSEGV 11 Core Invalid memory reference SIGPIPE 13 Term Broken pipe: write to pipe with no readers SIGALRM 14 Term Timer signal from alarm(2) SIGTERM 15 Term Termination signal SIGUSR1 30,10,16 Term User-defined signal 1 SIGUSR2 31,12,17 Term User-defined signal 2 SIGCHLD 20,17,18 Ign Child stopped or terminated SIGCONT 19,18,25 Cont Continue if stopped SIGSTOP 17,19,23 Stop Stop process SIGTSTP 18,20,24 Stop Stop typed at terminal SIGTTIN 21,21,26 Stop Terminal input for background process SIGTTOU 22,22,27 Stop Terminal output for background process The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.

      在標(biāo)準(zhǔn)信號(hào)中,有一些信號(hào)是有三個(gè)“Value”,第一個(gè)值通常對(duì)alpha和sparc架構(gòu)有效,中間值針對(duì)x86、arm和其他架構(gòu),最后一個(gè)應(yīng)用于mips架構(gòu)。一個(gè)‘-’表示在對(duì)應(yīng)架構(gòu)上尚未定義該信號(hào)。

      【Linux C編程】第十二章 信號(hào)1 【Linux C編程】第十二章 信號(hào)2

      不同的操作系統(tǒng)定義了不同的系統(tǒng)信號(hào)。因此有些信號(hào)出現(xiàn)在Unix系統(tǒng)內(nèi),也出現(xiàn)在Linux中,而有的信號(hào)出現(xiàn)在FreeBSD或Mac?OS中卻沒有出現(xiàn)在Linux下。這里我們只研究Linux系統(tǒng)中的信號(hào)。

      默認(rèn)動(dòng)作:

      Term:終止進(jìn)程

      Ign: 忽略信號(hào) (默認(rèn)即時(shí)對(duì)該種信號(hào)忽略操作)

      Core:終止進(jìn)程,生成Core文件。(查驗(yàn)進(jìn)程死亡原因, 用于gdb調(diào)試)

      Stop:停止(暫停)進(jìn)程

      Cont:繼續(xù)運(yùn)行進(jìn)程

      注意從man 7 signal幫助文檔中可看到 : The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.

      特別強(qiáng)調(diào)了9) SIGKILL 和 19) SIGSTOP信號(hào),不允許忽略和捕捉,只能執(zhí)行默認(rèn)動(dòng)作。甚至不能將其設(shè)置為阻塞。

      另外需清楚,只有每個(gè)信號(hào)所對(duì)應(yīng)的事件發(fā)生了,該信號(hào)才會(huì)被遞送(但不一定遞達(dá)),不應(yīng)亂發(fā)信號(hào)!!

      7. Linux常規(guī)信號(hào)一覽表

      1) SIGHUP: 當(dāng)用戶退出shell時(shí),由該shell啟動(dòng)的所有進(jìn)程將收到這個(gè)信號(hào),默認(rèn)動(dòng)作為終止進(jìn)程 2) SIGINT:當(dāng)用戶按下了組合鍵時(shí),用戶終端向正在運(yùn)行中的由該終端啟動(dòng)的程序發(fā)出此信號(hào)。默認(rèn)動(dòng) 作為終止進(jìn)程。 3) SIGQUIT:當(dāng)用戶按下組合鍵時(shí)產(chǎn)生該信號(hào),用戶終端向正在運(yùn)行中的由該終端啟動(dòng)的程序發(fā)出些信 號(hào)。默認(rèn)動(dòng)作為終止進(jìn)程。 4) SIGILL:CPU檢測(cè)到某進(jìn)程執(zhí)行了非法指令。默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件 5) SIGTRAP:該信號(hào)由斷點(diǎn)指令或其他 trap指令產(chǎn)生。默認(rèn)動(dòng)作為終止里程 并產(chǎn)生core文件。 6) SIGABRT: 調(diào)用abort函數(shù)時(shí)產(chǎn)生該信號(hào)。默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件。 7) SIGBUS:非法訪問內(nèi)存地址,包括內(nèi)存對(duì)齊出錯(cuò),默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件。 8) SIGFPE:在發(fā)生致命的運(yùn)算錯(cuò)誤時(shí)發(fā)出。不僅包括浮點(diǎn)運(yùn)算錯(cuò)誤,還包括溢出及除數(shù)為0等所有的算法錯(cuò)誤。默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件。 9) SIGKILL:無條件終止進(jìn)程。本信號(hào)不能被忽略,處理和阻塞。默認(rèn)動(dòng)作為終止進(jìn)程。它向系統(tǒng)管理員提供了可以殺死任何進(jìn)程的方法。 10) SIGUSE1:用戶定義 的信號(hào)。即程序員可以在程序中定義并使用該信號(hào)。默認(rèn)動(dòng)作為終止進(jìn)程。 11) SIGSEGV:指示進(jìn)程進(jìn)行了無效內(nèi)存訪問。默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件。 12) SIGUSR2:另外一個(gè)用戶自定義信號(hào),程序員可以在程序中定義并使用該信號(hào)。默認(rèn)動(dòng)作為終止進(jìn)程。 13) SIGPIPE:Broken pipe向一個(gè)沒有讀端的管道寫數(shù)據(jù)。默認(rèn)動(dòng)作為終止進(jìn)程。 14) SIGALRM: 定時(shí)器超時(shí),超時(shí)的時(shí)間 由系統(tǒng)調(diào)用alarm設(shè)置。默認(rèn)動(dòng)作為終止進(jìn)程。 15) SIGTERM:程序結(jié)束信號(hào),與SIGKILL不同的是,該信號(hào)可以被阻塞和終止。通常用來要示程序正常退出。執(zhí)行shell命令Kill時(shí),缺省產(chǎn)生這個(gè)信號(hào)。默認(rèn)動(dòng)作為終止進(jìn)程。 16) SIGSTKFLT:Linux早期版本出現(xiàn)的信號(hào),現(xiàn)仍保留向后兼容。默認(rèn)動(dòng)作為終止進(jìn)程。 17) SIGCHLD:子進(jìn)程結(jié)束時(shí),父進(jìn)程會(huì)收到這個(gè)信號(hào)。默認(rèn)動(dòng)作為忽略這個(gè)信號(hào)。 18) SIGCONT:如果進(jìn)程已停止,則使其繼續(xù)運(yùn)行。默認(rèn)動(dòng)作為繼續(xù)/忽略。 19) SIGSTOP:停止進(jìn)程的執(zhí)行。信號(hào)不能被忽略,處理和阻塞。默認(rèn)動(dòng)作為暫停進(jìn)程。 20) SIGTSTP:停止終端交互進(jìn)程的運(yùn)行。按下組合鍵時(shí)發(fā)出這個(gè)信號(hào)。默認(rèn)動(dòng)作為暫停進(jìn)程。 21) SIGTTIN:后臺(tái)進(jìn)程讀終端控制臺(tái)。默認(rèn)動(dòng)作為暫停進(jìn)程。 22) SIGTTOU: 該信號(hào)類似于SIGTTIN,在后臺(tái)進(jìn)程要向終端輸出數(shù)據(jù)時(shí)發(fā)生。默認(rèn)動(dòng)作為暫停進(jìn)程。 23) SIGURG:套接字上有緊急數(shù)據(jù)時(shí),向當(dāng)前正在運(yùn)行的進(jìn)程發(fā)出些信號(hào),報(bào)告有緊急數(shù)據(jù)到達(dá)。如網(wǎng)絡(luò)帶外數(shù)據(jù)到達(dá),默認(rèn)動(dòng)作為忽略該信號(hào)。 24) SIGXCPU:進(jìn)程執(zhí)行時(shí)間超過了分配給該進(jìn)程的CPU時(shí)間 ,系統(tǒng)產(chǎn)生該信號(hào)并發(fā)送給該進(jìn)程。默認(rèn)動(dòng)作為終止進(jìn)程。 25) SIGXFSZ:超過文件的最大長(zhǎng)度設(shè)置。默認(rèn)動(dòng)作為終止進(jìn)程。 26) SIGVTALRM:虛擬時(shí)鐘超時(shí)時(shí)產(chǎn)生該信號(hào)。類似于SIGALRM,但是該信號(hào)只計(jì)算該進(jìn)程占用CPU的使用時(shí)間。默認(rèn)動(dòng)作為終止進(jìn)程。 27) SGIPROF:類似于SIGVTALRM,它不公包括該進(jìn)程占用CPU時(shí)間還包括執(zhí)行系統(tǒng)調(diào)用時(shí)間。默認(rèn)動(dòng)作為終止進(jìn)程。 28) SIGWINCH:窗口變化大小時(shí)發(fā)出。默認(rèn)動(dòng)作為忽略該信號(hào)。 29) SIGIO:此信號(hào)向進(jìn)程指示發(fā)出了一個(gè)異步IO事件。默認(rèn)動(dòng)作為忽略。 30) SIGPWR:關(guān)機(jī)。默認(rèn)動(dòng)作為終止進(jìn)程。 31) SIGSYS:無效的系統(tǒng)調(diào)用。默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件。 34) SIGRTMIN ~ (64) SIGRTMAX:LINUX的實(shí)時(shí)信號(hào),它們沒有固定的含義(可以由用戶自定義)。所有的實(shí)時(shí)信號(hào)的默認(rèn)動(dòng)作都為終止進(jìn)程。

      8. 信號(hào)的產(chǎn)生

      (1)終端按鍵產(chǎn)生信號(hào)

      Ctrl + c → 2) SIGINT(終止/中斷) "INT" ----Interrupt Ctrl + z → 20) SIGTSTP(暫停/停止) "T" ----Terminal 終端。 Ctrl + \ → 3) SIGQUIT(退出)

      (2)硬件異常產(chǎn)生信號(hào)

      除0操作 → 8) SIGFPE (浮點(diǎn)數(shù)例外) "F" -----float 浮點(diǎn)數(shù) 非法訪問內(nèi)存 → 11) SIGSEGV (段錯(cuò)誤) 總線錯(cuò)誤 → 7) SIGBUS

      (3)kill命令產(chǎn)生信號(hào)

      kill命令產(chǎn)生信號(hào):kill -SIGKILL pid

      kill函數(shù):給指定進(jìn)程發(fā)送指定信號(hào)(不一定殺死)

      int kill(pid_t pid, int sig); 成功:0;失敗:-1 (ID非法,信號(hào)非法,普通用戶殺init進(jìn)程等權(quán)級(jí)問題),設(shè)置errno

      sig:不推薦直接使用數(shù)字,應(yīng)使用宏名,因?yàn)椴煌?a target="_blank" href="http://m.bai1xia.com/news/tags-192.html"style="font-weight:bold;">操作系統(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。

      練習(xí):循環(huán)創(chuàng)建5個(gè)子進(jìn)程,任一子進(jìn)程用kill函數(shù)終止其父進(jìn)程。

      子進(jìn)程kill父進(jìn)程

      1 #include 2 #include 3 #include 4 #include 5 6 int main() 7 { 8 int i = 0; 9 for (i = 0; i < 5; i++) 10 { 11 pid_t pid = fork(); 12 if (pid == 0) 13 { 14 break; 15 } 16 } 17 if (i == 2) 18 { 19 //son 20 printf("I will kill father after 5s\n"); 21 sleep(5); 22 kill(getppid(), SIGKILL); 23 while(1) 24 { 25 sleep(1); 26 } 27 } 28 else if (i == 5) 29 { 30 //parent 31 while(1) 32 { 33 printf("I am father, I am happy!\n"); 34 sleep(1); 35 } 36 } 37 38 return 0; 39 }

      練習(xí):循環(huán)創(chuàng)建5個(gè)子進(jìn)程,父進(jìn)程用kill函數(shù)終止第3個(gè)子進(jìn)程。

      父進(jìn)程kill子進(jìn)程

      1 #include 2 #include 3 #include 4 #include 5 6 int main() 7 { 8 int i = 0; 9 pid_t pid3, pid; 10 for (i = 0; i < 5; i++) 11 { 12 pid = fork(); 13 if (pid == 0) 14 { 15 break; 16 } 17 //parent 18 if (i == 2) 19 { 20 pid3 = pid; 21 } 22 } 23 if (i < 5) 24 { 25 while(1) 26 { 27 printf("I am child, pid = %d, ppid = %d\n", getpid(), getppid()); 28 sleep(3); 29 } 30 } 31 else if (i == 5) 32 { 33 printf("I am parent, pid = %d, I will kill son pid = %d\n", getpid(), pid3); 34 sleep(5); 35 kill(pid3, SIGKILL); 36 while(1) 37 { 38 sleep(1); 39 } 40 } 41 42 return 0; 43 }

      (4)raise 和 abort 函數(shù)

      raise 函數(shù):給當(dāng)前進(jìn)程發(fā)送指定信號(hào)(自己給自己發(fā)) raise(signo) == kill(getpid(), signo);

      int raise(int sig); 成功:0,失敗非0值

      abort 函數(shù):給自己發(fā)送異常終止信號(hào) 6) SIGABRT 信號(hào),終止并產(chǎn)生core文件

      void abort(void); 該函數(shù)無返回

      raise 和 abort示例

      1 #include 2 #include 3 #include 4 #include 5 #include 6 7 int main() 8 { 9 printf("I will die!\n"); 10 sleep(2); 11 //raise(SIGKILL); //kill(getpid(), sig); 12 abort(); 13 14 return 0; 15 }

      (5)軟件條件產(chǎn)生信號(hào)

      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ù),無失敗。

      常用:取消定時(shí)器alarm(0),返回舊鬧鐘余下秒數(shù)。

      例:alarm(5) → 3sec → alarm(4) → 5sec → alarm(5) → alarm(0)

      定時(shí),與進(jìn)程狀態(tài)無關(guān)(自然定時(shí)法)!就緒、運(yùn)行、掛起(阻塞、暫停)、終止、僵尸...無論進(jìn)程處于何種狀態(tài),alarm都計(jì)時(shí)。

      練習(xí):編寫程序,測(cè)試你使用的計(jì)算機(jī)1秒鐘能數(shù)多少個(gè)數(shù)。

      1s計(jì)數(shù)

      1 #include 2 #include 3 4 int main() 5 { 6 int i = 0; 7 alarm(1); 8 while(1) 9 { 10 printf("%d\n", i++); //1s數(shù)數(shù) 11 } 12 13 return 0; 14 }

      alarm示例

      1 #include 2 #include 3 4 int main() 5 { 6 int ret = 0; 7 //返回距離上一次設(shè)置時(shí)間的剩余秒數(shù) 8 ret = alarm(6); 9 printf("ret = %d\n", ret); //ret = 0 10 sleep(2); 11 ret = alarm(7); //獲取距離上一次鬧鐘值,并設(shè)置新鬧鐘 12 printf("ret = %d\n", ret); //ret = 4 13 sleep(2); 14 15 while(1) 16 { 17 printf("I am happy\n"); //這塊打印7次 18 sleep(1); 19 } 20 21 return 0; 22 }

      使用time命令查看程序執(zhí)行的時(shí)間。 程序運(yùn)行的瓶頸在于IO,優(yōu)化程序,首選優(yōu)化IO。

      實(shí)際執(zhí)行時(shí)間 = 系統(tǒng)時(shí)間 + 用戶時(shí)間 + 等待時(shí)間

      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í)方式

      1)自然定時(shí):ITIMER_REAL → 14)SIGLARM ? 計(jì)算自然時(shí)間

      2)虛擬空間計(jì)時(shí)(用戶空間):ITIMER_VIRTUAL → 26)SIGVTALRM ? ?只計(jì)算進(jìn)程占用cpu的時(shí)間

      3)運(yùn)行時(shí)計(jì)時(shí)(用戶+內(nèi)核):ITIMER_PROF → 27)SIGPROF ?計(jì)算占用cpu及執(zhí)行系統(tǒng)調(diào)用的時(shí)間

      練習(xí): 使用setitimer函數(shù)實(shí)現(xiàn)alarm函數(shù),重復(fù)計(jì)算機(jī)1秒數(shù)數(shù)程序。

      setitimer 1s計(jì)數(shù)

      1 #include 2 #include 3 #include 4 5 int main() 6 { 7 int num = 0; 8 struct itimerval myit = {{0, 0}, {1, 0}}; 9 setitimer(ITIMER_REAL, &myit, NULL); 10 while(1) 11 { 12 printf("%d\n", num++); 13 } 14 15 return 0; 16 }

      拓展練習(xí),結(jié)合man page編寫程序,測(cè)試it_interval、it_value這兩個(gè)參數(shù)的作用。

      提示: it_interval:用來設(shè)定兩次定時(shí)任務(wù)之間間隔的時(shí)間。

      it_value:定時(shí)的時(shí)長(zhǎng)

      兩個(gè)參數(shù)都設(shè)置為0,即清0操作。

      setitimer 示例

      1 #include 2 #include 3 #include 4 5 unsigned int myalarm(unsigned int seconds) 6 { 7 struct itimerval myit = {{0, 0}, {0, 0}}; 8 struct itimerval oldit; 9 myit.it_value.tv_sec = seconds; 10 setitimer(ITIMER_REAL, &myit, &oldit); //seconds 后發(fā)送 SIGALRM信號(hào) 11 printf("tv_sec = %ld, tv_micsec = %ld\n", oldit.it_value.tv_sec, oldit.it_value.tv_usec); 12 return oldit.it_value.tv_sec; 13 } 14 15 int main() 16 { 17 int ret = 0; 18 ret = myalarm(5); 19 printf("ret = %d\n", ret); 20 sleep(3); 21 ret = myalarm(3); 22 printf("ret = %d\n", ret); 23 24 while(1) 25 { 26 printf("who can kill me!\n"); 27 sleep(1); 28 } 29 30 31 return 0; 32 }

      setitimer示例

      1 #include 2 #include 3 #include 4 #include 5 6 //typedef void (*sighandler_t)(int); 7 //sighandler_t signal(int signum, sighandler_t handler); 8 9 void catch_sig(int num) 10 { 11 printf("cat %d sig\n", num); 12 } 13 14 int main() 15 { 16 signal(SIGALRM, catch_sig); 17 struct itimerval myit = {{3, 0}, {5, 0}}; //第一等待5s,之后是每隔3s 18 setitimer(ITIMER_REAL, &myit, NULL); 19 20 while(1) 21 { 22 printf("Who can kill me!\n"); 23 sleep(1); 24 } 25 26 27 return 0; 28 }

      9. 信號(hào)集操作函數(shù)

      內(nèi)核通過讀取未決信號(hào)集來判斷信號(hào)是否應(yīng)被處理。信號(hào)屏蔽字mask可以影響未決信號(hào)集。而我們可以在應(yīng)用程序中自定義set來改變mask。已達(dá)到屏蔽指定信號(hào)的目的。

      (1)信號(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ù)。

      (2)sigprocmask函數(shù)

      用來屏蔽信號(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

      IG_SETMASK: 當(dāng)how設(shè)置為此,set表示用于替代原始屏蔽及的新屏蔽集。相當(dāng)于 mask = set,若調(diào)用sigprocmask解除了對(duì)當(dāng)前若干個(gè)信號(hào)的阻塞,則在sigprocmask返回前,至少將其中一個(gè)信號(hào)遞達(dá)。

      (3)sigpending函數(shù)

      讀取當(dāng)前進(jìn)程的未決信號(hào)集

      int sigpending(sigset_t *set); set傳出參數(shù)。 返回值:成功:0;失敗:-1,設(shè)置errno

      練習(xí):編寫程序。把所有常規(guī)信號(hào)的未決狀態(tài)打印至屏幕。

      (4)信號(hào)捕捉

      1)signal函數(shù)

      注冊(cè)一個(gè)信號(hào)捕捉函數(shù):

      typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);

      該函數(shù)由ANSI定義,由于歷史原因在不同版本的Unix和不同版本的Linux中可能有不同的行為。因此應(yīng)該盡量避免使用它,取而代之使用sigaction函數(shù)。

      void (*signal(int signum, void (*sighandler_t)(int))) (int);

      能看出這個(gè)函數(shù)代表什么意思嗎? ?注意多在復(fù)雜結(jié)構(gòu)中使用typedef。

      2)sigaction函數(shù)

      修改信號(hào)處理動(dòng)作(通常在Linux用其來注冊(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 (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); };

      sa_restorer:該元素是過時(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)屬性。

      3)信號(hào)捕捉特性

      進(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)不由☆來指定。而是用sa_mask來指定。調(diào)用完信號(hào)處理函數(shù),再恢復(fù)為☆。

      XXX信號(hào)捕捉函數(shù)執(zhí)行期間,XXX信號(hào)自動(dòng)被屏蔽。

      阻塞的常規(guī)信號(hào)不支持排隊(duì),產(chǎn)生多次只記錄一次。(后32個(gè)實(shí)時(shí)信號(hào)支持排隊(duì))

      練習(xí)1:為某個(gè)信號(hào)設(shè)置捕捉函數(shù)。

      SIGALRM信號(hào)設(shè)置捕捉函數(shù)

      1 #include 2 #include 3 #include 4 #include 5 6 void catch_sig(int num) 7 { 8 printf("catch %d sig\n", num); 9 } 10 11 int main() 12 { 13 //注冊(cè)一下捕捉函數(shù) 14 struct sigaction act; 15 act.sa_flags = 0; 16 act.sa_handler = catch_sig; 17 sigemptyset(&act.sa_mask); 18 19 sigaction(SIGALRM, &act, NULL); 20 21 //setitimer 22 struct itimerval myit = {{3, 0}, {5, 0}}; 23 setitimer(ITIMER_REAL, &myit, NULL); 24 while(1) 25 { 26 printf("Who can kill me!\n"); 27 sleep(1); 28 } 29 30 return 0; 31 }

      練習(xí)2: 驗(yàn)證在信號(hào)處理函數(shù)執(zhí)行期間,該信號(hào)多次遞送,那么只在處理函數(shù)之行結(jié)束后,處理一次。

      信號(hào)處理期間屏蔽該信號(hào)

      1 #include 2 #include 3 #include 4 5 void catch_sig(int num) 6 { 7 printf("begin call, catch %d sig\n", num); 8 sleep(5); 9 printf("end call, catch %d sig\n", num); 10 } 11 12 int main() 13 { 14 struct sigaction act; 15 act.sa_flags = 0; 16 sigemptyset(&act.sa_mask); 17 sigaddset(&act.sa_mask, SIGQUIT); //臨時(shí)屏蔽 ctl+\ 信號(hào) 18 act.sa_handler = catch_sig; 19 20 //注冊(cè)捕捉 21 sigaction(SIGINT, &act, NULL); 22 23 while(1) 24 { 25 printf("who can kill me?\n"); 26 sleep(1); 27 } 28 29 return 0; 30 }

      練習(xí)3:驗(yàn)證sa_mask在捕捉函數(shù)執(zhí)行期間的屏蔽作用。

      示例同上

      4)內(nèi)核實(shí)現(xiàn)信號(hào)捕捉過程

      【Linux C編程】第十二章 信號(hào)2

      Linux

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:謝佳標(biāo):R語(yǔ)言初學(xué)者必須要知道的事兒
      下一篇:WPS表格設(shè)置不顯示零值的方法步驟圖(wps表格零值不顯示怎么設(shè)置)
      相關(guān)文章
      亚洲日本VA午夜在线电影| 曰韩亚洲av人人夜夜澡人人爽 | 久久综合亚洲色一区二区三区| 在线日韩日本国产亚洲| 亚洲А∨精品天堂在线| 深夜国产福利99亚洲视频| 亚洲欧美日韩久久精品| 亚洲人成人伊人成综合网无码| 亚洲va久久久久| 一本色道久久88—综合亚洲精品| 亚洲娇小性xxxx| 18禁亚洲深夜福利人口| 亚洲熟妇无码AV| 亚洲Av永久无码精品黑人| 亚洲av成人片在线观看| 亚洲av无一区二区三区| 国产成人亚洲精品播放器下载| 在线观看亚洲专区| 亚洲国产综合精品一区在线播放| 亚洲成片观看四虎永久| 精品国产亚洲男女在线线电影| 亚洲一区二区三区免费| 亚洲中文字幕无码永久在线| 亚洲乱码精品久久久久..| 久久亚洲精品AB无码播放| 亚洲男人天堂av| 亚洲国产成人精品青青草原| 亚洲另类古典武侠| 亚洲乱码在线观看| 日韩国产精品亚洲а∨天堂免| 在线播放亚洲精品| 亚洲一区视频在线播放| 亚洲色无码一区二区三区| 亚洲AV无一区二区三区久久| 久久丫精品国产亚洲av不卡| 亚洲婷婷在线视频| 亚洲精品V天堂中文字幕| 亚洲国产精品张柏芝在线观看| 亚洲a级成人片在线观看| 亚洲精品无码专区| 亚洲一级片免费看|