Linux下多進程編程(C語言)
Linux下多進程編程(C語言)一、 進程簡介
1、進程是程序的執行。程序是靜態的,進程是動態的。
2、進程在內存中有三部分組成:數據段、堆棧段和代碼段。
代碼段:就是存放程序代碼的數據,如果有數個進程運行同一個一個程序,那麼它們就可以使用同一個代碼段(代碼段是可以共享的);
堆棧段:存放的是子程序的返回地址、參數以及程序的局部變量,主要是保存進程的執行的環境,這裏用到了棧先進後出的特性,可以看做具有記憶上一次執行的環境。
數據段:存放程序的全局變量 、常數
//動態數據分配的數據空間
係統如果同時運行數個相同的程序,它們之間就不能使用同一個堆棧段和數據段,但是可以使用同一個代碼段(代碼段是可以共享的)
二、所要用到的函數
1 、fork() 功能:產生新的進程
1.1 依據返回值可以判斷產生的是父進程還是子進程:
(1)== 0 子線程 ;(2)>0 父進程(此時返回值為子進程的進程號) ;(3) < 0 創建進程失敗
1.2 父進程的進程號要比子進程的進程號小,因為父進程要比子線程早產生
1.3 進程的三個組成部分中代碼段是父子進程共享的(同一個程序中),係統會複製數據段和堆棧段給子進程,父子進程會獨立運行,互不影響。
1.4 fork函數執行一次,產生一個新進程,複製數據段、堆棧段,但是如果一個大程序在運行的時候,fork的時候複製數據段和堆棧段會開銷很大。係統的解決的方法:一般CPU都是以頁為單位分配空間的,無論是數據段還是堆棧段都是由很多頁組成的,當實際執行fork的時候,物理空間上,兩個進程的數據段和堆棧段還是共享的,邏輯上是分開的,隻有當一個進程寫某個數據的時候,這時兩個進程之間的數據才有了區別,係統會將有區別的頁從物理上 分開,而其他的不動,從而使係統在空間上的開銷達到最小。(也就是說隻有當有進程間的堆棧段和數據段是有區別的時候才會將二者分開,否則在物理空間上,二者依然是共享的)
1.5 程序示例:
2、 getpid() 獲得當前進程的進程ID
3、 getppid() 獲得當前進程的父進程的進程ID
4、exec類函數
在linux環境下,一個進程啟動一個程序的執行可以使用exec類的函數來完成,這個exec類的函數包括:
(1) int execv (const char *filename, char *const argv[])
(2) int execl (const char *filename, const char *arg0, . . . )
(3)int execve (const char *filename, char *const argv[], char *const env[])
(4)int execle (const char *filename, const char *arg0, char *const env[], . . . )
(5)int execvp (const char *filename, char *const argv[])
(6)int execlp (const char *filename, const char *arg0, . . . )
一個進程一旦調用了exec類的函數,它本身就死了,它的代碼段會被替換成新的程序代碼,並且廢棄原有的數據段和堆棧段,重新分配新的數據段和堆棧段,唯一留下的就是進程號,對於係統而言,還是同一個進程,不過這個進程已經是另一個程序了。如果一個程序想啟動另一個程序的執行但是繼續運行自己的話,需要結合fork函數和exec函數一塊使用。
5 wait函數保證父進程運行完後等待子進程退出才退出,否則父進程結束了,子進程還在運行。
三、進程間通信
linux係統中實現進程間通信的方式中常用的有:
(1)管道(2) 消息隊列(3) 共享內存(4) 信號量 (5)套接口等。
3.1 管道
管道是進程間通信的最古老的方式,分兩種:
(1)無名管道(父進程和子進程間通信使用)。
(2)有名管道(同一機器上的任意兩個進程間的通信使用)
3.1.1 無名管道
無名管道由函數pipe()來創建 int pipe(int filedis[2]) 參數filedis返回兩個文件的描述符號:filedis[0]為讀而打開(隻用來從裏麵讀數據,不可寫) ;filedis[1]為寫而打開(隻用來向裏麵寫數據,不可讀)。 filedis[1]的輸出是filedis[0]的輸入。
3.1.2 有名管道(有名管道就是有名字的管道):
linux係統下的有名管道由兩方式創建:命令行方式mknod和函數mkfifo
方式一: mkfifo("myfifo","rw")
方式二: mknod myfifo p(使用file myfifo可以看出它是一個命名管道)
上述兩種方式都在當前目錄下生成了一個名為fifo的有名管道。生成了有名管道後就
可以使用一般的文件io函數如open、 close、 read和writre對其進行操作了
int mkfifo (const char *filename, mode t mode) sys/stat.h
3.2 共享內存
共享內存是運行在同一機器上的進程間的通信最快的方式,因為數據直接存在內存不需要在不同的進程間進行複製,通常由一個進程創建一個共享內存區,其餘進程對這塊內存進行讀寫。
3.2.1 得到共享內存有兩種方式:(1)映射/dev/mem設備(2)映射內存映像文件
映射/dev/mem不給係統帶來額外的開銷,但是在實際中應用不常用,因為它控製存取的是實際的物理內存,在linux係統下,隻有通過限製linux係統存取的內存才可以做到
通常常用的方式是使用shmXXX函數族來實現共享內存進行存儲
3.2.2 所要用到的函數
int shmget(key_t key,int size,int flag)
獲得一個共享存儲標識符,也就是請求分配size大小的內存用作共享內存 key_t關鍵字用來存儲引用標識符,是個長整型數據,在頭文件sys/types.h中定義。
上述函數執行完畢,共享內存就創建完畢了。
其餘進程使用 shmat()函數就可以連接到自身的地址空間中了
void* shmat(int shmid,void* addr,int flag)
shmid為shmget函數返回的共享存儲標識符
addr和flag決定了以什麼方式來確定連接地址
函數的返回值就是該進程數據段所連接的實際地址,也就是共享內存的地址,進程可以對其進行讀寫操作
使用共享存儲來實現進程間的通信的注意點是對數據存取的同步,必須確保一個進程去讀取數據時,它所想要的數據已經寫好了,通常信號量可以被用來實現對共享存儲數據存取的同步
3.3 套接字
套接字是實現linux操作係統和其他操作係統之間的進程間通信的方式之一,www ftp telnet等服務都是基於套接字的
除了適用於異地的計算機進程間通信外,套接字同樣適用於本地同一計算機內部的進程間通信。
最後更新:2017-04-02 15:28:25