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
相关文章
相关标签/搜索