赞
踩
目录
管道是Linux中的最古老的通信方式;
我们把一个进程链接到另一个进程的一个数据流称为一个"管道";
头文件: #include <unistd.h>
功能:创建一无名管道原型int pipe(int fd[2]);
参数fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端返回值:成功返回0,失败返回错误代码
通常用fork函数来共享管道;接下来我们来实现一下匿名管道的基本读写的实现:
不关闭描述符
- #include <stdio.h>
- #include <unistd.h>
- #include <string.h>
- #include <wait.h>
-
- int main(){
- int fd[2];
- int ret=pipe(fd);
- if(ret<0){
- perror("pipe");
- return 0;
- }
- pid_t pid=fork();
- if(pid<0){
- perror("fork");
- return 0;
- }else if(pid==0){
- //child
- //子进程进行接受
- char buf_read[1024]={0};
- read(fd[0],buf_read,sizeof(buf_read)-1);
- printf("The content is :%s\n",buf_read);
- }else{
- //father
- //父进程发送数据
- char buf_write[]="success!";
- write(fd[1],buf_write,strlen(buf_write));
- wait(NULL);
- }
- return 0;
- }
运行结果
我们可以看出来在父进程向管道写入信息,在子进程读取管道信息.
关闭描述符
- #include <stdio.h>
- #include <unistd.h>
- #include <string.h>
- #include <wait.h>
-
- int main(){
- int fd[2];
- int ret=pipe(fd);
- if(ret<0){
- perror("pipe");
- return 0;
- }
- pid_t pid=fork();
- if(pid<0){
- perror("fork");
- return 0;
- }else if(pid==0){
- //child
- //子进程进行接受
- close(fd[1]);
- char buf_read[1024]={0};
- read(fd[0],buf_read,sizeof(buf_read)-1);
- printf("The content is :%s\n",buf_read);
- }else{
- //father
- //父进程发送数据
- close(fd[0]);
- char buf_write[]="success!";
- write(fd[1],buf_write,strlen(buf_write));
- wait(NULL);
- }
- return 0;
- }
运行结果
我们可以看出依旧可以进行读写~原因是什么呢?
首先父进程创建管道,系统默认打开三个文件:标准输入,标准输出,和标准错误;创建管道则有fd[0],fd[1];分别表示管道的读端和写端;
然后父进程创建子进程,子进程拷贝父进程,也拷贝了管道信息;所以父子进程共享管道;
父进程关闭读端,子进程关闭写端,但是管道是共享的,依旧可以进行信息交互;
看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了“Linux一切皆文件思想”。
当没有数据可读时O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
当管道满的时候O_NONBLOCK disable: write调用阻塞,直到有进程读走数据O_NONBLOCK enable:调用返回-1,errno值为EAGAIN如果所有管道写端对应的文件描述符被关闭,则read返回0
如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
fcntl函数原型:
- #include<unistd.h>
- #include<fcntl.h>
- int fcntl(int fd, int cmd);
- int fcntl(int fd, int cmd, long arg);
- int fcntl(int fd, int cmd ,struct flock* lock);
(1)F_DUPFD
与dup函数功能一样,复制由fd指向的文件描述符,调用成功后返回新的文件描述符,与旧的文件描述符共同指向同一个文件。
(2)F_GETFD
读取文件描述符close-on-exec标志
(3)F_SETFD
将文件描述符close-on-exec标志设置为第三个参数arg的最后一位
(4)F_GETFL
获取文件打开方式的标志,标志值含义与open调用一致
(5)F_SETF
设置文件打开方式为arg指定方式
设置读端为非阻塞:
- #include <stdio.h>
- #include <unistd.h>
- #include <fcntl.h>
-
- int main(){
- int fd[2];
- int ret=pipe(fd);
- if(ret<0){
- perror("pipe");
- return 0;
- }
- pid_t pid=fork();
- if(pid<0){
- perror("fork");
- return 0;
- }else if(pid==0){
- //child
- //关闭写端
- //设置读端为非阻塞属性
- //读
- close(fd[1]);
- int flag=fcntl(fd[0],F_GETFL);
- fcntl(fd[0],F_SETFL,flag|O_NONBLOCK);
- char buf_read[1024]={0};
- ssize_t read_size=read(fd[0],buf_read,sizeof(buf_read)-1);
- printf("read_size:%ld\n",read_size);
- perror("read");
-
- }else{
- //father
- //关闭读端
- //写关闭/不关闭
- close(fd[0]);
- close(fd[1]);
- }
- while(1){
- sleep(1);
- }
- return 0;
- }
运行结果为:
设置写端非阻塞:
- #include <stdio.h>
- #include <unistd.h>
- #include <fcntl.h>
-
- int main(){
- int fd[2];
- int ret=pipe(fd);
- if(ret<0){
- perror("pipe");
- return 0;
- }
- pid_t pid=fork();
- if(pid<0){
- perror("fork");
- return 0;
- }else if(pid==0){
- //child
- //关闭子进程读端
- //设置子进程写段为非阻塞
- //子进程写
- close(fd[0]);
- int flag=fcntl(fd[1],F_GETFL);
- fcntl(fd[1],F_SETFL,flag|O_NONBLOCK);
- ssize_t write_size;
- while(1){
- write_size=write(fd[1],"a",1);
- if(write_size<0){
- break;
- }
- }
- printf("write_size:%ld\n",write_size);
- }else{
- //father
- //关闭父进程写端
- //关闭父进程读端/不关闭
- close(fd[1]);
- //close(fd[0]);
- while(1){
- sleep(1);
- }
- }
- return 0;
- }
运行结果:
只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;
通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
管道提供流式服务一般而言,进程退出,管道释放,所以管道的生命周期随进程一般而言,内核会对管道操作进行同步与互斥管道是半双工的;
数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
命名管道是一种特殊类型的文件
$ mkfifo pipe1
执行结果:
也可以程序创建
函数原型:
- #include<sys/types.h>
- #include<sys/stat.h>
- int mkfifo(const char *filename,mode_t mode);
代码如下:
- #include <stdio.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- int main(){
- mkfifo("pipe2",0664);
- return 0;
- }
运行结果
如果当前打开操作是为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
O_NONBLOCK enable:立刻返回成功如果当前打开操作是为写而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
匿名管道由pipe函数创建并打开。
命名管道由mkfifo函数创建,打开用openFIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。