pipe()管道通訊

管道

管道的概念:

管道是一種最基本的IPC機制,做用於有血緣關係的進程之間,完成數據傳遞。調用pipe系統函數便可建立一個管道。有以下特質:函數

1. 其本質是一個僞文件(實爲內核緩衝區)spa

2. 由兩個文件描述符引用,一個表示讀端,一個表示寫端。code

3. 規定數據從管道的寫端流入管道,從讀端流出。blog

管道的原理: 管道實爲內核使用環形隊列機制,藉助內核緩衝區(4k)實現。隊列

管道的侷限性:進程

① 數據本身讀不能本身寫。ip

② 數據一旦被讀走,便不在管道中存在,不可反覆讀取。字符串

③ 因爲管道採用半雙工通訊方式。所以,數據只能在一個方向上流動。string

④ 只能在有公共祖先的進程間使用管道。it

常見的通訊方式有,單工通訊、半雙工通訊、全雙工通訊。

pipe函數

建立管道

    int pipe(int pipefd[2]); 成功:0;失敗:-1,設置errno

函數調用成功返回r/w兩個文件描述符。無需open,但需手動close。規定:fd[0] → r; fd[1] → w,就像0對應標準輸入,1對應標準輸出同樣。向管道文件讀寫數據實際上是在讀寫內核緩衝區。

管道建立成功之後,建立該管道的進程(父進程)同時掌握着管道的讀端和寫端。如何實現父子進程間通訊呢?一般能夠採用以下步驟:

1. 父進程調用pipe函數建立管道,獲得兩個文件描述符fd[0]、fd[1]指向管道的讀端和寫端。

2. 父進程調用fork建立子進程,那麼子進程也有兩個文件描述符指向同一管道。

3. 父進程關閉管道讀端,子進程關閉管道寫端。父進程能夠向管道中寫入數據,子進程將管道中的數據讀出。因爲管道是利用環形隊列實現的,數據從寫端流入管道,從讀端流出,這樣就實現了進程間通訊。

    練習:父子進程使用管道通訊,父寫入字符串,子進程讀出並,打印到屏幕。 【pipe.c】

思考:爲甚麼,程序中沒有使用sleep函數,但依然能保證子進程運行時必定會讀到數據呢?

#include <unistd.h>
 
#include <string.h>
 
#include <stdlib.h>
 
#include <stdio.h>
 
#include <sys/wait.h>
 
 
void sys_err(const char *str)
 
{
 
perror(str);
 
exit(1);
 
}
 
 
int main(void)
 
{
 
pid_t pid;
 
char buf[1024];
 
int fd[2];
 
char *p = "test for pipe\n";
 
 
if (pipe(fd) == -1)
sys_err("pipe");
 
 
pid = fork();
 
if (pid < 0) {
 
sys_err("fork err");
 
} else if (pid == 0) {
 
close(fd[1]);
 
int len = read(fd[0], buf, sizeof(buf));
 
write(STDOUT_FILENO, buf, len);
 
close(fd[0]);
 
} else {
 
close(fd[0]);
 
write(fd[1], p, strlen(p));
 
wait(NULL);
 
close(fd[1]);
 
}
 
 
return 0;
 
}

 

 

 

管道的讀寫行爲

    使用管道須要注意如下4種特殊狀況(假設都是阻塞I/O操做,沒有設置O_NONBLOCK標誌):

1. 若是全部指向管道寫端的文件描述符都關閉了(管道寫端引用計數爲0),而仍然有進程從管道的讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會返回0,就像讀到文件末尾同樣。

2. 若是有指向管道寫端的文件描述符沒關閉(管道寫端引用計數大於0),而持有管道寫端的進程也沒有向管道中寫數據,這時有進程從管道讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會阻塞,直到管道中有數據可讀了纔讀取數據並返回。

3. 若是全部指向管道讀端的文件描述符都關閉了(管道讀端引用計數爲0),這時有進程向管道的寫端write,那麼該進程會收到信號SIGPIPE,一般會致使進程異常終止。固然也能夠對SIGPIPE信號實施捕捉,不終止進程。具體方法信號章節詳細介紹。

4. 若是有指向管道讀端的文件描述符沒關閉(管道讀端引用計數大於0),而持有管道讀端的進程也沒有從管道中讀數據,這時有進程向管道寫端寫數據,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入數據並返回。

總結:

① 讀管道: 1. 管道中有數據,read返回實際讀到的字節數。

2. 管道中無數據:

(1) 管道寫端被所有關閉,read返回0 (好像讀到文件結尾)

  (2) 寫端沒有所有被關閉,read阻塞等待(不久的未來可能有數據遞達,此時會讓出cpu)

    ② 寫管道: 1. 管道讀端所有被關閉, 進程異常終止(也可以使用捕捉SIGPIPE信號,使進程不終止)

2. 管道讀端沒有所有關閉:

(1) 管道已滿,write阻塞。

(2) 管道未滿,write將數據寫入,並返回實際寫入的字節數。

 

 

 

 

 

父進程建立的兩個子進程之間進行通訊

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc ,char *argv[]){
char buf[4];
int fd[2];
if(pipe(fd)==-1){
printf("pipe failed\n");
}
int rc1=fork();

if(rc1==0){
close(fd[0]);
write(fd[1],"1234",4);
printf("rc1 write ok\n");
exit(1);
}else{
int rc2=fork();
if(rc2==0){

close(fd[1]);
read(fd[0],buf,4);
int i=0;
for(i;i<4;i++){
printf("rc2 : buf[%d]=%c\n",i,buf[i]);

}
exit(1);
}

}




return 0;}








[root@localhost codec5]# rc1 write ok
rc2 : buf[0]=1
rc2 : buf[1]=2
rc2 : buf[2]=3
rc2 : buf[3]=4
相關文章
相關標籤/搜索