欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux — 管道

程序员文章站 2022-07-13 16:20:06
...

引入管道:

每个进程各自有不同的PCB(task_struct),各自拥有不同的用户地址空间,所以进程的运行是具有独立性的,当操作系统需要完成某项功能时需要进程间进行交互,这时内核协助各进程完成相互访问。

管道的本质:

1.管道是内核开辟的专门用来进程间通信的一块文件缓冲区,一个进程连接到另一个进程在管道中传输的数据是以字符流的形式;
2.管道是单向的,一个进程在管道的一端进行数据的读取,另一个进程在管道的另一端进行数据的写入,当一个进程将管道中的数据读走之后,数据就从管道中删除;
3.管道内部自动提供同步机制,使读写有序;
4.当进程退出了管道的生命周期也就结束了,内核会对管道操作进行同步和互斥;
5.如果想要进行双向通信,需要建立两个管道。

管道的分类
管道分为匿名管道和命名管道。

匿名管道
匿名管道应用于具有血缘关系的父子进程之间,当一个进程创建一个管道之后,再进行fork创建子进程,这时父进程和子进程可以通过管道进行通信。

创建一个匿名管道:

函数—— int pipe(int fd[2])
参数:fd—代表文件描述符数组;fd[0]代表读端,fd[1]代表写端。
返回值:成功返回0,失败返回错误代码。

Linux — 管道

//mypipe.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main()
{
    //创建管道
        int fd[2];
        if(pipe(fd)==-1){
                perror("pipe"),exit(1);
        }
    //创建子进程
        pid_t pid;
        pid=fork();
        if(pid==-1){
                perror("fork"),exit(1);
        }else if(pid==0){
            //子进程在管道进行写数据
                close(fd[0]);
                size_t size=write(fd[1],"PIPE",4);
                if(size==0){
                perror("write"),exit(1);
                }
                close(fd[1]);
        }else{
            //父进程从管道读数据
            close(fd[1]);
                char buf[10]={0};
                size_t size=read(fd[0],buf,10);
                if(size==0){
                        perror("read"),exit(1);
                        }
                close(fd[0]);
                printf("%s\n",buf);
        }
        return 0;
}

顺便写一下Makefile:

//依赖文件
mypipe:mypipe.c
    //编译
    gcc -o mypipe mypipe.c
     // gcc -o aaa@qq.com $^
.PHONY:clean //创建伪目标
clean:
    //保证执行完之后删除目标文件
    rm -f mypipe

父进程从管道中读出了子进程向管道中写的数据:
Linux — 管道

匿名管道的读写规则:
1.当所有管道写端对应的文件描述符被关闭,则read返回0;
2.当所有进程读端对应的文件描述符被关闭,则write操作返回信号SIG_PIPE,进而导致write进程退出;
3.当写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性;当写入的数据量大于PIPE_BUF时Linux将不能保证写入的原子性;
4.当管道里没有数据,进程有两种解决方案:进程暂停运行,read调用阻塞,直到有数据再停止阻塞;进程返回-1,erron的值为EAGAIN;
5.当管道中数据满的时候,进程有两种解决方案:进程暂停运行,write调用阻塞,直到有数据被读走;进程返回-1,erron的值为EAGAIN。

命名管道:
命名管道适用于不相关进程之间进行通信,是一种特殊的文件。

创建命名管道
利用命令进行创建:mkfifo
Linux — 管道
(提醒一下使用rm pipename 可以删除命名管道。)
利用函数进行创建:

int mkfifo(const char* filename,mode_t mode)
参数:filename是创建管道的名字,mode是文件权限(mode%~umask)。
函数调用成功返回0,失败返回-1

//server.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main()
{
        umask(0);
        //创建管道
        if(mkfifo("./myfifo",S_IFIFO|0666)==-1){
                perror("myfifo"),exit(1);
        }

        int fd=open("./myfifo",O_RDONLY);
        if(fd<0){
                perror("open"),exit(1);
        }
        char buf[1024];
        while(1){
            //从管道中读数据,读成功输出
                size_t size=read(fd,buf,sizeof(buf)-1);
                if(size<0){
                        perror("read"),exit(1);
                }else if(size==0){
                        printf("client is quit! server begin quit!\n ");
                }else{
                        buf[size]=0;
                        printf("client:%s\n",buf);
                }
        }
        close(fd);
}
//client.c
#include<string.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>

int main()
{
        int fd=open("./myfifo",O_WRONLY);
        if(fd<0){
                perror("open"),exit(1);
        }
        char buf[1024];
        while(1){
            //从键盘输入,并刷新
                printf("Please enter:\n");
                fflush(stdout);
                //将标准输入的数据读到数组中,读成功就写到管道
                size_t size=read(0,buf,sizeof(buf)-1);
                if(size>0){
                        buf[size]=0;
                        write(fd,buf,strlen(buf));
                }else{
                        perror("write"),exit(1);
                }
        }
        close(fd);
}
//Makefile
.PHONY:all
all:server client

server server.c
    gcc -o aaa@qq.com $^
client client.c
    gcc -o aaa@qq.com $^
.PHONY:clean
clean:
    rm -f server client myfifo

Linux — 管道

如何测试管道的默认大小?

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

int main()
{
        int fd[2];
        int p=pipe(fd);
        if(p==-1){
                perror("pipe"),exit(1);
        }
        pid_t pid=fork();
        long long count=0;
        if(pid==-1){
                perror("fork"),exit(1);
        }else if(pid==0){
                close(fd[0]);
                char* msg="1";
                while(1){
                        write(fd[1],msg,1);
                        ++count;
                        printf("count=%d\n",count);
                }
        }else{
                char buf[2];
                while(1){
                        memset(buf,'\0',sizeof(buf));
                }
        }
        return 0;
}

管道默认大小:
Linux — 管道

相关标签: 管道