概述
管道的概念:
管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:
1. 其本质是一个文件(实为内核缓冲区)
2. 由两个文件描述符引用,一个表示读端,一个表示写端。
3. 规定数据从管道的写端流入管道,从读端流出。
管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
管道的局限性:
① 数据自己读不能自己写。
② 数据一旦被读走,便不在管道中存在,不可反复读取。
③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
④ 只能在有公共祖先的进程间使用管道。
常见的通信方式有,单工通信、半双工通信、全双工通信。
1、管道的创建
#include <unistd.h>
int pipe(int pipefd[2]);
linux下创建管道可以通过函数pipe来完成。该函数如果调用成功,数组中将包含两个新的文件描述符。
管道两端可分别用描述符fd[0] 以及fd[1]来描述。需要注意的是,管道两端的任务是固定的,一端只能用于读,由描述符fd[0]表示,称其为管道读端;
另一端只能用于写,由描述符fd[1]来表示,称其为管道写端。如果试图从管道写端读数据,或者向管道读端写数据都将导致出错。
管道是一种文件,因此对文件操作的I/O函数都可以用于管道,如read,write等。
管道一旦创建成功,就可以作为一般的文件来使用,对一般文件进行操作的I/O函数也适用于管道文件。
------------------------------------------------------------------------------------------
管道的一般用法是,进程在使用fork函数创建子进程前先创建一个管道,该管道用于在父子进程间通信,然后创建子进程,
之后父进程关闭管道的读端,子进程关闭管道的写端。父进程负责向管道写数据而子进程负责读数据。当然父进程可以关闭管道的写端而子进程关闭管道的读端。
这样管道就可以用于父子进程间的通信,也可以用于兄弟进程间的通信。
2、从管道中读数据
如果某个进程要读取管道中的数据,那么该进程应当关闭fd[1],同时向管道写数据的进程应当关闭fd[0]。因为管道只能用于具有亲缘关系的进程间的通信,在各个程进行通信时,
他们共享文件描述符。在使用前,应及时地关闭不需要的管道的另一端,以避免意外错误的发生。
进程在管道的读端读数据时,如果管道的写端不存在,则读进程认为已经读到了数据的末尾,读函数返回读出的字节数为0;管道的写端如果存在,且请求读取的字节数大于
PIPE_BUF,则返回管道中现有的所有数据;如果请求的字节数不大于PIPE_BUF,则返回管道中现有的所有数据(此时,管道中数据量小于请求的数据量),或者返回请求的
字节数(此时,管道中数据量大于等于请求的数据量)。
注意:PIPE_BUF在/usr/include/linux/limits.h中定义,不同的内核版本可能会有所不同。
3、向管道写数据
如果某进程希望向管道中写入数据,那么该进程应当关闭fd[0]文件描述符,同时管道另一端的进程关闭fd[1]。向管道中写入数据时,linux不保证写入的原子性(原子性是指操作在
任何时候都不能被任何原因所打断,操作要么不做要么就一定完成)。管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么
写操作将一直被阻塞等待。
在写管道时,如果要求写的字节数小于等于PIPE_BUF,则多个进程对同一管道的写操作不会交错进行。但是,如果有多个进程同时写一个管道,而且某些进程要求写入的字节数超过
PIPE_BUF所能容纳时,则多个写操作的数据可能会交错。
注意:
只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道写入数据的进程将收到内核传来的SIGPIPE信号。应用程序可以处理也可以忽略该信号,如果忽略该信号或者
从其处理程序返回,则write出错,错误码为EPIPE。
必须在系统调用fork之前调用pipe,否则子进程将不会继承管道的文件描述符。
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char* str = "i am parent";
char buf[4];
bzero(buf, sizeof(buf));
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if(0 == pid)//child read
{
sleep(1);//等待父进程向管道写入数据
printf("i am child, read to readn");
close(fd[1]);
while(read(fd[0], buf, sizeof(buf)-1) > 0)
{
//printf("len=%d, %sn", strlen(buf), buf);
write(STDOUT_FILENO, buf, strlen(buf));
bzero(buf, sizeof(buf));
}
write(STDOUT_FILENO, "n", 1);
close(fd[0]);
exit(0);
}
else//parent write
{
printf("i am parent, read to readn");
close(fd[0]);
write(fd[1], str, strlen(str));
close(fd[1]);
wait(NULL);//
exit(0);
}
}
最后
以上就是贤惠秀发为你收集整理的linux 管道的创建和读写 pipe的全部内容,希望文章能够帮你解决linux 管道的创建和读写 pipe所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复