1. 程式人生 > >程序間通訊(IPC)-管道、訊息佇列、共享記憶體、訊號、訊號量、套接字

程序間通訊(IPC)-管道、訊息佇列、共享記憶體、訊號、訊號量、套接字

多程序:

首先,先來講一下fork之後,發生了什麼事情。

由fork建立的新程序被稱為子程序(child process)。該函式被呼叫一次,但返回兩次。兩次返回的區別是子程序的返回值是0,而父程序的返回值則是新程序(子程序)的程序 id。將子程序id返回給父程序的理由是:因為一個程序的子程序可以多於一個,沒有一個函式使一個程序可以獲得其所有子程序的程序id。對子程序來說,之所以fork返回0給它,是因為它隨時可以呼叫getpid()來獲取自己的pid;也可以呼叫getppid()來獲取父程序的id。(程序id 0總是由交換程序使用,所以一個子程序的程序id不可能為0 )。

fork之後,作業系統會複製一個與父程序完全相同的子程序,雖說是父子關係,但是在作業系統看來,他們更像兄弟關係,這2個程序共享程式碼空間,但是資料空間是互相獨立的,子程序資料空間中的內容是父程序的完整拷貝,指令指標也完全相同,子程序擁有父程序當前執行到的位置

兩程序的程式計數器pc值相同,也就是說,子程序是從fork返回處開始執行的),但有一點不同,如果fork成功,子程序中fork的返回值是0,父程序中fork的返回值是子程序的程序號,如果fork不成功,父程序會返回錯誤。
可以這樣想象,2個程序一直同時執行,而且步調一致,在fork之後,他們分別作不同的工作,也就是分岔了。這也是fork為什麼叫fork的原因

至於哪一個最先執行,可能與作業系統(排程演算法)有關,而且這個問題在實際應用中並不重要,如果需要父子程序協同,可以通過原語的辦法解決。

常見的通訊方式:

每個程序各自有不同的使用者地址空間,任何一個程序的全域性變數在另一個程序中都看不到,所以程序之間要交換資料必須通過核心,在核心中

開闢一塊緩衝區,程序A把資料從使用者空間拷到核心緩衝區,程序B再從核心緩衝區把資料讀走,核心提供的這種機制稱為程序間通訊


不同程序間的通訊本質:程序之間可以看到一份公共資源;而提供這份資源的形式或者提供者不同,造成了通訊方式不同。

管道是一種具有兩個端點的通訊通道,一個管道實際上就是隻存在在記憶體中的檔案,對這個檔案操作需要兩個已經開啟檔案進行,他們代表管道的兩端,也叫兩個句,管道是一種特殊的檔案,不屬於一種檔案系統,而是一種獨立的檔案系統,有自己的資料結構,根據管道的使用範圍劃分為無名管道和命名管道。

1. 無名管道pipe:

管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程序間使用。程序的親緣關係通常是指父子程序關係。

無名管道用於父程序和子程序之間,通常父程序建立管道,然後由通訊的子程序繼承父程序的讀端點控制代碼和寫端點控制代碼,或者父程序有讀寫控制代碼的子程序,這些子程序可以使用管道直接通訊,不需要通過父程序。

2. 高階管道popen:

將另一個程式當做一個新的程序在當前程式程序中啟動,則它算是當前程式的子程序,這種方式我們成為高階管道方式。

3. 命名管道FIFO:

有名管道也是半雙工的通訊方式,但是它允許無親緣關係程序間的通訊。

命名管道是為了解決無名管道只能在父子程序間通訊而設計的,命名管道是建立在實際的磁碟介質或檔案系統(而不是隻存在記憶體中),任何程序可以通過檔名或路徑建立與該檔案的聯絡,命名管道需要一種FIFO檔案(有先進先出的原則),雖然FIFO檔案的inode節點在磁碟上,但僅是一個節點而已,檔案的資料還是存在於記憶體緩衝頁面中,和普通管道相同。

4. 訊息佇列MessageQueue:

訊息佇列是由訊息的連結串列,存放在核心中並由訊息佇列識別符號標識。訊息佇列克服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。

訊息佇列是訊息的連結串列,包括Posix訊息佇列和system v訊息佇列(Posix常用於執行緒,system常用於程序),有寫許可權的程序可以向訊息佇列中新增訊息,有讀許可權的程序可以讀走訊息佇列的訊息。

