我會用幾篇博客總結一下在Linux中進程之間通信的幾種方法,我會把這個開頭的摘要部分在這個系列的每篇博客中都打出來
進程之間通信的方式
進程間通信(四)—共享存儲區傳送門:http://www.cnblogs.com/lenomirei/p/5651995.html
進程間通信(三)—信號量傳送門:http://www.cnblogs.com/lenomirei/p/5649792.html
進程間通信(二)—消息隊列傳送門:http://www.cnblogs.com/lenomirei/p/5642575.html
進程間通信(一)—管道傳送門:http://www.cnblogs.com/lenomirei/p/5636339.html
我感覺這麼寫下去越來越不像進程間的通信了,更像是進程間的打招呼。。。信號就是這樣的,某某(這個某某有很多可能性)給進程一個信號,進程就會在適當的情況下處理這個信號(這說明進程可能不會立即處理信號),什麼是適當的時候呢?比如說中斷返回的時候,或者內核態返回用戶態的時候(這個情況出現的比較多)等等(推薦本書《Linux內核設計與實現》,裡面講過)。。。這個也不是本篇的主題就不多述了。
首先來說信號是怎麼產生的!
然後說信號的處理方式,誰來處理信號?肯定是操作系統來,難不成還是程序員麼。文章一開頭就說了,信號不一定會被立即處理,操作系統不會為了處理一個信號而把當前正在運行的進程掛起(切換進程)或者殺掉(肯定不會殺掉啊,難道看見一個信號就殺害一個無辜群眾麼),掛起(進程切換)的話消耗太大了,如果不是緊急信號,可能是不會立即處理的。操作系統多選擇在內核態切換回用戶態的時候處理信號,這樣就利用兩者的切換來處理了(不用單獨進行進程切換以免浪費時間)。
總歸是不能避免的,因為很有可能在睡眠的進程就接收到信號,操作系統肯定不願意切換當前正在happy地跑著的進程,於是就得把信號儲存啊,因為是進程收到的信號,所以把信號儲存在進程唯一的PCB(就是task_struct)當中。struct sigpending pending;字段就是存放信號的信號表,之後會解釋pending。
信號的處理過程
所有的信號可以通過kill -l 命令查看
需要注意的幾點
信號的處理方式有三種
這次就沒有創建函數什麼的了,主要是信號相關的一些操作函數,但是由於比較多和繁雜,不好每一個都寫測試用例看輸出結果,最後的測試程序只用了一部分函數,並沒有全部使用到
前面說了用kill命令可以給進程發信號,可是我想用C語言編寫程序發,別怕!你需要下面的函數(raise函數只能給進程本身發信號)
或者你說不想光給自己發信號,光自己是很沒意思,那麼看下面這個函數
看這個信號(6) SIGABRT,這個信號可以讓進程異常終止,他有一個對應的函數
看這個信號(14) SIGALRM,這個信號是鬧鐘信號,它可以由這個函數發送
接下來說明一下阻塞信號,就是(pending)了
之前提到過,可以忽略一個信號,那麼我說,阻塞和忽略是不一樣的,阻塞是進程收不到該信號,忽略是進收到該信號,卻不做出任何反應
實際執行信號的處理動作稱為信號遞達(Delivery),信號從產生到遞達之間的狀態,稱為信號未決(Pending)。進程可以選擇阻塞(Block )某個信號。被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作。
task_struct中有類似字段,之前說的信號可能不會立即被執行就會存儲在pending表裡面
其中一旦信號被block,當有信號產生的時候會一直pending,因為未決就是未處理的意思,block到不了就處理不了,pending會一直有該位
以為PCB中使用32位來表示上圖中的block和pending表,所以非實時信號就算發送多個,也只顯示一個。實時信號會排隊,有幾個來就有幾個排隊
block表:某位為0表示該位對應標號的信號未被阻塞,為1表示阻塞
pending表:某位為0表示信號還未產生或者已經被處理
上圖中2號信號兩個表同時為1表示產生了2號信號,但因為2號信號被阻塞所以一直未決。
介紹幾個操作這兩張表的函數
int sigemptyset(sigset_t *set);用於初始化一個信號集(新創建出來的sigset_t對象都要先執行這個函數再去操作)
int sigfillset(sigset_t *set);初始化信號集,所有位置位1,表示支持所有信號(就好像添加了所有的信號)
int sigaddset(sigset_t *set, int signo);把感興趣的(想要操作)信號添加到信號集
int sigdelset(sigset_t *set, int signo);把感興趣的(想要操作)信號從信號集踢出去
int sigismember(const sigset_t *set, int signo);判斷你傳入的signo是否是set集合中的一員
如何阻塞一個信號?用下面的函數
如何獲取當前pending表中的信息?你需要它~
事已至此,基本操作就說完了,廢話少說,show me the code
功能看結果圖:先貼結果圖,一開始沒有任何信號,所以pending表中全是0,我通過Ctrl+C傳入2號信號,看到pending表中有2號被置位了,經過10秒取消阻塞,2號信號被處理(經過我自定義的函數)
我的程序只有一個文件server.c
1 #include <stdio.h> 2 #include <sys/signal.h> 3 #include <sys/types.h> 4 #include <signal.h> 5 6 7 8 void func(int num) 9 { 10 printf("catch signal number is %d",num); 11 12 } 13 14 15 void printfpendingsignal(sigset_t *set) 16 { 17 int i; 18 for(i=1;i<32;++i) 19 { 20 if(sigismember(set,i)) 21 { 22 printf("1"); 23 24 } 25 else 26 { 27 printf("0"); 28 } 29 } 30 printf("\n"); 31 } 32 33 34 int main() 35 { 36 sigset_t s,p,o; 37 signal(SIGINT,func); 38 sigemptyset(&s); 39 sigemptyset(&p); 40 sigemptyset(&o); 41 sigaddset(&s,SIGINT); 42 sigprocmask(SIG_SETMASK,&s,&o); 43 int count=0; 44 while(1) 45 { 46 sigpending(&p); 47 printfpendingsignal(&p); 48 sleep(1); 49 if(count++==10) 50 { 51 printf("recover!\n"); 52 sigprocmask(SIG_SETMASK,&o,NULL); 53 } 54 } 55 return 0; 56 }
http://xxxxxx/Linuxjc/1140761.html TechArticle