无名管道的介绍

1. 相关概念

无名管道就是在内核空间开辟一块区域,然后会给进程两个文件描述符,只要多个进程可以得到这两个文件描述符,就可以对同一个管道进行操作

无名管道是一个半双工的通信方式,意味着有固定的读端和写端

既然无名管道给当前进程的是两个文件描述符,所以可以通过文件IO函数对其进行操作

无名管道只能用于具有亲缘关系的进程间通信,原因是因为无名管道既然给当前进程的是两个文件描述符,那么这两个文件描述符的创建只能在当前进程的用户空间,当fork之后产生的进程,会继承原本父进程的所有的空间,所以他能得到这两个文件描述符

2. 相关函数

#include <unistd.h>
int pipe(int pipefd[2]);
功能:
创建一个无名管道
参数:
pipefd:
保存两个文件描述符的数组
pipefd[0] 用于读操作
pipefd[1] 用于写操作
返回值:
成功:
0
失败:
-1

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    //创建一个无名管道
    int pipefd[2];
    if(pipe(pipefd) == -1)
    {
        perror("pipe error");
        exit(1);
    }

    //printf("pipefd[0] = %d, pipefd[1] = %d\n", pipefd[0], pipefd[1]);

    //向管道写入数据
    write(pipefd[1], "hello world", strlen("hello world"));
    write(pipefd[1], "abcdefghijklmn", strlen("abcdefghijklmn"));

    //注意:管道无法使用lseek改变文件的偏移量
    // if(lseek(pipefd[0], 10, SEEK_SET) == -1)
    // {
    //     perror("lseek error");
    //     exit(1);
    // }

    //从管道中读取数据
    char buf[32] = {0};
    read(pipefd[0], buf, 15);
    printf("buf = %s\n", buf);

    memset(buf, 0, sizeof(buf));
    read(pipefd[0], buf, 15);
    printf("buf = %s\n", buf);

    //如果管道中没有内容,读操作会阻塞
    memset(buf, 0, sizeof(buf));
    read(pipefd[0], buf, 15);
    printf("buf = %s\n", buf);

    return 0;
}

3. 无名管道的读写规律

3.1 读写端都存在,只读不写

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    //创建一个无名管道
    int pipefd[2];
    if(pipe(pipefd) == -1)
    {
        perror("pipe error");
        exit(1);
    }

    //向管道写入数据
    write(pipefd[1], "hello world", strlen("hello world"));

    //读写端都存在,只读不写
    //如果管道中有数据则正常读取
    //如果管道中没数据则读操作会一直阻塞

    //从管道中读取数据
    char buf[32] = {0};
    read(pipefd[0], buf, 15);
    printf("buf = %s\n", buf);

    memset(buf, 0, sizeof(buf));
    read(pipefd[0], buf, 15);
    printf("buf = %s\n", buf);

    return 0;
}

3.2 读写端都存在,只写不读

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    //创建一个无名管道
    int pipefd[2];
    if(pipe(pipefd) == -1)
    {
        perror("pipe error");
        exit(1);
    }

    //读写端都存在,只写不读
    //会一直往管道中写数据,当管道满时,写操作会阻塞
    //默认无名管道64k字节

    int num = 0;
    while(1)
    {
        num++;
        write(pipefd[1], "1234", 1024);
        printf("num = %d\n", num);
    }

    return 0;
}

3.3 关闭写端,只有读端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    //创建一个无名管道
    int pipefd[2];
    if(pipe(pipefd) == -1)
    {
        perror("pipe error");
        exit(1);
    }

    //向管道写入数据
    write(pipefd[1], "hello world", strlen("hello world"));

    //关闭写端,只有读端
    //如果管道中有数据则正常读取,
    //如果管道中没有数据,读操作会返回0
    close(pipefd[1]);

    //从管道中读取数据
    ssize_t bytes;
    char buf[32] = {0};
    bytes = read(pipefd[0], buf, 15);
    printf("bytes = %ld, buf = %s\n", bytes, buf);

    memset(buf, 0, sizeof(buf));
    bytes = read(pipefd[0], buf, 15);
    printf("bytes = %ld, buf = %s\n", bytes, buf);

    return 0;
}

3.4 关闭读端,只有写端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    //创建一个无名管道
    int pipefd[2];
    if(pipe(pipefd) == -1)
    {
        perror("pipe error");
        exit(1);
    }

    //关闭读端,只有写端
    //当执行第一个写操作时,当前系统中会产生一个信号SIGPIPE,
    //这个信号的名字称之为管道破裂,这个信号默认对当前进程的处理是
    //结束进程
    close(pipefd[0]);

    int num = 0;
    ssize_t bytes;
    while(1)
    {
        num++;
        puts("111111111111");
        if((bytes = write(pipefd[1], "1234", 1024)) == -1)
        {
            perror("write error");
            exit(1);
        }
        printf("bytes = %ld, num = %d\n", bytes, num);
    }

    return 0;
}

4. 使用无名管道实现进程间通信

由于无名管道是通过调用函数直接给当前进程两个文件描述符,所以没关系的进程无法获得相同的两个文件描述符,所以只有使用fork之后创建的进程才可以继承这两个文件描述符,所以无名管道只能使用在具有亲缘关系的进程间通信

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    int pipefd[2];
    if(pipe(pipefd) == -1)
    {
        perror("pipe error");
        exit(1);
    }

    pid_t pid;
    if((pid = fork()) == -1)
    {
        perror("fork error");
        exit(1);
    }
    else if(pid > 0) //父进程负责发送数据
    {
        char buf[32] = {0};
        while(1)
        {
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf) - 1] = '\0';
            write(pipefd[1], buf, sizeof(buf));
        }
    }
    else //子进程负责接收数据
    {
        char buf[32] = {0};
        while(1)
        {
            memset(buf, 0, sizeof(buf));
            read(pipefd[0], buf, sizeof(buf));
            
            printf("from parent: %s\n", buf);
        }
    }

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小徐的记事本

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值