进程间通信(IPC机制)——管道

一.管道的介绍

管道是进程间通信工具,管道包括无名管道和有名管道。前者在父子进程中流行,后者由于可独立成磁盘文件形式存在,能够被无血缘关系的进程共享管道是一种队列类型的数据结构,他的数据从一端输入,另一端输出。其最常见应用是连接两个进程的输入输出,即把一个进程的输出作为另一个进程的输入。

二.有名管道

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

1. 有名管道的创建:

有名管道的创建

  1. 命令创建: mkfifo + 管道名
  2. 系统调用创建

创建有名管道所需要的头文件:fcntl

2. 有名管道的使用举例:

进程 a 从键盘获取的数据循环传递给另一个进程 b。
我们可以用创建一个文件,a从键盘获取字符串存放到文件中,b从文件中读取。但是文件是存在磁盘上的,速度没有管道快,而且从文件读不到数据不会等待,但是管道不一样,b从管道读不到数据会阻塞,直到a又向管道中写入数据。

写端:

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

int main()
{
    
    
    int fd = open("fifo",O_WRONLY);
    assert(fd != -1);
    printf("open FIFO success\n");

    while(1)
    {
    
    
        printf("please input:\n");

        char buff[128] = {
    
    0};
        fgets(buff,128,stdin);
        write(fd,buff,strlen(buff)-1);

        if(strncmp(buff,"end",3)==0)
        {
    
    
            break;
        }
    }

    close(fd);
    exit(0);
}

读端:

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

int main()
{
    
    
    int fd = open("fifo",O_RDONLY);
    assert(fd != -1);
    printf("FIFO open success\n");

    while(1)
    {
    
    
        char buff[128] = {
    
    0};
        int n = read(fd,buff,127);

        if(n <= 0 || (strncmp(buff, "end", 3)==0))
        {
    
    
            break;
        }
        printf("%s\n",buff);
    }

    close(fd);
    exit(0);
}


(1)如果读端或者写端先打开,那么将会在open阻塞住 ,直至另一端也打开管道。
(2)写端和读端都打开后,读端会在read处阻塞,写端向管道中写入数据后,读端才正常执行read,执行完继续在read阻塞。
(3)如果读端关闭,写端在向管道输入数据后会触发信号(SIGPIPE)自动退出。
(4)如果写端关闭,读端read函数值会返回0。

三.无名管道

无名管道主要应用于父子进程间的通信。

1.无名管道的创建

使用pipe()可以对无名管道进行创建 创建成功返回 0,失败返回-1
fds[0]是管道读端的描述符
fds[1]是管道写端的描述符

2.无名管道的使用举例

代码:

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

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


    pid_t pid = fork();
    assert(pid != -1);

    if(pid == 0)
    {
    
    
    	close(fd[1]);
        char buff[128] = {
    
    0};
        read(fd[0],buff,127);
        printf("child read:%s\n",buff);
        close(fd[0]);
    }
    else
    {
    
    
    	close(fd[0]);
        char buff[128] = {
    
    0};
        printf("please input(less 128):\n");
        fgets(buff,128,stdin);
        write(fd[1],buff,strlen(buff)-1);
        close(fd[1]);
    }
    
    exit(0);
}

四.管道的实现

在这里插入图片描述
写入从头指针开始,读取从尾指针开始。写入之后,头指针挪动,读取之后尾指针挪动。
如果是头指针赶上尾指针,那么管道被写满,写就会被阻塞。如果是尾指针赶上头指针,那么管道为空,read阻塞。

五.相关面试题

  1. 管道的通信方式是什么?怎么理解管道是这种通信方式?
    管道的通信方式是半双工通信;
    由于Linux一个命令只能完成一个功能,所以一个复杂点的任务需要好几个进程协同完成,第一个进程处理结果需交给第二个进程,然后一次交给第三个,等等,像流水线完成某个商品的生产一样,这个过程只需要数据单向往下传输,所以设计的时候做成了半双工。
    当然也可以使用管道实现双工通信,需要两个管道。

  2. 三种通信方式是什么?有哪些区别?
    单工通信:传输方向只有一个方向,单工通信只有一根数据线,它也只在一个方向上进行,如打印机、电视机等。
    半双工通信:可以双向通信,但只能轮流传输,也只有一根数据线,不同于单工通信的是这根数据线即可作为发送又可作为接收,虽数据可在两个方向上传送,但通信双方不能同时收发数据。
    全双工通信:可以同时双向传输数据,数据的发送和接收用两根不同的数据线,通信双方在同一时刻都能进行发送和接收,发送和接收同时进行,没有延迟。

  3. 有名管道和无名管道的区别是什么?
    有名管道可以在任意两个进程间可以通信。
    无名管道只能在父子进程间通信。

  4. 写入管道的数据存储在哪里?
    写入管道的数据存储在内存中。

猜你喜欢

转载自blog.csdn.net/weixin_56935264/article/details/125086591