信号的基本概念
1.信號
? ?信號主要用來通知進程發生了異步事件,而不會給進程傳遞任何數據。信號總共有62個,前32個被稱為普通信號,34-64被稱為實時信號。通常只關心普通信號。
2.信號的產生
? 1> 鍵盤
? ? ? ?通過組合鍵發送一個信號,一定是給前臺進程的。例如ctrl+c
? 2>用系統函數發送信號
? ? ? 可以給指定進程發送信號,例如kill命令用kill()函數實現,abort函數是當前進程接收到信號而異常終止。raise自己給自己發送信號。
? 3>由軟件條件產生
? ? alarm(時間數),當alarm完成后直接終止進程。
3.信號的處理
? ?1>忽略
? ?2>執行信號默認處理方式
? ?3>自定義方式:捕捉信號
? ? ?使用sighandler_t signal(int signum, sighandler_t handler);
? ? ?注:第一個參數是哪個信號,第二個參數是捕捉信號的函數名
4.阻塞信號
? ?1>實際執行信號處理動作稱為信號遞達。
? ?2>信號從產生到遞達之間的狀態稱為信號未決。
?進程可以選擇阻塞某個信號,被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作。(忽略與阻塞不同,忽略是在信號遞達之后)
信號不是立即處理的,是在一個合適的點上處理的。除了9號信號。
信號在內核中表示示意圖:
一個信號沒有pending,可以被block。block與pending沒有關系。
信號代碼舉例:
??1?#include<stdio.h>2?#include<signal.h>3?void?show(sigset_t*?sig_pending)//展示未決的信號4?{5???int?i=1;6???for(i=1;i<32;++i)7???{8?????if(sigismember(sig_pending,i))9?????{10???????????printf("1");11?????}12?13????else14???{15?????????printf("0");16????}17?18?19????}20?printf("\n");21?}22?23?void?handler(int?sig)//捕捉遞達的信號24?{25???printf("I??see?you!?%d\n",sig);26?27?28??}29?30?int?main()31?{32??????sigset_t?sig_mask;33??????sigset_t?sig_old;34??????sigset_t?sig_pending;35??????sigemptyset(&sig_mask);36??????sigemptyset(&sig_old);37??????sigemptyset(&sig_pending);38??????sigaddset(&sig_mask,SIGINT);//將2號信號放入信號集中阻塞39??????sigprocmask(SIG_SETMASK,&sig_mask,&sig_old);40??????int?count=10;41?????signal(SIGINT,handler);42??????while(1)43??????{44?????????sigpending(&sig_pending);//獲取當前進程信號集,與pending有關45?????????show(&sig_pending);46?????????sleep(1);47????????if(count<0)48?????????{49???????????sigprocmask(SIG_SETMASK,&sig_old,NULL);//恢復2號信號50?????????}51?????????count--;52??????}53????return?0;54?}用戶模式與內核模式切換關于信號
5.捕捉信號
? 1>sigaction
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct
sigaction *oact);
sigaction函數可以讀取和修改與指定信號相關聯的處理動作。調用成功則返回0,出錯則
返回- 1。signo是指定信號的編號。若act指針非空,則根據act修改該信號的處理動作。
若oact指針非 空,則通過oact傳出該信號原來的處理動作。act和oact指向sigaction結
構體
.
??1?#include<stdio.h>2?#include<signal.h>3?#include<string.h>4?void?handler(int?sig)5?{6???printf("I?am?that?signal...%d\n",sig);7?}?8?int?main()9?{10????struct?sigaction?old;11????struct?sigaction?act;12????act.sa_handler=handler;//自定義處理信號動作13????act.sa_flags=0;14????sigemptyset(&act.sa_mask);15????memset(&old,'\0',sizeof(old));16????sigaction(SIGINT,&act,&old);對2號信號設置17?????while(1)18?????{19??????printf("I?am?waiting??a?signal....\n");20??????sleep(2);21??????}22??????return?0;23?}?2>pause
int pause(void);
pause函數使調用進程掛起直到有信號遞達。如果信號的處理動作是終止進程,則進程終
止,pause函數沒有機會返回;如果信號的處理動作是忽略,則進程繼續處于掛起狀態,pause
不返回;如果信號的處理動作是捕捉,則調用了信號處理函數之后pause返回-1,errno設置為
EINTR, 所以pause只有出錯的返回值(想想以前還學過什么函數只有出錯返回值?)。錯誤碼
EINTR表 示“被信號中斷”。
下面我們用alarm和pause實現sleep(3)函數,稱為mysleep。
? 1 #include<stdio.h>
? 2 #include<signal.h>
? 3 #include<string.h>
? 4 void handler(int sig)
? 5 {
? 6 //donothing
? 7 }
? 8 void ?my_sleep(int time)
? 9 {
?10 ?struct sigaction act,old;
?11 ?act.sa_handler=handler;
?12 ?act.sa_flags=0;
?13 ?sigemptyset(&act.sa_mask);
?14 ?sigaction(SIGALRM,&act,&old);
?15 ?alarm(time);//設置鬧鐘
?16 ?pause();
?17 ?int ret=alarm(0);//清理鬧鐘
?18 ?sigaction(SIGALRM,&old,NULL);恢復信號
?19 ?
?20 }
?21 int main()
?22 {
?23 ? ? while(1)
?26 ? ? ?{
?27 ? ? ?printf("I am waiting ?a signal....\n");
?28 ? ? ?my_sleep(5);
?29 ? ? ?}
?30 ? ? ?return 0;
?31 }?
?程序結果:每隔5秒打印一次信息,相當于調用了sleep()函數。
?程序分析:
? ?
1. main函數調用mysleep函數,后者調用sigaction注冊了SIGALRM信號的處理函數
sig_alrm。
2. 調用alarm(nsecs)設定鬧鐘。
3. 調用pause等待,內核切換到別的進程運行。
4. nsecs秒之后,鬧鐘超時,內核發SIGALRM給這個進程。
5. 從內核態返回這個進程的用戶態之前處理未決信號,發現有SIGALRM信號,其處理函數
是sig_alrm。
6. 切換到用戶態執行sig_alrm函數,進入sig_alrm函數時SIGALRM信號被自動屏蔽,
從sig_alrm函數返回時SIGALRM信號自動解除屏蔽。然后自動執行系統調用sigreturn再次進入 內核,再返回用戶態繼續執行進程的主控制流程(main函數調用的mysleep函數)。
7. pause函數返回-1,然后調用alarm(0)取消鬧鐘,調用sigaction恢復SIGALRM信號
以前的處理 動作。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
轉載于:https://blog.51cto.com/10541571/1770532
總結
- 上一篇: centos7系统/etc/resolv
- 下一篇: WDS使用捕获映像制作企业自定义映像