5. 共享記憶體SharedMemory:

共享記憶體就是對映一段能被其他程序所訪問的記憶體,這段共享記憶體由一個程序建立,但多個程序都可以訪問(使多個程序可以訪問同一塊記憶體空間)。共享記憶體是最快的 IPC 方式,它是針對其他程序間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,如訊號量,配合使用,來實現程序間的同步和通訊。

6. 訊號 sinal :

訊號是一種比較複雜的通訊方式,用於通知接收程序某個事件已經發生。

除了用於程序間通訊之外,程序還可以傳送訊號給程序本身。除了系統核心和root之外,只有具備相同id的程序才可以訊號進行通訊。

7. 訊號量Semaphore:

訊號量是一個計數器,可以用來控制多個程序對共享資源的訪問。它常作為一種鎖機制,防止某程序正在訪問共享資源時,其他程序也訪問該資源。因此,主要作為程序間以及同一程序內不同執行緒之間的同步手段。

8. 套接字Socket:

套接字也是一種程序間通訊機制,與其他通訊機制不同的是,它可用於不同機器間的程序通訊。

通訊過程如下:

  8.1命名socket

  SOCK_STREAM 式本地套接字的通訊雙方均需要具有本地地址,其中伺服器端的本地地址需要明確指定,指定方法是使用 struct sockaddr_in 型別的變數。

  8.2 繫結

  SOCK_STREAM 式本地套接字的通訊雙方均需要具有本地地址,其中伺服器端的本地地址需要明確指定,指定方法是使用 struct sockaddr_in 型別的變數,將相應欄位賦值,再將其繫結在建立的伺服器套接字上,繫結要使用 bind 系統呼叫,其原形如下:

int bind(int socket, const struct sockaddr *address, size_t address_len);

其中 socket表示伺服器端的套接字描述符,address 表示需要繫結的本地地址,是一個 struct sockaddr_in 型別的變數,address_len 表示該本地地址的位元組長度。

  8.3 監聽

  伺服器端套接字建立完畢並賦予本地地址值(名稱,本例中為Server Socket)後,需要進行監聽,等待客戶端連線並處理請求,監聽使用 listen 系統呼叫,接受客戶端連線使用accept系統呼叫,它們的原形如下:

int listen(int socket, int backlog);
int accept(int socket, struct sockaddr *address, size_t *address_len);

        其中 socket 表示伺服器端的套接字描述符;backlog 表示排隊連線佇列的長度(若有多個客戶端同時連線,則需要進行排隊);address 表示當前連線客戶端的本地地址,該引數為輸出引數,是客戶端傳遞過來的關於自身的資訊;address_len 表示當前連線客戶端本地地址的位元組長度,這個引數既是輸入引數,又是輸出引數。

  8.4 連線伺服器

  客戶端套接字建立完畢並賦予本地地址值後,需要連線到伺服器端進行通訊,讓伺服器端為其提供處理服務。

  對於SOCK_STREAM型別的流式套接字,需要客戶端與伺服器之間進行連線方可使用。連線要使用 connect 系統呼叫,其原形為

int connect(int socket, const struct sockaddr *address, size_t address_len);
        其中socket為客戶端的套接字描述符,address表示目標伺服器端的變數地址,是一個 struct sockaddr_in 型別的變數,address_len 表示伺服器變數地址的位元組長度。實現連線的程式碼如下:
connect(client_sockfd, (struct sockaddr*)&serv_address, sizeof(serv_address));

     8.5 相互發送接收資料

  無論客戶端還是伺服器,都要和對方進行資料上的互動,這種互動也正是我們程序通訊的主題。一個程序扮演客戶端的角色,另外一個程序扮演伺服器的角色,兩個程序之間相互發送接收資料,這就是基於本地套接字的程序通訊。傳送和接收資料要使用 write 和 read 系統呼叫,它們的原形為:

