匿名管道和有名管道

1. 管道的概念

本质:  
        内核缓冲区 
伪文件 - 不占用磁盘空间
特点:
两部分:
读端,写端,对应两个文件描述符
数据写端流入, 读端流出
操作管道的进程被销毁之后,管道自动被释放了
管道读写两端默认是阻塞的

2. 管道的原理

内部实现方式:队列
环形队列
特点:先进先出
缓冲区大小:
默认4k,通过ulimit -a查看

3. 管道的局限性

队列:
数据只能读取一次,不能重复读取
半双工:
数据的传输是单向的
匿名管道:
适用于有血缘关系的进程

4. 创建匿名管道

int pipe(int fd[2]);
fd- 传出参数
fd[0] - 读端
fd[1] - 写端

5. 父子进程使用管道通信

父进程读,要关闭写端
子进程写,要关闭读端

6. 管道的读写行为

读操作
有数据
read(fd) - 正常读,返回读出的字节数
无数据
写端全部关闭
read解除阻塞,返回0
相当于读文件读到了尾部
没有全部关闭
read阻塞
写操作
读端全部关闭
管道破裂,进程被终止,因为内核给当前进程发信号SIGPIPE,默认就是结束进程
读端没全部关闭
缓冲区写满了,write 阻塞,等待读端读走
缓冲区没有满,write继续写
如何设置非阻塞?
默认读写两端都阻塞
设置读端为非阻塞pipe(fd)
fcntl(变参函数)
设置方法:
获取原来的flags
int flags = fcntl(fd[0], F_GETFL);
// 设置新的flags
flag |= O_NONBLOCK;
// flags = flags | O_NONBLOCK;

fcntl(fd[0], F_SETFL, flags);

代码举例:

父子进程利用管道通信

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

/*
功能:父子间进行通信,实现ps aux | grep bash
自己通过代码进行实现的话,首先需要一个管道和两个进程
ps aux把数据写入管道,grep bash从管道读取数据
*/
int main(int argc, const char* argv[])
{       
        //需要管道
        int fd[2];
        int ret=pipe(fd);
        if(ret==-1)
        {       
                perror("pipe error");
                exit(1);
        }
        //创建子进程
        pid_t pid=fork();
        if(pid==-1)
        {       
                perror("fork error");
                exit(1);
        }
        
        //ps aux | grep bash
        //父进程 ps aux,写管道,关闭终端
        if(pid>0)
        {       
                close(fd[0]);
                //数据默认输出到终端,现在要让数据写到管道里面去
                //数据写到管道,STDOUT_FILENO指向管道的写端
                dup2(fd[1],STDOUT_FILENO);
                execlp("ps","ps","aux",NULL);
                perror("execlp ps");
                exit(1);
        }       
        //子进程 grep bash 从管道中搜索 读管道 关闭写端
        else if(pid==0)
        {
                close(fd[1]);
                dup2(fd[0],STDIN_FILENO);
                execlp("grep","grep","bash",NULL);
                perror("execlp grep");
                exit(1);
        }       
        return 0;
}       

FIFO

1. 特点

有名管道
在磁盘上有这样一个文件 ls -l,用p打头标识
伪文件,在磁盘大小永远为0,因为实际操作的是内核缓冲区
在内核中有一个对应的缓冲区
半双工的通信方式

2. 使用场景

没有血缘关系的进程间通信

3. 创建方式

命令:mkfifo 管道名
函数:mkfifo
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo( const char *pathname, mode_t mode );
mkfifo函数需要两个参数,第一个参数(pathname)是将要在文件系统中创建的一个专用文件。第二个参数(mode)用来规定FIFO的读写权限。Mkfifo函数如果调用成功的话,返回值为0;如果调用失败返回值为-1

4. fifo文件可以使用IO函数进行操作

open/close
read/write

5. 进程间通信

6.总结

mkfifo函数的作用是在文件系统中创建一个文件,该文件用于提供FIFO功能,即命名管道。对文件系统来说,匿名管道是不可见的,它的作用仅限于在父进程和子进程两个进程间进行通信。而命名管道是一个可见的文件,因此,它可以用于任何两个进程之间的通信,不管这两个进程是不是父子进程,也不管这两个进程之间有没有关系。

代码举例:

读端
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>

//服务端
int main(int argc, const char* argv[])
{
        if(argc<2)
        {
                printf("./a.out fifoname\n");
                exit(1);
        }
        //判断文件是否存在
        int ret=access(argv[1],F_OK);
        if(ret==-1)
        {
                int r=mkfifo(argv[1],0664);
                if(r==-1)
                {
                        perror("mkfifo error");
                        exit(1);
                }
                printf("有名管道创建成功,可以开始通信了\n");
        }

        int fd=open(argv[1],O_RDONLY);
        if(fd==-1)
        {
                perror("open error");
                exit(1);
        }

        char recvbuf[1024];
        while(1)
        {
                memset(recvbuf,0,sizeof(recvbuf));
                int ret=read(fd,recvbuf,sizeof(recvbuf));
                fputs(recvbuf,stdout);
        }

        close(fd);
}
写端
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>

//客户端
int main(int argc, const char* argv[])                                                                          
{       
        if(argc<2)                                                                                              
        {       
                printf("./a.out fifoname\n");                                                                   
                exit(1);                                                                                        
        }                                                                                                       
        
        //判断文件是否存在
        int ret=access(argv[1],F_OK);                                                                           
        if(ret==-1)                                                                                             
        {       
                int r=mkfifo(argv[1],0664);                                                                     
                if(r==-1)                                                                                       
                {       
                        perror("mkfifo error");                                                                 
                        exit(1);                                                                                
                }
                printf("有名管道%s创建成功\n",argv[1]);                                                         
        }                                                                                                       
        
        int fd=open(argv[1],O_WRONLY);                                                                          
        if(fd==-1)                                                                                              
        {       
                perror("open error");                                                                           
                exit(1);                                                                                        
        }                                                                                                       
        
        char sendbuf[1024]={0};
        while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)                                                       
        {       
                write(fd,sendbuf,strlen(sendbuf));
                memset(&sendbuf,0,sizeof(sendbuf));
        }
        close(fd);
        return 0;
}

猜你喜欢

转载自blog.csdn.net/wk_bjut_edu_cn/article/details/80960499