標籤:
一、進程基礎
- 進程:進程是作業系統的概念,每當我們執行一個程式時,對於作業系統來講就建立了一個進程,在這個過程中,伴隨著資源的分配和釋放。可以認為進程是一個程式的一次執行過程。
- 進程與程式的區別:
- 程式時靜態,它是一些儲存 在磁碟上得指令的有序集合,沒有任何執行的概念。
- 進程是一個動態概念,它是程式執行的過程,包括建立、調度和消亡。
- 進程在作業系統中的表示:task_struct, 進程由一個叫task_struct的結構體描述,也叫進程式控制制塊PCB,也就是說linux中的每個進程對應一個task_struct結構體。該結構體記錄了進程的一切。
- Linux進程中的檔案:Linux作業系統中每個進程有兩個資料結構描敘檔案相關資訊。
- 第一個:fs_struct,它包含此進程當前工作目錄和根目錄、umask。umask是新檔案被 建立的預設模式,它可以通過系統調用來改變。
- 第二個:files_struct,包含此進程正在使用的所有檔案的資訊。f_mode欄位描述該檔案是以什麼模式建立的:唯讀、讀寫、還是唯寫。f_pos儲存檔案中下一個讀或寫將發生的位置。f_inode描敘檔案的VFS索引節點,而f_ops是一個常式向量的指標,每個代表一個想施加於檔案的操作的函數。
- 進程的運行時結構:(摘自Vamei部落格,http://www.cnblogs.com/vamei/archive/2012/10/09/2715388.html)
二、進程式控制制
Linux環境下,有兩個基本的操作用於建立和修改進程:函數fork()用來建立一個新的進程,該進程幾乎是當前進程的一個完全拷貝;函數族exec()用來啟動另外的進程以取代當前啟動並執行進程。
2.1 fork系統調用
定義如下:
#include<sys/types.h>#include<unistd.h> pid_t fork(void);
該函數的調用返回兩次,在父進程中返回的是子進程的PID,在子進程返回的是0,根據此來判斷當前進程是父進程還是子進程。fork調用失敗時,返回-1.並設定errno。
fork函數複製當前進程,在task_struct中建立新的進程表項,很多屬性複製自父進程,採用“寫時複製”的技術,同時,父進程中開啟的檔案描述符在子進程中也是開啟的,計數器加1.
範例程式碼:來自http://blog.csdn.net/jason314/article/details/5640969
/* * fork_test.c * version 1 * Created on: 2010-5-29 * Author: wangth */#include <unistd.h>#include <stdio.h> int main () { pid_t fpid; //fpid表示fork函數返回的值 int count=0; fpid=fork(); if (fpid < 0) printf("error in fork!"); else if (fpid == 0) { printf("i am the child process, my process id is %d/n",getpid()); printf("我是爹的兒子/n");//對某些人來說中文看著更直白。 count++; } else { printf("i am the parent process, my process id is %d/n",getpid()); printf("我是孩子他爹/n"); count++; } printf("統計結果是: %d/n",count); return 0;}
2.2 exec函數族
系統調用execve()對當前進程進行替換,替換者為一個指定的程式,其參數包括檔案名稱(filename)、參數列表(argv)以及環境變數(envp)。exec函數族當然不止一個,但它們大致相同,在Linux中,它們分別是:execl,execlp,execle,execv,execve和execvp。
一個進程一旦調用exec類函數,它本身就"死亡"了,系統把程式碼片段替換成新的程式的代碼,廢棄原有的資料區段和堆棧段,並為新程式分配新的資料區段與堆棧段,唯一留下的,就是進程號,也就是說,對系統而言,還是同一個進程,不過已經是另一個程式了。
那麼如果我的程式想啟動另一程式的執行但自己仍想繼續啟動並執行話,怎麼辦呢?那就是結合fork與exec的使用。下面一段代碼顯示如何啟動運行其它程式:
樣本:
char command[256];void main(){ int rtn; /*子進程的返回數值*/ while(1) { /* 從終端讀取要執行的命令 */ printf( ">" ); fgets( command, 256, stdin ); command[strlen(command)-1] = 0; if ( fork() == 0 ) { /* 子進程執行此命令 */ execlp( command, command ); /* 如果exec函數返回,表明沒有正常執行命令,列印錯誤資訊*/ perror( command ); exit( errorno ); } else { /* 父進程, 等待子進程結束,並列印子進程的傳回值 */ wait ( &rtn ); printf( " child process return %d/n",. rtn ); } }}
三、進程通訊
我們知道多,進程間的地址空間相對獨立。進程與進程間不能像線程間通過全域變數通訊。 如果想處理序間通訊,就需要其他機制。
常用的處理序間通訊方式有這幾種
- 傳統的處理序間通訊方式:無名管道(pipe)、有名管道(fifo)和訊號(signal)
- System v IPC對象:共用記憶體(share memory)、訊息佇列(message queue)和號誌(semaphore)
- BSD:通訊端(socket)
3.1 管道通訊
管道是基於檔案描述符的通訊方式。當一個管道建立時,它會建立兩個檔案描述符fd[0]和fd[1]。其中fd[0]固定用於讀管道,而fd[1]固定用於寫管道,一般檔案I/O的函數都可以用來操作管道(lseek除外)。
樣本:父進程向管道中寫資料,子進程從管道中讀取資料“hello world”
#include <fcntl.h>#include <stdio.h>#include "apue.h"#include <errno.h>#include <sys/wait.h>#include <sys/types.h>#include <unistd.h>#include <stropts.h>#include <sys/mman.h>int main(int argc , char *argv[]){ int n,fd[2]; pid_t pid; char line[MAXLINE]; if(pipe(fd)<0) err_sys("pipe err"); if((pid=fork())<0) err_sys("pipe erro"); else if(pid>0) { close(fd[0]); if(write(fd[1],"hello world \n",13)<0) err_sys("write err"); } else{ close(fd[1]); if((n=read(fd[0],line,MAXLINE))<0) err_sys("read erro"); if(write(STDOUT_FILENO,line,n)<0) err_sys("write erro"); } exit(0);}3.2 訊號量
推薦部落格:http://blog.chinaunix.net/uid-26833883-id-3228615.html
3.3 共用記憶體
推薦部落格:http://blog.chinaunix.net/uid-26833883-id-3230564.html
共用記憶體的介紹:
- 共用記憶體是一種最為高效的處理序間通訊方式,進程可以直接讀寫記憶體,而不需要任何資料的拷貝。
- 為了在多個進程間交換資訊,核心專門留出了一塊記憶體區,可以由需要訪問的進程將其映射到自己的私人地址空間。進程就可以直接讀寫這一塊記憶體而不需要進行資料的拷貝,從而大大提高效率。
- 由於多個進程共用一段記憶體,因此也需要依靠某種同步機制。
操作流程:
<1>建立/開啟共用記憶體
<2>映射共用記憶體,即把指定的共用記憶體映射到進程的地址空間用於訪問
<3>撤銷共用記憶體映射
<4>刪除共用記憶體對象
相關API:shmget,shmat,shmd,shmctl.
3.4 訊息佇列
四、參考
進程基礎 http://www.cnblogs.com/vamei/archive/2012/10/09/2715388.html
從程式到進程 http://www.cnblogs.com/vamei/archive/2012/09/20/2694466.html
進程間管道通訊 http://blog.chinaunix.net/uid-26833883-id-3227144.html
作者:西芒xiaoP
出處:http://www.cnblogs.com/panweishadow/
若用於非商業目的,您可以自由轉載,但請保留原作者資訊和文章連結URL。
【Linux】多進程編程