日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Linux进程间通信--信号

發布時間:2023/11/27 生活经验 62 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux进程间通信--信号 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

signal

一、初步理解信號

為了理解信號 ,先從我們最熟悉的場景說起:

1.用戶輸入命令,在Shell下啟動一個前臺進程。? ? ? ? ?

2.用戶按下Ctrl-C,這個鍵盤輸入產生一個硬件中斷。? ? ? ? ?

3.如果CPU當前正在執行這個進程的代碼,則該進程的用戶空間代碼暫停執行,CPU從用戶態切換到內核態處理硬件斷。?

4. 終端驅動程序將Ctrl-C解釋成一個SIGINT信號,記在該進程的PCB中(也可以說發送了一個SIGINT信號給該進程)。?

5. 當某個時刻要從內核返回到該進程的用戶空間代碼繼續執行之前,首先處理PCB中記錄的信號,發現有一個SIGINT信號待處理,而這個信號的默認處理動作是終止進程,所以直接終止進程而不再返回它的用戶空間代碼執行。

注意,Ctrl-C產生的信號只能發給前臺進程。一個命令 后面加個&可以放到后臺運行,這樣 Shell不必等待進程結束就可以接受新的命令,啟動新的進程。Shell可以同時運行一個前臺進 程和任意多個后臺進程,只有前臺進程才能接到Ctrl-C這種控制鍵產生的信號。前臺進程 在運行過程中用戶隨時可能按下Ctrl-C而產生一個信號,也就是說該進程的用戶空間代碼執行到任何地方都有可能收到SIGINT信號而終止,所以信號相對于進程的控制流程來說是異步(Asynchronous)的

二、用kill -l命令可以察看系統定義的信號列表:


? ?每個信號都有一個編號和一個宏定義名稱,這些宏定義可以在signal.h中找到,例如其中有 定 義#define SIGINT 2。編號34以上的是實時信號,只討論編號34以下的信號,不討論實時信號。這些信號各自在什么條件下產生,默認的處理動作是什么,在signal(7)中都有:

man 7 signal

?

各類信號的說明都有。

三、信號的產生或者發送

