標籤: 暫無標籤

FIFO也稱為有名管道,它是一種文件類型,在文件系統中可以看到。程序中可以查看文件stat結構中st_mode成員的值來判斷文件是否是FIFO文件。創建一個FIFO文件類似於創建文件,FIFO文件就像普通文件一樣。

1 FIFO管道 -概述

FIFO中可以很好地解決在無關進程間數據交換的要求,並且由於它們是存在於文件系統中的,這也提供了一種比匿名管道更持久穩定的通信辦法。
FIFO的通信方式類似於在進程中使用文件來傳輸數據,只不過FIFO類型文件同時具有管道的特性。在數據讀出時,FIFO管道中同時清除數據。在shell中mkfifo命令可以建立有名管道,下面通過一個實例來幫助讀者理解FIFO。mkfifo命令的幫助手冊如下所示:
mkfifo [option] name...
其中option選項中可以選擇要創建FIFO的模式,使用形式為-m mode,這裡mode指出將要創建FIFO的八進位模式,注意,這裡新創建的FIFO會像普通文件一樣受到創建進程的umask修正。在shell中輸入命令如下:
$mkfifo –m 600 fifocat
$cat < fifocat
$./recat >fifocat
$./recat >fifocat
#include
#include
#include
#include
#include
#define BUFES PIPE_BUF
int main ( void )
{
FILE *fp;
char * cmd = "cat file1"; "
char * buf[BUFSZ];
...
...
...
pclose ( fp ) ; "
exit (0) ;
}
$_
以上實例使用系統命令 mkfifo創建FIFO類型文件fifocat,並通過14.2.4節的程序recat來讀取文件recat.c,將程序的標準輸出從定向到fifocat中,再使用命令cat從fifocat讀出數據。

2 FIFO管道 -創建FIFO


創建一個FIFO文件類似於創建文件,FIFO文件就像普通文件一樣,也是可以經過路徑名來訪問的。相應文件stat結構的域st_mode的編碼指明了文件是否是FIFO類型。FIFO管道通過函數 mkfifo創建,函數原型如下:
#include
#include
int mkfifo( const char * filename, mode_t mode );
mkfifo函數中參數mode指定FIFO的讀寫許可權,新創建FIFO的用戶ID和組ID規則域open函數相同。參數filename指定新創建FIFO的文件名稱。函數如果成功返回0,出 錯返回–1,並更改errno的值。errno有可能出現的值為:EACCESS、EEXIST、ENAMETOO- LONG、ENOENT、ENOSPE、ENOTDIR和EROFS。
下面實例演示了如何使用 mkfifo函數來創建一個FIFO。程序中從程序的命令行參數中得到一個文件名,然後使用 mkfifo函數創建FIFO文件。新創建的FIFO只具有讀寫許可權。由於FIFO文件的特性,所以它被隱性地規定不具有執行許可權。
程序清單14-5 create_fifo.c 使用 mkfifo函數創建FIFO管道
#include
#include
#include
#include
#include
int main (int argc, char *argv[] )
{
mode_t mode = 0666; "
if ( argc != 2 ){
"
printf("USEMSG: create_fifo {fifoname}\n");
exit (1);
}
"
if ( ( mkfifo (argv[1], mode )) < 0) {
perror ( "failed to mkfifo" );
exit ( 1 );
}
else
printf ("you successfully create a FIFO name is : %s\n", argv[1]);
"
exit (0);
}
(2)在shell中編譯該程序如下:
$gcc create_fifo.c–o create_fifo
(3)在shell中運行該程序如下:
$./ create_fifo
USEMSG: create_fifo {fifoname}
輸入正確的命令符。
$./ create_fifo fifo1
you successfully create a FIFO name is :fifo1
$./ create_fifo fifo1
mkfifo: File exists
上述程序使用 mkfifo函數創建一個FIFO,名字是基於用戶的輸入文件名,可以看到當要創建一個已經存在的FIFO時,程序會產生一個EEXIST的異常,相對應該異常,perror函數列印了相應的幫助信息為mkfifo: File exists。

3 FIFO管道 -FIFO的讀寫操作


一般的I/O(open close read write unlink)函數都可以用於FIFO文件,需要注意的是,在使用open函數打開一個FIFO文件時,open函數參數flag標誌位的O_NONBLOCK標誌,它關係到函數的返回狀態。詳細說明如表14-2所示。
表14-2 open函數的flag(O_NONBLOCK)詳細說明

O_NONBLOCK標誌

詳 細 說 明

置位

只讀open立即返回。當只寫open時,如果沒有進程為讀打開FIFO,則返回–1,並置errno值為ENXIO

不置位

open視情況阻塞。只讀open要阻塞到有進程為寫打開FIFO,只寫open要阻塞到有進程為讀打開FIFO