int read(int socket, char *buffer, size_t len);
int write(int socket, char *buffer, size_t len);

       其中 socket 為套接字描述符;len 為需要傳送或需要接收的資料長度;

  對於 read 系統呼叫,buffer 是用來存放接收資料的緩衝區,即接收來的資料存入其中,是一個輸出引數;

  對於 write 系統呼叫,buffer 用來存放需要傳送出去的資料,即 buffer 內的資料被髮送出去,是一個輸入引數;返回值為已經發送或接收的資料長度。

  8.6 斷開連線

  互動完成後,需要將連線斷開以節省資源,使用close系統呼叫,其原形為:

int close(int socket);

無名管道:

管道如何實現程序間的通訊

(1)父程序建立管道,得到兩個檔案描述符指向管道的兩端 int pipe(int fd[2]);//建立管道,fd[0]讀端,fd[1]寫端

(2)父程序fork出子程序,子程序也有兩個檔案描述符指向同一個管道。

(3)父程序關閉fd[0],子程序關閉fd[1],即父程序關閉管道讀端,子程序關閉管道寫端(因為管道只支援單向通訊)。父程序可以往管道寫,子程序可以從管道讀,管道是環形佇列實現的,資料從寫端流入從讀端流出,這樣就實現了程序間通訊。
這裡寫圖片描述

管道讀取資料的四種的情況

(1)讀端不讀,寫端一直寫 
這裡寫圖片描述
(2)寫端不寫,但是讀端一直讀 
這裡寫圖片描述

(3)讀端一直讀,且fd[0]保持開啟,而寫端寫了一部分資料不寫了,並且關閉fd[1]。 
這裡寫圖片描述

如果一個管道讀端一直在讀資料,而管道寫端的引用計數大於0決定管道是否會堵塞,引用計數大於0,只讀不寫會導致管道堵塞。

(4)讀端讀了一部分資料,不讀了且關閉fd[0],寫端一直在寫且f[1]還保持開啟狀態。

這裡寫圖片描述

總結:
如果一個管道的寫端一直在寫,而讀端的引用計數是否大於0決定管道是否會堵塞,引用計數大於0,只寫不讀再次呼叫write會導致管道堵塞; 
如果一個管道的讀端一直在讀,而寫端的引用計數是否大於0決定管道是否會堵塞,引用計數大於0,只讀不寫再次呼叫read會導致管道堵塞; 

而當他們的引用計數等於0時,只寫不讀會導致寫端的程序收到一個SIGPIPE訊號,導致程序終止,只讀不寫會導致read返回0,就像讀到檔案末尾一樣。

管道特點

1.管道只允許具有血緣關係的程序間通訊,如父子程序間的通訊。
2.管道只允許單向通訊。
3.管道內部保證同步機制,從而保證訪問資料的一致性。
4.面向位元組流
5.管道隨程序,程序在管道在,程序消失管道對應的埠也關閉,兩個程序都消失管道也消失。

訊號:

訊號是Linux系統中用於程序之間通訊或操作的一種機制,訊號可以在任何時候傳送給某一程序,而無須知道該程序的狀態。如果該程序並未處於執行狀態,則該訊號就由核心儲存起來,直到該程序恢復執行並傳遞給他為止。如果一個訊號被程序設定為阻塞,則該訊號的傳遞被延遲,直到其阻塞被取消時才被傳遞給程序。

Linux提供了幾十種訊號,分別代表著不同的意義。訊號之間依靠他們的值來區分,但是通常在程式中使用訊號的名字來表示一個訊號。在Linux系統中,這些訊號和以他們的名稱命名的常量被定義在/usr/includebitssignum.h檔案中。通常程式中直接包含<signal.h>就好。

訊號是在軟體層次上對中斷機制的一種模擬,是一種非同步通訊方式,訊號可以在使用者空間程序和核心之間直接互動。核心也可以利用訊號來通知使用者空間的程序來通知使用者空間發生了哪些系統事件。訊號事件有兩個來源:

1)硬體來源,例如按下了cltr+C,通常產生中斷訊號sigint

2)軟體來源,例如使用系統呼叫或者命令發出訊號。最常用的傳送訊號的系統函式是kill,raise,setitimer,sigation,sigqueue函式。軟體來源還包括一些非法運算等操作。

一旦有訊號產生,使用者程序對訊號產生的響應有三種方式:

1)執行預設操作,linux對每種訊號都規定了預設操作。

2)捕捉訊號,定義訊號處理函式,當訊號發生時,執行相應的處理函式。

