Linux 进程间通信之管道

导读:本篇文章讲解 Linux 进程间通信之管道,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

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.管道的特点

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/95564.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!