一,匿名管道PIPE局限性
管道的主要局限性正體現在它的特點上:
- 只支援單向資料流;
- 只能用於具有親緣關係的進程之間;
- 沒有名字;
- 管道的緩衝區是有限的(管道制存在於記憶體中,在管道建立時,為緩衝區分配一個頁面大小);
- 管道所傳送的是無格式位元組流,這就要求管道的讀出方和寫入方必須事先約定好資料的格式,比如多少位元組算作一個訊息(或命令、或記錄)等等;
如果我們想在不相關的進程之間交換資料,可以使用FIFO檔案來做這項工作,它經常被稱為具名管道,是一種特殊類型的檔案。
二,具名管道FIFO。 2.1 有名管道相關的關鍵概念
管 道應用的一個重大限制是它沒有名字,因此,只能用於具有親緣關係的處理序間通訊,在有名管道(named pipe或FIFO)提出後,該限制得到了克服。FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的檔案形式存在於檔案系統中。這樣,即 使與FIFO的建立進程不存在親緣關係的進程,只要可以訪問該路徑,就能夠彼此通過FIFO相互連信(能夠訪問該路徑的進程以及FIFO的建立進程之 間),因此,通過FIFO不相關的進程也能交換資料。值得注意的是,FIFO嚴格遵循先進先出(first in first out),對管道及FIFO的讀總是從開始處返回資料,對它們的寫則把資料添加到末尾。它們不支援諸如lseek()等檔案定位操作。
2.2有名管道的建立
具名管道可以從命令列上建立,命令列方法是使用下面這個命令:
$ mkfifo filename
具名管道也可以從程式裡建立,相關函數有:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_t mode)
該函數的第一個參數是一個普通的路徑名,也就是建立 後FIFO的名字。第二個參數與開啟普通檔案的open()函數中的mode 參數相同。 如果mkfifo的第一個參數是一個已經存在的路徑名時,會返回EEXIST錯誤,所以一般典型的調用代碼首先會檢查是否返回該錯誤,如果確實返回該錯 誤,那麼只要調用開啟FIFO的函數就可以了。一般檔案的I/O函數都可以用於FIFO,如close、read、write等等。
man協助說明:
DESCRIPTION
mkfifo() makes a FIFO special file with name pathname. mode specifies the FIFO's permissions. It is modified by the process's umask in the usual way: the permissions of the created file are (mode & ~umask). A FIFO special file is similar to a pipe, except that it is created in a different way. Instead of being an anonymous communications channel, a FIFO special file is entered into the file system by calling mkfifo(). Once you have created a FIFO special file in this way, any process can open it for reading or writing, in the same way as an ordinary file. However, it has to be open at both ends simultaneously before you can proceed to do any input or output operations on it. Opening a FIFO for reading normally blocks until some other process opens the same FIFO for writing, and vice versa. See fifo(7) for nonblocking handling of FIFO special files.
RETURN VALUE
On success mkfifo() returns 0. In the case of an error, -1 is returned (in which case, errno is set appropriately).
2.3有名管道的開啟規則(與匿名管道一樣)
FIFO(具名管道)與pipe(匿名管道)之間唯一的區別在它們建立與開啟的方式不同,一量這些工作完成之後,它們具有相同的語義。
man協助說明:The only difference between pipes and FIFOs is the manner in which they are created and opened. Once these tasks have been accomplished, I/O on pipes and FIFOs has exactly the same semantics。
有名管道比管道多了一個開啟操作:open。
FIFO的開啟規則:
如果當前開啟操作是為讀而開啟FIFO時,若已經有相應進程為寫而開啟該FIFO,則當前開啟操作將成功返回;否則,可能阻塞直到有相應進程為寫而開啟該FIFO(當前開啟操作設定了阻塞標誌);或者,成功返回(當前開啟操作沒有設定阻塞標誌)。
如果當前開啟操作是為寫而開啟FIFO時,如果已經有相應進程為讀而開啟該FIFO,則當前開啟操作將成功返回;否則,可能阻塞直到有相應進程為讀而開啟該FIFO(當前開啟操作設定了阻塞標誌);或者,返回ENXIO錯誤(當前開啟操作沒有設定阻塞標誌)。
2.4有名管道的讀寫規則
從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檔案
#include <stdio.h><unistd.h><stdlib.h><sys/stat.h><sys/types.h> main( argc, **(argc != ,argv[(mkfifo(argv[],) == -
結果:
<unistd.h><stdlib.h><sys/stat.h><sys/types.h><fcntl.h> main( argc, ** fd = open(,O_RDONLY); (fd == -
結果:
<unistd.h><stdlib.h><sys/stat.h><sys/types.h><fcntl.h> main( argc, ** fd = open(,O_WRONLY); (fd == -
結果:
<unistd.h><stdlib.h><sys/stat.h><sys/types.h><fcntl.h> main( argc, **(argc != ,argv[= open(argv[(infd == -(mkfifo(,) == -= open((fd == - buf[* n = ((n = read(infd,buf,*
讀進程:
#include <stdio.h><unistd.h><stdlib.h><sys/stat.h><sys/types.h><fcntl.h> main( argc, **(argc != ,argv[= open(argv[],O_WRONLY|O_CREAT|(outfd == -= open((fd == - buf[* n = ((n = read(fd,buf,*
結果:
複製成功!
註:本文參考:http://blog.csdn.net/sooneboy/article/details/3915490