Linux 进程间通信之管道

Linux 进程间通信之管道

1.什么是管道

管道可以用来在两个进程之间传递数据,其实我们已经用过管道了,比如 ps -ef | grep bash
| 就是个管道,其作用就是将 ps 命令的结果写入管道文件,然后 grep 再从管道文件中读出该数据进行过滤。
管道文件的读写是在内存上进行的。管道文件有一个读端还有一个写端,如果对一个管道文件只进行读而没有写,或者只进行写而没有读,就会产生阻塞的。就像两个人要打电话,两个人要同时打开手机进行通信,自己是没有办法通信的,所以对一个管道文件进行操作,就必须同时有两个进程。

2.有名管道

有名管道可以在任意两个进程之间通信

2.1有名管道的创建

1)命令创建

mkfifo
FIFO

2)系统调用创建

#include <sys/types.h>
#include <sys/stat.h>
/ /filename 是管道名 mode 是创建的文件访问权限
int mkfifo(const char *filename, mode_t mode);

示例:
在这里插入图片描述

2.2 创建一个进程 a 要将从键盘获取的数据循环传递给另一个进程 b

我们已经在当前文件夹下创建了一个名fifo的管道文件。
创建a.cC文件对fifo文件进行写操作。代码如下:

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

int main()
{
    int fdw = open("fifo",O_WRONLY);
    assert( fdw != -1 );

    printf("fdw = %d\n",fdw);

    printf("请输入:\n");
    char buff[128] = {0};
    fgets(buff,128,stdin);

    write(fdw,buff,strlen(buff));
    close(fdw);

    exit(0);
}

创建a.cC文件对fifo文件进行读操作。代码如下:

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

int main()
{
    int fdr = open("fifo",O_RDONLY);
    assert( fdr != -1 );

    printf("fdr = %d\n",fdr);

    char buff[128] = {0};

    int n = read(fdr,buff,127);
    printf("buff = %s,n = %d\n",buff,n);
    close(fdr);

分别编译a.c和b.c然后分别执行可执行文件a和可执行文件b
在这里插入图片描述
我们发现两个进程产生了阻塞。**因为对管道文件操作,同时需要两个进程。**所以我们新打开一个终端,同时执行可执行文件a和可执行文件b。
在这里插入图片描述
两个进程没有产生阻塞。接下来向管道中写入hello,观察。
在这里插入图片描述
写端已写入,同时读端已读到。(注意:当管道文件内容为空,读端阻塞,当管道文件内容写满,写端阻塞。)
改进以上a.c和b.c可以在写端一直输入,读端一直读管道中的内容,当输入end时结束。
改进后a.c代码如下:

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

int main()
{
    int fdw = open("fifo",O_WRONLY);
    assert( fdw != -1 );

    printf("fdw = %d\n",fdw);

    while(1)
    {   
         printf("请输入:\n");
         char buff[128] = {0};
         fgets(buff,128,stdin);
        if(strncmp(buff,"end",3) == 0)
        {
           break;
        }
        write(fdw,buff,strlen(buff));
    }   
    close(fdw);

    exit(0);
}

改进后b.c代码如下:

#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>
#include<fcntl.h>

int main()
{
    int fdr = open("fifo",O_RDONLY);
    assert( fdr != -1 );

    printf("fdr = %d\n",fdr);
    while(1)
    {
        char buff[128] = {0};

        int n = read(fdr,buff,127);
        if(n == 0)
        {
            break;
        }
        printf("buff = %s\n",buff);
    }
    close(fdr);

    exit(0);
}

编译同时运行两个可执行程序a和b:
在这里插入图片描述
键盘输入end,写端结束进程,同时读端也结束,因为当写端进程结束,读端在管道中读到的内容为0,b.c中当读到内容为0时,退出循环。

那么当读端进程结束,写端进程会怎么样呢?我们实验一下!
在这里插入图片描述
我们发现当读端进程结束,写端进程也结束。这是因为:**当管道读端关闭,写端写入数据触发异常,内核就会给写端发送信号,使之进程结束。**就像两个人打电话,对方把电话挂了,通话也就结束了。

3.无名管道

无名管道只在父子进程间通信。
无名管道用pipe()创建,用man命令查看pipe方法。
在这里插入图片描述
返回值类型为int型,成功返回0,失败返回-1,参数为一个int型数组,数组大小为2,pipefd[0]是管道读端的描述符,pipefd[1]是管道写端的描述符。
代码演示:

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

int main()
{
   int pipefd[2];
   int res = pipe(pipefd);
   assert(res != -1);

   pid_t pid = fork();
   assert(pid != -1);
   if(pid == 0)
   {
      close(pipefd[1]);
      char buff[128] = {0};
      read(pipefd[0], buff,127);
      printf("child buff = %s\n",buff);
      close(pipefd[0]);
   }
   else
   {
       close(pipefd[0]);
       char buff[128] = {0};
       fgets(buff,128,stdin);
       write(pipefd[1],buff,strlen(buff));
       close(pipefd[1]);
   }
   exit(0);
}

编译运行:
在这里插入图片描述

4.管道的特点

无论有名还是无名,写入管道的数据都在内存中
管道是一种半双工的通信方式
有名管道和无名管道的区别:有名管道可以在任意进程间使用,无名管道只能在父子进程间通信

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WYSCODER

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

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

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

打赏作者

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

抵扣说明:

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

余额充值