FIFO的寫操作規則類似於匿名管道的寫操作規則,當沒有進程為讀打開FIFO,調用write函數來進行寫操作會產生信號SIGPIPE,則信號可以被捕捉或者完全忽略。
%注意:當FIFO的所有寫進程都已經關閉,則為FIFO的讀進程產生一個文件結束符。
FIFO的出現,極好地解決了系統在應用過程中產生的大量的中間臨時文件的問題。FIFO可以被shell調用使數據從一個進程到另一個進程,系統不必為該中間通道去煩惱清理不必要的垃圾,或者去釋放該通道的資源,它可以被留做後來的進程使用。並且規避了匿名管道在 作用域的限制,可應用於不相關的進程之間。
下面實例演示了使用FIFO來進行兩個 進程間通信的例子。在程序write_fifo.c中打開一個名為fifo1的FIFO文件,並分10次向這個FIFO中寫入數據。在程序read_fifo.c中先打開fifo1文件,讀取裡面的數據並輸出到標準輸出中。
在vi編輯器中編輯該程序如下:
程序清單14-6 write_fifo.c 使用FIFO進行通信
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFES PIPE_BUF
int main(void)
{
int fd ;
int n, i ;
char buf[BUFES];
time_t tp;
printf("I am %d\n",getpid()); "
if((fd=open("fifo1",O_WRONLY))<0){ "
perror("open");
exit(1);
}
for ( i=0 ; i<10; i++){ "
time(&tp); "
"
n=sprintf(buf,"write_fifo %d sends %s",getpid(),ctime(&tp));
printf("Send msg:%s\n",buf);
if((write(fd, buf, n+1))<0) { "
perror("write");
close(fd); "
exit(1);
}
sleep(3); "
}
close(fd); "
exit(0);
}
程序中使用open函數打開一個名為fifo1的FIFO管道,並分10次向fifo1中寫入字元串,其中的數據有當前進程ID以及寫入時的系統時間。並把這個數據串輸出到標準輸出,然後程序自動睡眠3秒。
(1)在vi編輯器中編輯該程序如下:
程序清單14-7 read_fifo.c 使用FIFO進行通信
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFES PIPE_BUF
int main(void)
{
int fd;
int len;
char buf[BUFES];
mode_t mode = 0666; "
if((fd=open("fifo1",O_RDONLY))<0) "
{
perror("open");
exit(1);
}
while((len=read(fd,buf, BUFES))>0) "
printf("read_fifo read: %s",buf);
close(fd); "
exit(0);
}
程序中使用open函數以讀方式打開一個名為fifo1的FIFO管道,並循環讀出管道的數據,這裡使用while循環的作用就是確保數據可以全部讀出,因為在讀FIFO管道數據時,默認的是一次性讀取PIPE_BUF個位元組,當管道中數據多於PIPE_BUF個位元組時,一次性讀出PIPE_BUF-1個位元組,然後read函數返回,再列印數據到標準輸出。
(2)在shell中分別編譯上述兩個程序如下:
$gcc write_fifo.c–o write_fifo
$gcc read_fifo.c–o read_fifo
(3)在shell中使用 mkfifo創建程序中將要用到的FIFO管道。
$mkfifo –m 666 fifo1
(4)打開兩個shell分別運行程序write_fifo 和程序 read_fifo。一個shell中輸入如下:
$./write_fifo
i am 3708
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:01 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:04 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:07 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:10 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:13 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:16 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:19 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:22 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:25 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:28 2008
另一個shell中輸入如下:
$./read_fifo
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:01 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:04 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:07 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:10 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:13 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:16 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:19 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:22 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:25 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:28 2008
上述例子可以擴展成客戶端與伺服器通信的實例,write_fifo的作用類似於客戶端,可以打開多個客戶端向一個伺服器發送請求信息,read_fifo類似於伺服器,它適時監控著FIFO的讀出端,當有數據時,讀出並進行處理,但是有一個關鍵的問題是,每一個客戶端必須預先知道伺服器提供的FIFO介面,如圖1所示。
圖1 FIFO在客戶端與伺服器通信的應用1

4 FIFO管道 -FIFO的缺點


當然FIFO也有它的局限性,如圖2所示。 客戶端可以發請求到伺服器,但前提是要知道一個公共的FIFO通道,對於實現伺服器回傳應答到客戶端的問題,可以通過為每一個客戶端創建一個專用的FIFO,來實現回傳應答。但也有不足,伺服器會同時應答成千上萬個客戶端,創建如此多的FIFO是否會使系統負載過大,相應的如何判斷客戶端是否因意外而崩潰成為難題,或者客戶端不讀取應答直接退出,所以伺服器必須處理SIGPIPE信號,並做相應處理。
%說明:在伺服器端打開公共FIFO的時候,如果僅以讀打開,則當所有的客戶端都退出時,伺服器端會讀取到文件結束符。這個問題的解決辦法是伺服器以讀寫打開公共FIFO,如圖2所示。伺服器與客戶端如何實現互相通信。
圖2 FIFO在客戶端與伺服器通信的應用2
上一篇[fild]    下一篇 [FICQ]

相關評論

同義詞:暫無同義詞