3)忽略訊號,當不希望接收到的訊號對程序的執行產生影響,而讓程序繼續執行時,可以忽略該訊號,即不對訊號程序作任何處理。

  有兩個訊號是應用程序無法捕捉和忽略的,即SIGKILL和SEGSTOP,這是為了使系統管理員能在任何時候中斷或結束某一特定的程序。

上圖表示了Linux中常見的命令

1、訊號傳送:

訊號傳送的關鍵使得系統知道向哪個程序傳送訊號以及傳送什麼訊號。下面是訊號操作中常用的函式:

例子:建立子程序,為了使子程序不在父程序發出訊號前結束,子程序中使用raise函式傳送sigstop訊號,使自己暫停;父程序使用訊號操作的kill函式,向子程序傳送sigkill訊號,子程序收到此訊號,結束子程序。

2、訊號處理

當某個訊號被髮送到一個正在執行的程序時,該程序即對此特定的訊號註冊相應的訊號處理函式,以完成所需處理。設定訊號處理方式的是signal函式,在程式正常結束前,在應用signal函式恢復系統對訊號的

預設處理方式。

3.訊號阻塞

有時候既不希望程序在接收到訊號時立刻中斷程序的執行,也不希望此訊號完全被忽略掉,而是希望延遲一段時間再去呼叫訊號處理函式,這個時候就需要訊號阻塞來完成。

 

例子:主程式阻塞了cltr+c的sigint訊號。用sigpromask將sigint假如阻塞訊號集合。

管道:

管道允許在程序之間按先進先出的方式傳送資料,是程序間通訊的一種常見方式。

管道是Linux 支援的最初Unix IPC形式之一,具有以下特點:

1) 管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道

2) 匿名管道只能用於父子程序或者兄弟程序之間(具有親緣關係的程序);

3) 單獨構成一種獨立的檔案系統:管道對於管道兩端的程序而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統,而是自立門戶,單獨構成一種檔案系統,並且只存在與記憶體中。

管道分為pipe(無名管道)和fifo(命名管道)兩種,除了建立、開啟、刪除的方式不同外,這兩種管道幾乎是一樣的。他們都是通過核心緩衝區實現資料傳輸。

  • pipe用於相關程序之間的通訊,例如父程序和子程序,它通過pipe()系統呼叫來建立並開啟,當最後一個使用它的程序關閉對他的引用時,pipe將自動撤銷。
  • FIFO即命名管道,在磁碟上有對應的節點,但沒有資料塊——換言之,只是擁有一個名字和相應的訪問許可權,通過mknode()系統呼叫或者mkfifo()函式來建立的。一旦建立,任何程序都可以通過檔名將其開啟和進行讀寫,而不侷限於父子程序,當然前提是程序對FIFO有適當的訪問權。當不再被程序使用時,FIFO在記憶體中釋放,但磁碟節點仍然存在。

管道的實質是一個核心緩衝區,程序以先進先出的方式從緩衝區存取資料:管道一端的程序順序地將程序資料寫入緩衝區,另一端的程序則順序地讀取資料,該緩衝區可以看做一個迴圈佇列,讀和寫的位置都是自動增加的,一個數據只能被讀一次,讀出以後再緩衝區都不復存在了。當緩衝區讀空或者寫滿時,有一定的規則控制相應的讀程序或寫程序是否進入等待佇列,當空的緩衝區有新資料寫入或滿的緩衝區有資料讀出時,就喚醒等待佇列中的程序繼續讀寫。

無名管道:

pipe的例子:父程序建立管道,並在管道中寫入資料,而子程序從管道讀出資料

命名管道:

和無名管道的主要區別在於,命名管道有一個名字,命名管道的名字對應於一個磁碟索引節點,有了這個檔名,任何程序有相應的許可權都可以對它進行訪問。

而無名管道卻不同,程序只能訪問自己或祖先建立的管道,而不能訪問任意已經存在的管道——因為沒有名字。

Linux中通過系統呼叫mknod()或makefifo()來建立一個命名管道。最簡單的方式是通過直接使用shell

mkfifo myfifo

 等價於

mknod myfifo p

以上命令在當前目錄下建立了一個名為myfifo的命名管道。用ls -p命令檢視檔案的型別時,可以看到命名管道對應的檔名後有一條豎線"|",表示該檔案不是普通檔案而是命名管道。

