1. 程式人生 > >Linux進程間的通信

Linux進程間的通信

sign 個數 view waitpid sed pla while term clas

一.進程與線程

  1. 進程是程序在某個數據集合上的一次運行活動,線程是進程中一個執行路徑。進程有自己獨立的地址空間,而線程沒有,線程必須依賴進程而存在。
  2. 進程是系統資源分配的單位,線程是系統調度的單位
  3. 操作系統以進程為單位分配資源,進程中的每個線程共享進程的資源,每個線程有自己的程序計數器、棧。

二.進程啟動的三種方式

  1. system("ps -ef"),使用shell啟動一個新的進程
  2. exec族函數會用新進程代替原有的進程,系統會從新的進程運行,新的進程的PID值會與原來的進程的PID值相同。
  3. fork函數復制進程映像,創建一個新的進程,新進程的許多屬性與當前進程是相同的,執行代碼也完全相同,但新進程有自己的數據空間、環境、文件描述符。

三.孤兒進程和僵死進程

  在linux中,子進程總是由父進程產生的,子進程和父進程是同時運行著的,父進程無法預知子進程什麽時候結束,當一個進程完成它的工作後,父進程需要調用waitpid系統調用獲取子進程的終止狀態。孤兒進程指的是父進程已經運行結束了,而它的子進程還在運行,那麽那些子進程將稱為孤兒進程。孤兒進程將被init進程(進程號為1)所收養,並由init進程對它們完成狀態收集工作。僵屍進程指的是一個進程使用fork創建子進程,如果子進程退出,而父進程並沒有調用wait或waitpid獲取子進程的狀態信息,那麽子進程的進程描述符仍然保存在系統中。這種進程稱之為僵死進程,進程處於僵死狀態

很明顯,如果進程不調用wait / waitpid的話,那麽保留的那段信息就不會釋放,其進程號就會一直被占用,但是系統所能使用的進程號是有限的,如果大量的產生僵死進程,將因為沒有可用的進程號而導致系統不能產生新的進程. 此即為僵屍進程的危害,應當避免。有兩種方法可以避免僵死進程,一種是子進程退出時向父進程發送SIGCHILD信號,父進程處理SIGCHILD信號,在信號處理函數中調用wait進行處理僵屍進程;另外一種是創建進程的時候fork兩次,原理是將子進程成為孤兒進程,從而其父進程變為init進程,通過init進程可以處理僵死進程。具體可以參考《孤兒進程和僵死進程總結

四.進程與信號

  信號提供了一種處理異步事件的方法。每個信號都有一個名字。這些名字都以三個字符SIG開頭。在頭文件<signal.h>中,這些信號都被定義為正整數(信號編號)。不存在編號為0的信號。(kill函數對信號編號0有特殊的應用)。信號是異步事件的經典實例。產生信號的事件對進程而言是隨機出現的。進程不能簡單地測試一個變量(例如errno)來判別是否出現了一個信號,而是必須告訴內核“在此信號出現時,請執行下列操作”。下面的實例是通過信號機制處理僵死進程,子進程退出時向父進程發送SIGCHILD信號,父進程處理SIGCHILD信號。在信號處理函數中調用wait進行處理僵屍進程。

產生信號的方式:

  • 當用戶按某些終端鍵時,引發終端產生的信號。
  • 硬件異常產生信號。
  • 進程調用kill(2)函數可將信號發送給另一個進程或進程組。(接收信號進程和發送信號進程的所有者必須相同,或者發送信號進程的所有者必須是超級用戶。)
  • 用戶可用kill(1)命令將信號發送給其他進程。

可以要求內核在某個信號出現時按照下列三種方式之一進行處理,我們稱之為信號的處理或者與信號相關的動作。

  • 忽略此信號。大多數信號都可使用這種方法進行處理,但是有兩種信號決不能被忽略:SIGKILL和SIGSTOP。這兩種信號不能被忽略的原因是:它們向超級用戶提供了使進程終止或停止的可靠方法。另外,如果忽略某些由硬件異常產生的信號(例如除以0),則進程的運行行為是未定義的。
  • 捕捉信號。為了做到這一點,要通知內核在某種信號發生時調用一個用戶函數。在用戶函數中,可執行用戶希望對這種事件進行的處理。註意,不能捕捉SIGKILL和SIGSTOP信號。
  • 執行系統默認動作。註意,針對大多數信號的系統默認動作是終止

技術分享
 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <errno.h>
 4 #include <stdlib.h>
 5 #include <signal.h>
 6 
 7 static void sig_child(int signo);
 8 
 9 int main()
10 {
11     pid_t pid;
12     //創建捕捉子進程退出信號
13     signal(SIGCHLD,sig_child);
14     pid = fork();
15     if (pid < 0)
16     {
17         perror("fork error:");
18         exit(1);
19     }
20     else if (pid == 0)
21     {
22         printf("I am child process,pid id %d.I am exiting.\n",getpid());
23         exit(0);
24     }
25     printf("I am father process.I will sleep two seconds\n");
26     //等待子進程先退出
27     sleep(2);
28     //輸出進程信息
29     system("ps -o pid,ppid,state,tty,command");
30     printf("father process is exiting.\n");
31     return 0;
32 }
33 
34 static void sig_child(int signo)
35 {
36      pid_t        pid;
37      int        stat;
38      //處理僵屍進程
39      while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
40             printf("child %d terminated.\n", pid);
41 }
View Code

五.進程與管道

參考:

  • 孤兒進程與僵屍進程[總結]

  • 進程與信號

Linux進程間的通信