Linux系統程式設計(11)——程式間通訊之有名管道
管道應用的一個重大限制是它沒有名字,因此,只能用於具有親緣關係的程式間通訊,在有名管道(named pipe或FIFO)提出後,該限制得到了克服。FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的檔案形式存在於檔案系統中。這樣,即使與FIFO的建立程式不存在親緣關係的程式,只要可以訪問該路徑,就能夠彼此通過FIFO相互通訊(能夠訪問該路徑的程式以及FIFO的建立程式之間),因此,通過FIFO不相關的程式也能交換資料。值得注意的是,FIFO嚴格遵循先進先出(first in first out),對管道及FIFO的讀總是從開始處返回資料,對它們的寫則把資料新增到末尾。它們不支援諸如lseek()等檔案定位操作。
有名管道的建立
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_tmode)
該函式的第一個引數是一個普通的路徑名,也就是建立後FIFO的名字。第二個引數與開啟普通檔案的open()函式中的mode 引數相同。如果mkfifo的第一個引數是一個已經存在的路徑名時,會返回EEXIST錯誤,所以一般典型的呼叫程式碼首先會檢查是否返回該錯誤,如果確實返回該錯誤,那麼只要呼叫開啟FIFO的函式就可以了。一般檔案的I/O函式都可以用於FIFO,如close、read、write等等。
有名管道的開啟規則
有名管道比管道多了一個開啟操作:open。
FIFO的開啟規則:
如果當前開啟操作是為讀而開啟FIFO時,若已經有相應程式為寫而開啟該FIFO,則當前開啟操作將成功返回;否則,可能阻塞直到有相應程式為寫而開啟該FIFO(當前開啟操作設定了阻塞標誌);或者,成功返回(當前開啟操作沒有設定阻塞標誌)。
如果當前開啟操作是為寫而開啟FIFO時,如果已經有相應程式為讀而開啟該FIFO,則當前開啟操作將成功返回;否則,可能阻塞直到有相應程式為讀而開啟該FIFO(當前開啟操作設定了阻塞標誌);或者,返回ENXIO錯誤(當前開啟操作沒有設定阻塞標誌)。
對開啟規則的驗證參見附2。
有名管道的讀寫規則
從FIFO中讀取資料:
約定:如果一個程式為了從FIFO中讀取資料而阻塞開啟FIFO,那麼稱該程式內的讀操作為設定了阻塞標誌的讀操作。
如果有程式寫開啟FIFO,且當前FIFO內沒有資料,則對於設定了阻塞標誌的讀操作來說,將一直阻塞。對於沒有設定阻塞標誌讀操作來說則返回-1,當前errno值為EAGAIN,提醒以後再試。
對於設定了阻塞標誌的讀操作說,造成阻塞的原因有兩種:當前FIFO內有資料,但有其它程式在讀這些資料;另外就是FIFO內沒有資料。解阻塞的原因則是FIFO中有新的資料寫入,不論信寫入資料量的大小,也不論讀操作請求多少資料量。
讀開啟的阻塞標誌只對本程式第一個讀操作施加作用,如果本程式內有多個讀操作序列,則在第一個讀操作被喚醒並完成讀操作後,其它將要執行的讀操作將不再阻塞,即使在執行讀操作時,FIFO中沒有資料也一樣(此時,讀操作返回0)。
如果沒有程式寫開啟FIFO,則設定了阻塞標誌的讀操作會阻塞。
注:如果FIFO中有資料,則設定了阻塞標誌的讀操作不會因為FIFO中的位元組數小於請求讀的位元組數而阻塞,此時,讀操作會返回FIFO中現有的資料量。
向FIFO中寫入資料:
約定:如果一個程式為了向FIFO中寫入資料而阻塞開啟FIFO,那麼稱該程式內的寫操作為設定了阻塞標誌的寫操作。
對於設定了阻塞標誌的寫操作:
當要寫入的資料量不大於PIPE_BUF時,linux將保證寫入的原子性。如果此時管道空閒緩衝區不足以容納要寫入的位元組數,則進入睡眠,直到當緩衝區中能夠容納要寫入的位元組數時,才開始進行一次性寫操作。
當要寫入的資料量大於PIPE_BUF時,linux將不再保證寫入的原子性。FIFO緩衝區一有空閒區域,寫程式就會試圖向管道寫入資料,寫操作在寫完所有請求寫的資料後返回。
對於沒有設定阻塞標誌的寫操作:
當要寫入的資料量大於PIPE_BUF時,linux將不再保證寫入的原子性。在寫滿所有FIFO空閒緩衝區後,寫操作返回。
當要寫入的資料量不大於PIPE_BUF時,linux將保證寫入的原子性。如果當前FIFO空閒緩衝區能夠容納請求寫入的位元組數,寫完後成功返回;如果當前FIFO空閒緩衝區不能夠容納請求寫入的位元組數,則返回EAGAIN錯誤,提醒以後再寫;
對FIFO讀寫規則的驗證:
下面的兩個程式說明了對FIFO的讀寫規則。
寫FIFO的程式:
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_SERVER"/tmp/fifoserver"
main(int argc,char** argv)
//引數為即將寫入的位元組數
{
intfd;
charw_buf[4096*2];
intreal_wnum;
memset(w_buf,0,4096*2);
if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
printf("cannotcreate fifoserver\n");
if(fd==-1)
if(errno==ENXIO)
printf("openerror; no reading process\n");
fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
//設定非阻塞標誌
//fd=open(FIFO_SERVER,O_WRONLY,0);
//設定阻塞標誌
real_wnum=write(fd,w_buf,2048);
if(real_wnum==-1)
{
if(errno==EAGAIN)
printf("writeto fifo error; try later\n");
}
else
printf("realwrite num is %d\n",real_wnum);
real_wnum=write(fd,w_buf,5000);
//5000用於測試寫入位元組大於4096時的非原子性
//real_wnum=write(fd,w_buf,4096);
//4096用於測試寫入位元組不大於4096時的原子性
if(real_wnum==-1)
if(errno==EAGAIN)
printf("trylater\n");
}
與上一個程式一起測試寫FIFO的規則,第一個命令列引數是請求從FIFO讀出的位元組數
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_SERVER"/tmp/fifoserver"
main(int argc,char** argv)
{
charr_buf[4096*2];
int fd;
int r_size;
int ret_size;
r_size=atoi(argv[1]);
printf("requredreal read bytes %d\n",r_size);
memset(r_buf,0,sizeof(r_buf));
fd=open(FIFO_SERVER,O_RDONLY|O_NONBLOCK,0);
//fd=open(FIFO_SERVER,O_RDONLY,0);
//在此處可以把讀程式編譯成兩個不同版本:阻塞版本及非阻塞版本
if(fd==-1)
{
printf("open%s for read error\n");
exit();
}
while(1)
{
memset(r_buf,0,sizeof(r_buf));
ret_size=read(fd,r_buf,r_size);
if(ret_size==-1)
if(errno==EAGAIN)
printf("nodata avlaible\n");
printf("realread bytes %d\n",ret_size);
sleep(1);
}
pause();
unlink(FIFO_SERVER);
}
管道常用於兩個方面:
1、在shell中時常會用到管道(作為輸入輸入的重定向),在這種應用方式下,管道的建立對於使用者來說是透明的;
2、用於具有親緣關係的程式間通訊,使用者自己建立管道,並完成讀寫操作。
FIFO可以說是管道的推廣,克服了管道無名字的限制,使得無親緣關係的程式同樣可以採用先進先出的通訊機制進行通訊。
管道和FIFO的資料是位元組流,應用程式之間必須事先確定特定的傳輸"協議",採用傳播具有特定意義的訊息。
相關文章
- Linux程式間通訊②:有名管道FIFOLinux
- Linux系統程式設計之程式間通訊方式:管道(二)Linux程式設計
- Linux系統程式設計之程式間通訊方式:管道(一)Linux程式設計
- Linux系統程式設計—有名管道Linux程式設計
- Linux系統程式設計之程式間通訊方式:命名管道(二)Linux程式設計
- Linux系統程式設計之程式間通訊方式:命名管道(一)Linux程式設計
- LLinux系統程式設計(10)——程式間通訊之管道Linux程式設計
- 系統程式設計——管道通訊程式設計
- linux 程式間通訊之管道Linux
- 3|程式間通訊--有名管道學習筆記筆記
- Linux系統程式設計之程式間通訊方式:訊息佇列Linux程式設計佇列
- Linux 的程式間通訊:管道Linux
- Linux系統程式設計之匿名管道Linux程式設計
- linux系統程式設計之管道(一):匿名管道(pipe)Linux程式設計
- linux系統程式設計之管道(三):命令管道(FIFO)Linux程式設計
- linux程式間通訊--管道(PIPE & FIFO)Linux
- 【linux】系統程式設計-1-程式、管道和訊號Linux程式設計
- 溫故之.NET程式間通訊——管道
- 程式間通訊(一)管道
- linux系統程式設計之管道(二):管道讀寫規則Linux程式設計
- PHP多程式程式設計(2):管道通訊PHP程式設計
- Linux系統程式設計之程式間通訊方式:共享記憶體例項演示Linux程式設計記憶體
- Linux程式執行緒學習筆記:程式間通訊 之 管道Linux執行緒筆記
- Linux作業系統 程式之間的通訊Linux作業系統
- 有名管道程式碼
- linux程式間通訊-----管道總結例項Linux
- “Linux程式設計”小結(程式間通訊)Linux程式設計
- 作業系統程式之間的通訊作業系統
- linux 程式間通訊之FIFOLinux
- Linux程式之間如何通訊?Linux
- Linux系統程式設計之命名管道與共享記憶體Linux程式設計記憶體
- 【IPC程式間通訊之二】管道PipeC程式
- 兩個有名管道實現qq通訊
- Linux 下的程式間通訊:使用管道和訊息佇列Linux佇列
- Linux環境程式設計程式間通訊機制理解Linux程式設計
- 程式間通訊——POSIX 有名訊號量與無名訊號量
- C#使用匿名管道在本地程式之間進行通訊C#
- linux環境程式設計(2): 使用pipe完成程式間通訊Linux程式設計