使用open()函式通過檔名可以開啟已經建立的命名管道,而無名管道不能由open來開啟。當一個命名管道不再被任何程序開啟時,它沒有消失,還可以再次被開啟,就像開啟一個磁碟檔案一樣。

可以用刪除普通檔案的方法將其刪除,實際刪除的是磁碟上對應的節點資訊。

例子:用命名管道實現聊天程式,一個張三端,一個李四端。兩個程式都建立兩個命名管道,fifo1,fifo2,張三寫fifo1,李四讀fifo1;李四寫fifo2,張三讀fifo2。

用select把,管道描述符和stdin假如集合,用select進行阻塞,如果有i/o的時候喚醒程序。(粉紅色部分為select部分,黃色部分為命名管道部分)

 

在linux系統中,除了用pipe系統呼叫建立管道外,還可以使用C函式庫中管道函式popen函式來建立管道,使用pclose關閉管道。

例子:設計一個程式用popen建立管道,實現 ls -l |grep main.c的功能

分析:先用popen函式建立一個讀管道,呼叫fread函式將ls -l的結果存入buf變數,用printf函式輸出內容,用pclose關閉讀管道;

接著用popen函式建立一個寫管道,呼叫fprintf函式將buf的內容寫入管道,執行grep命令。

popen的函式原型:

FILE* popen(const char* command,const char* type);

引數說明:command是子程序要執行的命令,type表示管道的型別,r表示讀管道,w代表寫管道。如果成功返回管道檔案的指標,否則返回NULL。

使用popen函式讀寫管道,實際上也是呼叫pipe函式呼叫建立一個管道,再呼叫fork函式建立子程序,接著會建立一個shell 環境,並在這個shell環境中執行引數所指定的程序。

訊息佇列:

訊息佇列,就是一個訊息的連結串列,是一系列儲存在核心中訊息的列表。使用者程序可以向訊息佇列新增訊息,也可以從訊息佇列讀取訊息。

訊息佇列與管道通訊相比,其優勢是對每個訊息指定特定的訊息型別,接收的時候不需要按照佇列次序,而是可以根據自定義條件接收特定型別的訊息。

可以把訊息看做一個記錄,具有特定的格式以及特定的優先順序。對訊息佇列有寫許可權的程序可以向訊息佇列中按照一定的規則新增新訊息,對訊息佇列有讀許可權的程序可以從訊息佇列中讀取訊息。

訊息佇列的常用函式如下表:

程序間通過訊息佇列通訊,主要是:建立或開啟訊息佇列,新增訊息,讀取訊息和控制訊息佇列。

例子:用函式msgget建立訊息佇列,呼叫msgsnd函式,把輸入的字串新增到訊息佇列中,然後呼叫msgrcv函式,讀取訊息佇列中的訊息並列印輸出,最後再呼叫msgctl函式,刪除系統核心中的訊息佇列。(黃色部分是訊息佇列相關的關鍵程式碼,粉色部分是讀取stdin的關鍵程式碼)

1.訊息佇列

訊息佇列是訊息的連結表,存放在核心中由訊息佇列識別符號標識識別符號IPC物件的內部名, 而它的外部名則是key(鍵), 它的基本型別是key_t, 在標頭檔案<sys/types.h>中定義為長整型.。鍵由核心變換成識別符號。  使用者可以從訊息佇列中讀取資料和新增訊息,其中傳送程序新增訊息到佇列的末尾,接收程序在佇列的頭部接收訊息,訊息一旦被接收,就會從佇列中刪除。和FIFO有點類似,但是它可以實現訊息的隨機查詢,比FIFO具有更大的優勢(比如按訊息的型別欄位取訊息)。

2.訊息佇列的三個資料結構

linux核心採用結構msqid_ds管理訊息佇列

