在前一篇文章——
linux程序間通訊——使用匿名管道
中,我們看到了如何使用匿名管道來在程序之間傳遞資料,同時也看到了這個方式的乙個缺陷,就是這些程序都由乙個共同的祖先程序啟動,這給我們在不相關的的程序之間交換資料帶來了不方便。這裡將會介紹程序的另一種通訊方式——命名管道,來解決不相關程序間的通訊問題。
一、什麼是命名管道
命名管道也被稱為fifo檔案,它是一種特殊型別的檔案,它在檔案系統中以檔名的形式存在,但是它的行為卻和之前所講的沒有名字的管道(匿名管道)類似。
由於linux中所有的事物都可被視為檔案,所以對命名管道的使用也就變得與檔案操作非常的統一,也使它的使用非常方便,同時我們也可以像平常的檔名一樣在命令中使用。
二、建立命名管道
我們可以使用兩下函式之一來建立乙個命名管道,他們的原型如下:
#include #include int mkfifo(const char *filename, mode_t mode);
int mknod(const char *filename, mode_t mode | s_ififo, (dev_t)0);
這兩個函式都能建立乙個fifo檔案,注意是建立乙個真實存在於檔案系統中的檔案,filename指定了檔名,而mode則指定了檔案的讀寫許可權。
mknod是比較老的函式,而使用mkfifo函式更加簡單和規範,所以建議在可能的情況下,盡量使用mkfifo而不是mknod。
三、訪問命名管道
1、開啟fifo檔案
與開啟其他檔案一樣,fifo檔案也可以使用open呼叫來開啟。注意,mkfifo函式只是建立乙個fifo檔案,要使用命名管道還是將其開啟。
但是有兩點要
注意,1、就是程式不能以o_rdwr模式開啟fifo檔案進行讀寫操作,而其行為也未明確定義,因為如乙個管道以讀/寫方式開啟,程序就會讀回自己的輸出,同時我們通常使用fifo只是為了單向的資料傳遞。2、就是傳遞給open呼叫的是fifo的路徑名,而不是正常的檔案。
開啟fifo檔案通常有四種方式,
open(const char *path, o_rdonly);//1
open(const char *path, o_rdonly | o_nonblock);//2
open(const char *path, o_wronly);//3
open(const char *path, o_wronly | o_nonblock);//4
在open函式的呼叫的第二個引數中,你看到乙個陌生的選項o_nonblock,選項o_nonblock表示非阻塞,加上這個選項後,表示open呼叫是非阻塞的,如果沒有這個選項,則表示open呼叫是阻塞的。
open呼叫的阻塞是什麼一回事呢?很簡單,對於以唯讀方式(o_rdonly)開啟的fifo檔案,如果open呼叫是阻塞的(即第二個引數為o_rdonly),除非有乙個程序以寫方式開啟同乙個fifo,否則它不會返回;如果open呼叫是非阻塞的的(即第二個引數為o_rdonly | o_nonblock),則即使沒有其他程序以寫方式開啟同乙個fifo檔案,open呼叫將成功並立即返回。
對於以只寫方式(o_wronly)開啟的fifo檔案,如果open呼叫是阻塞的(即第二個引數為o_wronly),open呼叫將被阻塞,直到有乙個程序以唯讀方式開啟同乙個fifo檔案為止;如果open呼叫是非阻塞的(即第二個引數為o_wronly | o_nonblock),open總會立即返回,但如果沒有其他程序以唯讀方式開啟同乙個fifo檔案,open呼叫將返回-1,並且fifo也不會被開啟。
四、使用fifo實現程序間的通訊
說了這麼多,下面就用乙個例子程式來說明一下,兩個程序如何通過fifo實現通訊吧。這裡有兩個原始檔,乙個fifowrite.c,它在需要時建立管道,然後向管道寫入資料,資料由檔案data.txt提供,大小為10m,內容全是字元『0』。另乙個原始檔為fiforead.c,它從fifo中讀取資料,並把讀到的資料儲存到另乙個檔案dataformfifo.txt中。為了讓程式更加簡潔,忽略了有些函式呼叫是否成功的檢查。
fifowrite.c的源**如下:
#include #include #include #include #include #include #include #include int main()
} printf("process %d opening fifo o_wronly\n", getpid());
//以只寫阻塞方式開啟fifo檔案,以唯讀方式開啟資料檔案
pipe_fd = open(fifo_name, open_mode);
data_fd = open("data.txt", o_rdonly);
printf("process %d result %d\n", getpid(), pipe_fd);
if(pipe_fd != -1)
//累加寫的位元組數,並繼續讀取資料
bytes_sent += res;
bytes_read = read(data_fd, buffer, pipe_buf);
buffer[bytes_read] = '\0';
} close(pipe_fd);
close(data_fd);
} else
exit(exit_failure);
printf("process %d finished\n", getpid());
exit(exit_success);
}
原始檔fiforead.c的**如下:
#include #include #include #include #include #include #include #include int main()
while(res > 0);
close(pipe_fd);
close(data_fd);
} else
exit(exit_failure);
printf("process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(exit_success);
}
執行結果如下:
分析:兩個程式都使用阻塞模式的fifo,為了讓大家更清楚地看清楚阻塞究竟是怎麼一回事,首先我們執行fifowrite.exe,並把它放到後台去執行。這時呼叫jobs命令,可以看到它確實在後台執行著,過了5秒後,再呼叫jobs命令,可以看到程序fifowrite.exe還沒有結束,它還在繼續執行。因為fifowrite.exe程序的open呼叫是阻塞的,在fiforead.exe還沒有執行時,也就沒有其他的程序以讀方式開啟同乙個fifo,所以它就一直在等待,open被阻塞,沒有返回。然後,當我們程序fiforead.exe執行時(為了檢視效能,在time命令中執行),fifowrite.exe中的open呼叫返回,程序開始繼續工作,然後結束程序。而fiforead.exe的open呼叫雖然也是阻塞模式,但是fifowrite.exe早已執行,即早有另乙個程序以寫方式開啟同乙個fifo,所以open呼叫立即返回。
從time中的輸出來看,管道的傳遞效率是非常高的,因為fiforead.exe既要讀取資料,還要寫資料到檔案dataformfifo.txt中,10m的資料只用了0.1秒多一點。
此外,如果此時,你在shell中輸入如下命令,ls -l /tmp/my_fifo,可以看到如下結果:
證明fifo檔案確實是存在於檔案系統中的檔案,檔案屬性的第乙個字元為『p',表示該檔案是乙個管道。
五、命名管道的安全問題
前面的例子是兩個程序之間的通訊問題,也就是說,乙個程序向fifo檔案寫資料,而另乙個程序則在fifo檔案中讀取資料。試想這樣乙個問題,只使用乙個fifo檔案,如果有多個程序同時向同乙個fifo檔案寫資料,而只有乙個讀fifo程序在同乙個fifo檔案中讀取資料時,會發生怎麼樣的情況呢,會發生資料塊的相互交錯是很正常的?而且個人認為多個不同程序向乙個fifo讀程序傳送資料是很普通的情況。
為了解決這一問題,就是讓寫操作的原子化。怎樣才能使寫操作原子化呢?答案很簡單,系統規定:在乙個以o_wronly(即阻塞方式)開啟的fifo中, 如果寫入的資料長度小於等待pipe_buf,那麼或者寫入全部位元組,或者乙個位元組都不寫入。如果所有的寫請求都是發往乙個阻塞的fifo的,並且每個寫記請求的資料長度小於等於pipe_buf位元組,系統就可以確保資料決不會交錯在一起。
六、命名管道與匿名管道的對比
使用匿名管道,則通訊的程序之間需要乙個父子關係,通訊的兩個程序一定是由乙個共同的祖先程序啟動。但是匿名管道沒有上面說到的資料交叉的問題。
與使用匿名管道相比,我們可以看到fifowrite.exe和fiforead.exe這兩個程序是沒有什麼必然的聯絡的,如果硬要說他們具有某種聯絡,就只能說是它們都訪問同乙個fifo檔案。它解決了之前在匿名管道**現的通訊的兩個程序一定是由乙個共同的祖先程序啟動的問題。但是為了資料的安全,我們很多時候要採用阻塞的fifo,讓寫操作變成原子操作。
Linux程序間通訊 使用匿名管道
在前面,介紹了一種程序間的通訊方式 使用訊號,我們建立通知事件,並通過它引起響應,但傳遞的資訊只是乙個訊號值。這裡將介紹另一種程序間通訊的方式 匿名管道,通過它程序間可以交換更多有用的資料。一 什麼是管道 如果你使用過linux的命令,那麼對於管道這個名詞你一定不會感覺到陌生,因為我們通常通過符號 ...
Linux程序間通訊 使用共享記憶體
一 什麼是共享記憶體 顧名思義。共享記憶體就是同意兩個不相關的程序訪問同乙個邏輯記憶體。共享記憶體是在兩個正在執行的程序之間共享和傳遞資料的一種很有效的方式。不同程序之間共享的記憶體通常安排為同一段物理記憶體。程序能夠將同一段共享記憶體連線到它們自己的位址空間中,全部程序都能夠訪問共享記憶體中的位址...
unix程序間通訊 使用管道通訊
使用管道需要注意以下4種特殊情況 假設都是阻塞i o操作,沒有設定o nonblock標誌 如果所有指向管道寫端的檔案描述符都關閉了 管道寫端的引用計數等於0 而仍然有程序從管道的讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會返回0,就像讀到檔案末尾一樣。如果有指向管道寫端的檔案描述符沒...