• 【Linux】IPC主题一 -------- 管道


    一、IPC简介

                Linux 环境下,进程地址控件相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程 1 把数据从用户空间拷贝到内核缓冲区,进程 2 在从内核缓冲区把数据读走,内核提供的这种机制称为 进程件通信(IPC)。

         

                在进程空间完成数据的传送需要借助操作系统提供的特殊方法,如:文件、管道、信号、共享内存、消息队列、套接字、命名管道。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信的方式有:

                (1)、管道(使用最简单)

                (2)、信号(开销最小)

                (3)、共享映射区(无血缘关系)

                (4)、本地套接字 (最稳定)

    二、管道(pipe)

           1、管道概念:

                        管道是一种最基本的 IPC 机制,作用于血缘关系的进程之间,完成数据传递。调用 pipe 系统函数即可创建一个管道。有如以下特质:

                         (1)、本质上是一个伪文件 (实为内核缓冲区)

                         (2)、有两个文件描述符引用,一个表示读端,一个表示写端。

                         (3)、规定数据从管道的写端流入管道,从读端流出

           2、管道的原理:管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。

           3、管道的局限性:

                          (1)、数据自己读不能写。

                          (2)、数据一旦被读走,便不在管道中存在,不可反复读取 。

                          (3)、由于管道采用半双双工通信方式。因此,数据只能在一个方向上流动。

                          (4)、只能在共有祖先的进程间使用管道。

                    常见的通信方式有:单工通信,半双工通信,全双工通信。

      4、使用管道在父子进程间进行通信(数据传输):

    #include<unistd.h>
    #include<stdlib.h>
    #include<string.h>
    #include<fcntl.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<stdio.h>


    int main(void)
    {
    int fd[2];

    pid_t pid;

    int ret = pipe(fd);

    if( ret == -1 )
    {
    perror("pipe error");
    exit(1);
    }
    pid = fork();

    if( pid == 0 ) // 子进程读数据
    {
    // sleep(3);
    close(fd[1]); // 关闭写管道的写文件描述符

    char buf[1024]="";

    ret = read(fd[0],buf,sizeof(buf));
    if( ret == 0 )
    {
    printf("管道中的数据已经读完 ");
    }

    // 把缓存 buf 中的数据显示到屏幕上
    write(STDOUT_FILENO,buf,ret);
    }
    else // 父进程 写数据
    {
    close(fd[0]); // 关闭管道的读文件描述符

    char buf[1024]="asdada";

    printf("Please input data: ");
    scanf("%s",buf);
    buf[strlen(buf)]=' ';
    // 把从标准输入中读取的数据写入管道中
    write(fd[1],buf,strlen(buf));
    }
    wait(NULL);
    }

    三、有名管道(FIFO)

            无名管道应用有一个重大的限制是它没有名字,因此,只能用于具有亲缘关系的父进程(父子进程)通信。为此 Linux 引入了 有名管道(FIFO)。FIFO 不同与无名管道之处自在于它提供了一个路径名与之关联,以管道类型的文件形式存放于文件系统中,这样,即使通信双方不存在亲缘关系,也可以用有名管道文件进行通信。

            有名管道通过 mkfifo 来创建,其函数原型如下:

      

    1. #include <sys/types.h>
    2. #include <sys/stat.h>
        int mkfifo(const char *pathname, mode_t mode);

      该函数的第一个参数是普通的路径名,也就是创建后的 FIFO 的名字,第二个参数类似有系统调用 open 中的 mode 参数。如果调用 mkfifo 时的第一个参数是一个已经存在的路径名,会返回一个 EXIST 错误,所以典型的调用代码会首先检查检查返回该错误,如果确实返回该错误,那么只要打开FIFO的函数就可以了。一般文件的 I/O 函数都可用于 FIFO,如 close 、read、write等。
               打开 FIFO 文件时需要注意一个问题,程序不能以 O_RDWR 模式打开 FIFO,因为管道是半双工通信的,在同一时段之内数据流向是单向的,如果在程序间需要双向数据传递的话,可以同时使用一对 FIFO管道,一个方向配一个。ca

               对于 FIFO 文件而言,读写是阻塞的,也就是说如果打卡了一个文件 空白的 FIFO 文件并进行 read 操作,只有在文件上有数据的时候(另一个程序才能执行 write)才能执行,而 没有数据流入的话,进程会阻塞在 read 上。执行 write 操作也是一样的。FIFO 文件内不存放任何数据,它只是一个用于传输的媒介。

              以下两个程序,一个是 mkfifo_w.c,它会创建FIFO,并尽快的写入数据。另一个是 mkfifo_r.c ,它是从 FIFO 文件中读取数据。

      

    /*
    mkfifo_w.c 这个程序主要是从输入中写数据到往有名管道中
    */

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

    int main(void)
    {
    int ret ;

    char *fifoname="myfifo";
    int fd;
    int open_mode = O_WRONLY;

    char buf[1024]="";
    umask(0);
    // 创建有名管道
    ret = mkfifo(fifoname,0777);
    if( ret == -1 )
    {
    perror("mkfifo error");
    exit(1);
    }

    printf("mkfifo creat success : ret = %d ",ret);

    // 打开有名管道,并且只能只读或者只写的方式打开管道,因为有名管道是半双工通信方式
    // 所以只能以只读方式打开或者以只写方式打开
    fd = open(fifoname,open_mode);

    if( fd == -1 )
    {
    perror("open error");
    exit(1);
    }

    printf("open %s success ",fifoname);
    // 往管道中写数据

    printf("Please input data: ");

    // 网缓冲中写数据
    scanf("%s",buf);
    write(fifoname,buf,strlen(buf));

    printf("buf = [%s] ",buf);
    printf(" processs %d finish ",getpid());

    close(fd);
    return 0;
    }

    ————————————————

    /*
    mkfifo_r.c 这个程序主要是从有名管道中读取数据并且打印到屏幕上
    */

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

    int main(void)
    {
    int ret;

    char fifoname="myfifo";
    int fd;
    int open_mode = O_RDONLY;

    char buf[1024]="";

    // 打开有名管道,并且只能只读或者只写的方式打开管道,因为有名管道是半双工通信方式
    // 所以只能以只读方式打开或者以只写方式打开
    fd = open(fifoname,open_mode);

    if( fd == -1 )
    {
    perror("open error");
    exit(1);
    }

    // 网缓冲区中读取数据,然后打印到屏幕上

    while( (read(fd,buf,sizeof(buf) ) > 0 ))
    {

    printf("Read data is [ %s ] ",buf);
    }

    printf(" processs %d finish ",getpid());

    close(fd);

    return 0;
    }
    ————————————————
    两个程序使用的都是阻塞模式的FIFO文件,先启动 mkfifo_w.c,,它将阻塞并等待有进程打开这个 FIFO文件,当 mkfifo_r.c程序打开的时候,写进程解除阻塞状态,并开始向管道中写,同时,读进程也从管道中读取。

    共享内存:https://blog.csdn.net/t_x_l_/article/details/72903253

    信号量:https://blog.csdn.net/t_x_l_/article/details/72876995

        https://blog.csdn.net/qq_35396127/article/details/78769675

  • 相关阅读:
    阿里架构师用3点讲透数据中台,这些都是你没看过的
    深度学习入门笔记(八):深层网络的原理
    创建一个最基本的SpringMvc项目
    区块链隐私资源
    weblogic wlst 例子
    如何找靠谱的2020网赚兼职?可信度高的网赚兼职推荐
    thinkphp链接多个数据库时怎么调用M方法?
    thinkphp链接多个数据库时怎么调用M方法?
    thinkphp链接多个数据库时怎么调用M方法?
    thinkphp链接多个数据库时怎么调用M方法?
  • 原文地址:https://www.cnblogs.com/wssongyue/p/13379427.html
Copyright © 2020-2023  润新知