struct msqid_ds  
  {  
    struct ipc_perm msg_perm;  //訊息佇列訪問許可權  
    struct msg *msg_first;    //指向第一個訊息的指標  
    struct msg *msg_last;     //指向最後一個訊息的指標  
    ulong  msg_cbytes;       //訊息隊列當前的位元組數  
    ulong  msg_qnum;        //訊息隊列當前的訊息個數  
    ulong  msg_qbytes;     //訊息佇列可容納的最大位元組數  
    pid_t  msg_lsqid;     //最後傳送訊息的程序號ID  
    pid_t  msg_lrqid;     //最後接收訊息的程序號ID  
    time_t msg_stime;     //最後傳送訊息的時間  
    time_t msg_rtime;     //最後接收訊息的時間  
    time_t msg_ctime;    //最近修改訊息佇列的時間  
};  

linux核心採用結構msg_queue來描述訊息佇列                                                                                       

struct msg_queue {  
    structkern_ipc_perm q_perm;  
    time_t q_stime;         /* last msgsndtime */  
    time_t q_rtime;         /* last msgrcvtime */  
    time_t q_ctime;         /* last changetime */  
    unsigned long q_cbytes;     /* current number of bytes on queue*/  
    unsigned long q_qnum;       /* number of messages inqueue */  
    unsigned long q_qbytes;     /* max number of bytes on queue */  
    pid_t q_lspid;          /* pid of last msgsnd */  
    pid_t q_lrpid;          /* last receive pid */  
    struct list_head q_messages;  
    struct list_head q_receivers;  
    struct list_head q_senders;  
};                                                         

syetem V IPC 為每一個IPC結構設定了一個ipc_perm結構,該結構規定了許可權和所有者

struct ipc_perm  
{  
   key_t  key;         //呼叫shmget()時給出的關鍵字  
   uid_t  uid;        //共享記憶體所有者的有效使用者ID   
   gid_t  gid;        //共享記憶體所有者所屬組的有效組ID   
   uid_t  cuid;       //共享記憶體建立者的有效使用者ID  
   gid_t  cgid;      //共享記憶體建立者所屬組的有效組ID  
  unsigned short  mode; //Permissions + SHM_DEST和SHM_LOCKED標誌  
  unsigned short   seq;  //序列號  
};

3.與訊息佇列有關的函式

3.1建立開啟訊息佇列




3.2新增訊息


3.3讀取訊息


3.4獲得或修改訊息佇列或者刪除訊息佇列


4.訊息佇列讀取資料工作模式


共享記憶體:

共享記憶體允許兩個或多個程序共享一個給定的儲存區這一段儲存區可以被兩個或兩個以上的程序對映至自身的地址空間中,一個程序寫入共享記憶體的資訊,可以被其他使用這個共享記憶體的程序,通過一個簡單的記憶體讀取讀出,從而實現了程序間的通訊。

採用共享記憶體進行通訊的一個主要好處是效率高,因為程序可以直接讀寫記憶體,而不需要任何資料的拷貝,對於像管道和訊息佇列等通訊方式,則需要在核心和使用者空間進行四次的資料拷貝,而共享記憶體則只拷貝兩次:一次從輸入檔案到共享記憶體區,另一次從共享記憶體到輸出檔案。

一般而言,程序之間在共享記憶體時,並不總是讀寫少量資料後就解除對映,有新的通訊時再重新建立共享記憶體區域;而是保持共享區域,直到通訊完畢為止,這樣,資料內容一直儲存在共享記憶體中,並沒有寫回檔案。共享記憶體中的內容往往是在解除對映時才寫回檔案,因此,採用共享記憶體的通訊方式效率非常高。

優點:使用共享記憶體進行程序間的通訊非常方便,而且函式的介面也簡單,資料的共享還使程序間的資料不用傳送,而是直接訪問記憶體,也加快了程式的效率。同時,它也不像匿名管道那樣要求通訊的程序有一定的父子關係(system V版本共有)。
缺點:共享記憶體沒有提供互斥同步的機制,這使得我們在使用共享記憶體進行程序間通訊時,往往要藉助其他的手段比如訊號量等來進行程序間的同步工作。

共享記憶體有兩種實現方式:1、記憶體對映 2、共享記憶體機制

1、記憶體對映

記憶體對映memory map機制使程序之間通過對映同一個普通檔案實現共享記憶體,通過mmap()系統呼叫實現。普通檔案被對映到程序地址空間後,程序可以像訪問普通記憶體一樣對檔案進行訪問,不必再呼叫read/write等檔案操作函式。

例子:建立子程序,父子程序通過匿名對映實現共享記憶體。