? 函數可以向一個進程主動地發出一個信號,我們可以通過兩個函數kill和alarm來發送一個信號。
1、kill函數 ? ?先來看看kill函數,進程可以通過kill函數向包括它本身在內的其他進程發送一個信號,如果程序沒有發送這個信號的權限,對kill函數的調用就將失敗,而失敗的常見原因是目標進程由另一個用戶所擁有。想一想也是容易明白的,你總不能控制別人的程序吧,當然超級用戶root。
kill函數的原型為:
#include <sys/types.h>  
#include <signal.h>  
int kill(pid_t pid, int sig);  
它的作用把信號sig發送給進程號為pid的進程,成功時返回0。
kill調用失敗返回-1,調用失敗通常有三大原因: 1、給定的信號無效(errno = EINVAL) 2、發送權限不夠( errno = EPERM ) 3、目標進程不存在( errno = ESRCH )
2、alarm函數 ? ?這個函數跟它的名字一樣,給我們提供了一個鬧鐘的功能,進程可以調用alarm函數在經過預定時間后向發送一個SIGALRM信號。
alarm函數的型如下:
#include <unistd.h>  
unsigned int alarm(unsigned int seconds); 
? ?alarm函數用來在seconds秒之后安排發送一個SIGALRM信號,如果seconds為0,將取消所有已設置的鬧鐘請求。alarm函數的返回值是以前設置的鬧鐘時間的余留秒數,如果返回失敗返回-1。
下面就給合fork、sleep和signal函數,用一個例子來說明kill函數的用法吧,源文件為,代碼如下:
#include <unistd.h>  
#include <sys/types.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <signal.h>  static int alarm_fired = 0;  void ouch(int sig)  
{  alarm_fired = 1;  
}  int main()  
{  pid_t pid;  pid = fork();  switch(pid)  {  case -1:  perror("fork failed\n");  exit(1);  case 0:  //子進程  sleep(5);  //向父進程發送信號  kill(getppid(), SIGALRM);  exit(0);  default:;  }  //設置處理函數  signal(SIGALRM, ouch);  while(!alarm_fired)  {  printf("Hello World!\n");  sleep(1);  }  if(alarm_fired)  printf("\nI got a signal %d\n", SIGALRM);  exit(0);  
}  

在代碼中我們使用fork調用復制了一個新進程,在子進程中,5秒后向父進程中發送一個SIGALRM信號,父進程中捕獲這個信號,并用ouch函數來處理,變改alarm_fired的值,然后退出循環。 ? 注:如果父進程在子進程的信號到來之前沒有事情可做,我們可以用函數pause()來掛起父進程,直到父進程接收到信號。當進程接收到一個信號時,預設好的信號處理函數將開始運行,程序也將恢復正常的執行。這樣可以節省CPU的資源,因為可以避免使用一個循環來等待。以本例子為例,則可以把while循環改為一句pause();
下面再以一個小小的例子來說明alarm函數和pause函數的用法吧,源文件名為,signal4.c,代碼如下:
#include <unistd.h>  
#include <sys/types.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <signal.h>  static int alarm_fired = 0;  void ouch(int sig)  
{  alarm_fired = 1;  
}  int main()  
{  //關聯信號處理函數  signal(SIGALRM, ouch);  //調用alarm函數,5秒后發送信號SIGALRM  alarm(5);  //掛起進程  pause();  //接收到信號后,恢復正常執行  if(alarm_fired == 1)  printf("Receive a signal %d\n", SIGALRM);  exit(0);  
} 

進程在5秒后接收到一個SIGALRM,進程恢復運行,打印信息并退出。
四、信號的處理 1.signal函數:

程序可用使用signal函數來處理指定的信號,主要通過忽略和恢復其默認行為來工作。signal函數的原型如下:
#include <signal.h>  
void (*signal(int sig, void (*func)(int)))(int); 
這是一個相當復雜的聲明,耐心點看可以知道signal是一個帶有sig和func兩個參數的函數,func是一個類型為void (*)(int)的函數指針。該函數返回一個與func相同類型的指針,指向先前指定信號處理函數的函數指針。準備捕獲的信號的參數由sig給出,接收到的指定信號后要調用的函數由參數func給出。其實這個函數的使用是相當簡單的,通過下面的例子就可以知道。注意信號處理函數的原型必須為void func(int),或者是下面的特殊值: SIG_IGN:忽略信號
SIG_DFL:恢復信號的默認行為 說了這么多,還是給出一個例子來說明一下吧,代碼如下:
運行結果:
? ?可以看到,第一次按下終止命令(ctrl+c)時,進程并沒有被終止,面是輸出OUCH! - I got signal 2,因為SIGINT的默認行為被signal函數改變了,當進程接受到信號SIGINT時,它就去調用函數ouch去處理,注意ouch函數把信號SIGINT的處理方式改變成默認的方式,所以當你再按一次ctrl+c時,進程就像之前那樣被終止了。

2.sigaction函數

前面我們看到了signal函數對信號的處理,但是一般情況下我們可以使用一個更加健壯的信號接口—sigaction函數。它的原型為:
#include <signal.h>  
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);  
該函數與signal函數一樣,用于設置與信號sig關聯的動作,而oact如果不是空指針的話,就用它來保存原先對該信號的動作的位置,act則用于設置指定信號的動作。
sigaction結構體定義在signal.h中,但是它至少包括以下成員: 1.void (*) (int) sa_handler;處理函數指針,相當于signal函數的func參數。 2.sigset_t sa_mask; 指定一個,信號集,在調用sa_handler所指向的信號處理函數之前,該信號集將被加入到進程的信號屏蔽字中。信號屏蔽字是指當前被阻塞的一組信號,它們不能被當前進程接收到。 3.int sa_flags;信號處理修改器;
4.sa_mask的值通常是通過使用信號集函數來設置的。 5.sa_flags,通常可以取以下的值:

? ?現在有一個這樣的問題,我們使用signal或sigaction函數來指定處理信號的函數,但是如果這個信號處理函數建立之前就接收到要處理的信號的話,進程會有怎樣的反應呢?它就不會像我們想像的那樣用我們設定的處理函數來處理了。sa_mask就可以解決這樣的問題,sa_mask指定了一個信號集,在調用sa_handler所指向的信號處理函數之前,該信號集將被加入到進程的信號屏蔽字中,設置信號屏蔽字可以防止信號在它的處理函數還未運行結束時就被接收到的情況,即使用sa_mask字段可以消除這一競態條件。
承接上面的例子,下面給出用sigaction函數重寫的例子代碼,代碼如下:
#include <unistd.h>  
#include <stdio.h>  
#include <signal.h>  void ouch(int sig)  
{  printf("\nOUCH! - I got signal %d\n", sig);  
}  int main()  
{  struct sigaction act;  act.sa_handler = ouch;  //創建空的信號屏蔽字,即不屏蔽任何信息  sigemptyset(&act.sa_mask);  //使sigaction函數重置為默認行為  act.sa_flags = SA_RESETHAND;  sigaction(SIGINT, &act, 0);  while(1)  {  printf("Hello World!\n");  sleep(1);  }  return 0;  
}  
運行結果與前一個例子中的相同。注意sigaction函數在默認情況下是不被重置的,如果要想它重置,則sa_flags就要為SA_RESETHAND。
關于信號總結到此,進程通信其他機制見博主另外博文。 賜教!

轉載于:https://www.cnblogs.com/melons/p/5791795.html

總結

以上是生活随笔為你收集整理的Linux进程间通信--信号的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。