問題:如何將一個“hello world”從a進程讀取,從b進程輸出。傳統的一個方法:利用文件的讀取來進行
但是有一個缺點,我們需要對文件的讀寫操作進程控制,並且文件是在磁盤中的,讀寫速度和內存不是一個級別,因此我們需要一個在內存中創建一個像文件一樣的容器來解決這樣的問題,因此就有了管道。
什麼是管道
管道是Linux/UNIX系統中比較原始的進程間通信形式,它實現數據以一種數據流的方式,在多進程間流動。在系統中其相當於文件系統上的一個文件,來緩存所要傳輸的數據。
管道通信是最常見的通信方式之一,其是在兩個進程之間實現一個數據流通的管道,該管道可以是雙向或單向的。這個文件是一種“特殊的文件”,它和一般文件有些不同的。
管道分爲有名管道和無名管道,
管道是半雙工的(至少在大多數系統是這樣的),數據只能向一個方向流動。需要雙方通信時,需要建立起兩個管道。
有名管道:在任意兩個進程之間進行使用
無名管道:父子進程之間使用
有名 管道的創建
使用命名管道的操作和使用普通文件十分相似,可以使用系統調用open打開一個命名管道,使用read和write函數對命名管道進行讀寫,使用close關閉一個命名管道,若要刪除一個命名管道,則使用系統調用unlink。
對有名管道進行讀寫操作
對管道進行寫操作,管道寫端關閉,讀端read返回值爲0
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<fcntl.h>
int main()
{
int fd = open("./fifo",O_WRONLY);
assert(fd!=-1);
printf("fd = %d\n",fd);
while(1)
{
printf("input:\n");
char buff[128] = {0};
fgets(buff,128,stdin);
if(strncmp(buff,"end",3)==0)
{
break;
}
write(fd,buff,strlen(buff));
}
close(fd);
exit(0);
}
對管道進行讀操作,管道爲空,讀操作會阻塞。
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<fcntl.h>
int main()
{
int fd = open("./fifo",O_RDONLY);
assert(fd!=-1);
printf("fd = %d\n",fd);
while(1)
{
char buff[128] = {0};
int n = read(fd,buff,127);
if(n==0)
{
break;
}
printf("n = %d,buff = %s\n",n,buff);
}
close(fd);
exit(0);
}
open有同步,需要一讀一寫進程才能打開一個管道文件,只有一個的話會阻塞在open位置
從FIFO中讀取數據的規則是:
1、如果一個進程爲了從FIFO中讀取數據而阻塞打開了FIFO,那麼稱該進程內的讀操作爲設置了阻塞標誌的讀操作。
2、如果有進程寫打開FIFO,且當前FIFO爲空,則對於設置了阻塞標誌的讀操作來說,將一直阻塞下去,直到有數據可以讀時才繼續執行;對於沒有設置阻塞標誌的讀操作來說,則返回0個字節,當前errno值爲EAGAIN,提醒以後再試。
3、對於設置了阻塞標誌的讀操作來說,造成阻塞的原因有兩種:當前FIFO內有數據,但有其它進程在讀這些數據;另外就是FIFO內沒有數據。解阻塞的原因是:FIFO中有新的數據寫入,不論寫入數據量的大小,也不論讀操作請求多少數據量,只要有數據寫入即可。
4、**讀打開的阻塞標誌只對本進程第一個讀操作施加作用,**如果本進程中有多個讀操作序列,則在第一個讀操作被喚醒並完成讀操作後,其它將要執行的讀操作將不再阻塞,即使在執行讀操作時,FIFO中沒有數據也一樣(此時,讀操作返回0)。
5、如果沒有進程寫打開FIFO,則設置了阻塞標誌的讀操作會阻塞。
6、如果FIFO中有數據,則設置了阻塞標誌的讀操作不會因爲FIFO中的字節數少於請求的字節數而阻塞,此時,讀操作會返回FIFO中現有的數據量。
向FIFO中寫入數據的規則是:
1、如果一個進程爲了向FIFO中寫入數據而阻塞打開FIFO,那麼稱該進程內的寫操作爲設置了阻塞標誌的寫操作。
2、對於設置了阻塞標誌的寫操作,當要寫入的數據量不大於PIPE_BUF時,Linux將保證寫入的原子性。如果此時管道空閒緩衝區不足以容納要寫入的字節數,則進入睡眠,直到當緩衝區中能夠容納要寫入的字節數時,纔開始進行一次性寫操作。寫入的數據長度小於等於PIPE_BUF時,那麼或者寫入全部字節,或者一個字節都不寫入,它屬於一個一次性行爲,具體要看FIFO中是否有足夠的緩衝區。
3、當要寫入的數據量大於PIPE_BUF時,Linux將不再保證寫入的原子性。FIFO緩衝區一有空閒區域,寫進程就會試圖向管道寫入數據,寫操作在寫完所有請求寫的數據後返回。
4、對於沒有設置阻塞標誌的寫操作,當要寫入的數據量大於PIPE_BUF時,Linux將不再保證寫入的原子性。在寫滿所有FIFO空閒緩衝區後,寫操作返回。
5、當要寫入的數據量不大於PIPE_BUF時,Linux將保證寫入的原子性。如果當前FIFO空閒緩衝區能夠容納請求寫入的字節數,寫完後成功返回;如果當前FIFO空閒緩衝區不能夠容納請求寫入的字節數,則返回EAGAIN錯誤,提醒以後再寫。
無名管道
無名管道創建
pipe()創建無名管道
管道讀寫規則
管道內沒有數據時,讀端(read)發生阻塞,等待有效數據進行讀取
管道容量被數據填滿時,寫端(write)發生阻塞,等待進程將數據讀走再進行寫入
如果所有管道寫端對應的文件描述符被關閉,read返回0,但會將之前管道里的數據讀完
如果所有管道的讀端對應的文件描述符被關閉,write操作會產生信號,SIGPIPE,進而導致write進程退出
當要寫入的數據量不大於管道的容量(PIPE_BUF)時,linux將保證寫入的原子性
當要寫入的數據量大於管道容量(PIPE_BUF)時,linux將不再保證寫入的原子性
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<fcntl.h>
int main()
{
int fd[2];//f[0] r ,f[1] w
pipe(fd);
assert(pid!=-1);
if(pid==0)//子進程
{
close(fd[1]);
char buff[128] = {0};
int n = read(fd[0],buff,127);
if(n == 0)
{
wait(NULL);
exit(0);
}
printf("child buff=%s",buff);
close(fd[0]);
}
else
{
close(fd[0]);
char buff[128] = {0};
printf("input:\n");
fgets(buff,128,stdin);
if(strncmp(buff,"end",3)==0)
{
exit(0);
}
write(fd[1],buff,strlen(buff));
close(fd[1]);
}
sleep(100);
exit(0);
}