Linux — 管道

引入管道:

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

管道的本质:

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

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

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

创建一个匿名管道:

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

这里写图片描述

//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 $@ $^
.PHONY:clean //创建伪目标
clean:
    //保证执行完之后删除目标文件
    rm -f mypipe

父进程从管道中读出了子进程向管道中写的数据:
这里写图片描述

匿名管道的读写规则:
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
这里写图片描述
(提醒一下使用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 $@ $^
client client.c
    gcc -o $@ $^
.PHONY:clean
clean:
    rm -f server client myfifo

这里写图片描述

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

#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;
}

管道默认大小:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值