FIFO管道

FIFO管道

它是一種檔案類型,在檔案系統中可以看到。程式中可以查看檔案stat結構中st_mode成員的值來判斷檔案是否是FIFO檔案。創建一個FIFO檔案類似於創建檔案,FIFO檔案就像普通檔案一樣。

基本介紹

  • 中文名: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 <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#define BUFES PIPE_BUF
int main ( void )
{
FILE *fp;
char * cmd = "cat file1"; /*shell 命令*/
char * buf[BUFSZ];
...
...
...
pclose ( fp ) ; /*關閉管道*/
exit (0) ;
}
$_
以上實例使用系統命令mkfifo創建FIFO類型檔案fifocat,並通過14.2.4節的程式recat來讀取檔案recat.c,將程式的標準輸出從定向到fifocat中,再使用命令cat從fifocat讀出數據。

檔案創建

創建一個FIFO檔案類似於創建檔案,FIFO檔案就像普通檔案一樣,也是可以經過路徑名來訪問的。相應檔案stat結構的域st_mode的編碼指明了檔案是否是FIFO類型。FIFO管道通過函式mkfifo創建,函式原型如下:
#include <sys/stat.h>
#include <sys/types.h>
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 <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[] )
{
mode_t mode = 0666; /*新創建的FIFO模式*/
if ( argc != 2 ){
/*向用戶提示程式使用幫助*/
printf("USEMSG: create_fifo {fifoname}\n");
exit (1);
}
/* 使用mkfifo函式創建一個FIFO管道*/
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]);
/* 輸出FIFO檔案的名稱 */
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。

讀寫操作

一般的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 <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#define BUFES PIPE_BUF
int main(void)
{
int fd ;
int n, i ;
char buf[BUFES];
time_t tp;
printf("I am %d\n",getpid()); /*說明進程的ID*/
if((fd=open("fifo1",O_WRONLY))<0){ /*以寫打開一個FIFO1*/
perror("open");
exit(1);
}
for ( i=0 ; i<10; i++){ /*循環10次向FIFO中寫入數據*/
time(&tp); /*取系統當前時間*/
/*使用sprintf 函式向buf中格式化寫入進程ID 和時間值*/
n=sprintf(buf,"write_fifo %d sends %s",getpid(),ctime(&tp));
printf("Send msg:%s\n",buf);
if((write(fd, buf, n+1))<0) { /*寫入到FIFO中*/
perror("write");
close(fd); /* 關閉FIFO檔案 */
exit(1);
}
sleep(3); /*進程睡眠3秒*/
}
close(fd); /* 關閉FIFO檔案 */
exit(0);
}
程式中使用open函式打開一個名為fifo1的FIFO管道,並分10次向fifo1中寫入字元串,其中的數據有當前進程ID以及寫入時的系統時間。並把這個數據串輸出到標準輸出,然後程式自動睡眠3秒。
(1)在vi編輯器中編輯該程式如下:
程式清單14-7 read_fifo.c 使用FIFO進行通信
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFES PIPE_BUF
int main(void)
{
int fd;
int len;
char buf[BUFES];
mode_t mode = 0666; /* FIFO檔案的許可權 */
if((fd=open("fifo1",O_RDONLY))<0) /* 打開FIFO檔案 */
{
perror("open");
exit(1);
}
while((len=read(fd,buf, BUFES))>0) /* 開始進行通信 */
printf("read_fifo read: %s",buf);
close(fd); /* 關閉FIFO檔案 */
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

檔案缺點

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

相關詞條

熱門詞條

聯絡我們