进程间通信---管道

news2024/9/23 17:15:40

进程间通信


概念

进程间通信就是在不同进程之间传播或交换信息数据, 简称IPC(Interprocess communication).

意义
  • 数据传输, 资源共享
  • 事件通知, 进程控制
本质

让不同的进程看到同一份资源

管道


匿名管道

原理:

匿名管道仅限于本地父子进程之间的通信, 本质就是让两个父子进程先看到同一份被打开的文件资源, 然后父子进程就可以对该文件进行写入或是读取操作, 进而实现进程间通信.

pipe函数:

  • int pipe(int pipefd[2]); // #include <unistd.h>
  • pipefd: 输出型参数, 数组pipefd用于返回两个指向管道读端和写端的文件描述符. pipefd[0]是管道读端的文件描述符, pipefd[1]是管道写端的文件描述符.
  • return int: pipe函数调用成功时返回0, 调用失败时返回-1.
  • 注意: 管道的信道是半双工的.

使用(父进程读, 子进程写为例):

  1. 父进程通过调用pipe函数创建管道
  2. 父进程创建子进程
  3. 父进程关闭写端,子进程关闭读端
// 父进程读子进程写---父进程关闭写端,子进程关闭读端 
static void test_pipe1()
{
    // 1.父进程先调用pipe函数,创建匿名管道获取读写端的文件描述符
    int pipefd[2];
    memset(pipefd,0,sizeof(pipefd));

    int ret=pipe(pipefd);
    if(ret!=0)
    {
        perror("pipe");
        exit(1);
    }

    // 2.父进程调用fork创建子进程, 在进程创建中子进程会继承父进程的struct file, 
    // 所以父子进程的pipefd中文件描述符会标定同一个匿名管道
    pid_t id=fork();
    if(id<0)
    {
        perror("fork");
        exit(2);
    }
    
    // 3.因为管道通信是半双工的, 所以需要根据需求关闭父子进程各一个fd

    if(0==id) 
    {
        // child code
        close(pipefd[0]);
        
        int n=10;
        while(n--)
        {
            char buffer[1024];
            sprintf(buffer,"hello father, I am child: %d",n);
            write(pipefd[1],buffer,strlen(buffer));
                
            sleep(1);
        }

        close(pipefd[1]);
        exit(0);
    }

    // father code
    close(pipefd[1]);
    
    while(true)
    {
        char buffer[1024];
        memset(buffer,0,sizeof(buffer));

        ssize_t n=read(pipefd[0],buffer,sizeof(buffer)-1);
        if(n>0)
        {
            buffer[n]='\0';

            printf("chlid# %s\n",buffer);
        }
        else if(0==n)
        {
            printf("child exit");
            break;
        }
        else 
        {
            perror("read");
            exit(3);
        }
    }

    close(pipefd[0]);
    exit(0);
}

int main(int argc,char *argv[],char *env[])
{
    test_pipe1();

    return 0;
}

运行结果:

image-20230420192048772

四种特殊情况:

  1. 写端进程不写, 读端进程一直读, 那么此时会因为管道里面没有数据可读, 对应的读端进程会被挂起, 直到管道里面有数据后, 读端进程才会被唤醒.

  2. 读端进程不读, 写端进程一直写, 那么当管道被写满后, 对应的写端进程会被挂起, 直到管道当中的数据被读端进程读取后, 写端进程才会被唤醒.

  3. 写端进程将数据写完后将写端关闭, 那么读端进程将管道当中的数据读完后, 就会继续执行该进程之后的代码逻辑, 而不会被挂起.

  4. 读端进程将读端关闭, 而写端进程还在一直向管道写入数据, 那么操作系统会通过发送SIGPIPE信号给写端进程从而将写端进程杀掉.

验证第4种情况, 当写端进程一直往匿名管道写时, 读端进程把匿名管道对应的读取文件描述符关闭, 写端进程会收到什么信号(父进程写, 子进程读为例):

void sigpipe_handler(int signum)
{
    printf("收到信号: %d\n",signum);
    exit(4);
}

// 父进程写子进程读---父进程关闭读端,子进程关闭写端
static void test_pipe2()
{
    // 1.pipe
    int pipefd[2];
    int ret=pipe(pipefd);
    if(ret<0)
    {
        perror("pipe");
        exit(1);
    }

    // 2.fork
    pid_t id=fork();
    if(id<0)
    {
        perror("fork");
        exit(2);
    }

    if(0==id)
    {
        // child code
        close(pipefd[1]); // 子进程关闭写端

        int n=3;
        while(n--)
        {
            char buffer[1024];

            ssize_t n=read(pipefd[0],buffer,sizeof(buffer)-1);
            if(n>0)
            {
                buffer[n]='\0';
                printf("father# %s\n",buffer);

                // sleep(1);
            }
            else if(0==n) 
            {
                printf("father close\n");
                exit(0);
            }
            else
            {
                perror("read");
                exit(3);
            }
        }

        close(pipefd[0]);
        exit(0);
    }

    // father code
    close(pipefd[0]); // 父进程关闭读端

    // 父进程对SIGPIPE信号进行捕获
    signal(SIGPIPE,sigpipe_handler);

    // 父进程一直往匿名管道中写入数据
    int count=0;
    while(true)
    {
        char buffer[1024];
        sprintf(buffer,"I am father, pid: %d, count %d\n",getpid(),count++);

        ssize_t n=write(pipefd[1],buffer,strlen(buffer));
        if(n>0)
        {
            // printf("write n>0: n: %d\n",n);
            usleep(5000); // 父进程写慢点
        }   
        else if(0==n)
        {
            printf("write 0==n: n: %d\n",n);
            break;
        }
        else
        {
            perror("write");
            exit(5);
        }
    }

    close(1);
    exit(0);
}

运行结果:

image-20230420201150260

测试管道的大小:

// 测试管道的大小,父进程一直不读,子进程一直写
static void test_pipe_size()
{
    // 1.pipe
    int pipefd[2];
    int ret = pipe(pipefd);
    if (ret < 0)
    {
        perror("pipe");
        exit(1);
    }

    // 2.fork
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork");
        exit(2);
    }

    if (0 == id)
    {
        // child code
        close(pipefd[0]); // 子进程关闭写端
        // 子进程一直往匿名管道中写
        
        int count = 0; // 记录写入了多少个字节数
        while (true)
        {
            char one_byte = '$';
            ssize_t n = write(pipefd[1], &one_byte, 1);

            if (n > 0)
            {
                printf("count: %d bytes\n", ++count);
            }
        }

        close(pipefd[1]);
        exit(0);
    }

    // father code
    close(pipefd[1]); // 父进程关闭读端
    // 父进程一直不从匿名管道中读
    for(;;){}

    close(pipefd[0]);
    exit(0);
}

结论:我当前Linux版本(Linux VM-12-12-centos 3.10.0-1160.62.1.el7.x86_64 #1 SMP Tue Apr 5 16:57:59 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux)中管道的最大容量是65536字节. 当管道写满时, 写端进程继续写则会进入阻塞状态.

image-20230420203123618

命名管道

原理:

想要实现本地任意两个进程之间的通信, 可以通过命名管道来实现. 命名管道是一种特殊类型的文件(管道文件), 两个进程通过命名管道的文件路径打开同一个管道文件, 此时这两个进程也就看到了同一份资源,进而就可以进行进程间通信了。

mkfifo函数:

int mkfifo(const char *pathname, mode_t mode);

pathname: 根据传入pathname所指定的目录下创建命名管道文件, 默认在当前进程的工作目录下创建.

  1. /home/yx/code/ipc_fifo/fifofile: 表示在/home/yx/code/ipc_fifo目录下创建fifofile文件
  2. fifofile: 表示在默认目录下创建fifofile文件

mode: 表示创建命名管道文件的默认权限, 受usmask权限掩码影响, 实际权限=mode&(~umask);

return int: 创建成功返回0, 创建失败返回-1.

使用:

comm.h

#ifndef __COMM_H__
#define __COMM_H__

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

const char * FIFOFILEPATH="fifofile";
const int DEFAULTMODE=0666;
const int BUFFERSIZE=1024;

#endif

fifoServer.c

#include "comm.h"

static void server()
{
    // 1.调用mkfifo创建管道文件
    int ret=mkfifo(FIFOFILEPATH,DEFAULTMODE);
    if(ret<0) // 成功返回0,失败返回-1
    {
        perror("mkfifo");
        exit(1);
    }

    // 2.调用open以读的方式打开管道文件
    int fifo_fd=open(FIFOFILEPATH,O_RDONLY);
    if(fifo_fd<0)
    {
        perror("open");
        exit(2);
    }

    // 3.调用read从管道文件中读取数据
    char buffer[BUFFERSIZE];
    while(true)
    {
        ssize_t n=read(fifo_fd,buffer,sizeof(buffer)-1);
        if(n>0)
        {
            buffer[n]='\0';
            printf("%s\n",buffer);
        } 
        else if(n==0)
        {
            printf("client exit\n");
            break;
        }
        else
        {
            perror("read");
            close(fifo_fd); // 关闭fifo_fd文件描述符
            unlink(FIFOFILEPATH); // 删除管道文件
            exit(4);
        }
    }

    close(fifo_fd);
    unlink(FIFOFILEPATH);
    exit(0);
}

int main()
{
    server();

    return 0;
}

fifoClient.c

#include "comm.h"

static void client()
{
    // 1.调用open以写的方式打开管道文件
    int fifo_fd=open(FIFOFILEPATH,O_WRONLY);
    if(fifo_fd<0)
    {
        perror("open");
        exit(1);
    }

    // 2.调用write往管道文件中写入数据
    char buffer[BUFFERSIZE];
    while(true)
    {
        // 从0号文件描述符读取数据z
        printf("client# ");
        fflush(stdout);

        ssize_t n=read(0,buffer,sizeof(buffer)-1);
        buffer[n]='\0';

        // 将读取到的数据往管道文件中写入
        n=write(fifo_fd,buffer,strlen(buffer));
        if(n>0)
        {
            printf("write success n: %d\n",n);
        }
        else if(n==0)
        {
            // 读端关闭
            exit(2);
        }
        else
        {
            // 写入错误
            exit(3);
        }
    }

}

int main()
{
    client();
    return 0;
}

测试:

服务端先启动, 紧接着启动客户端. 客户端发送hello server, 可以看到服务端可以收到, 也即通过命名管道完成进程间通信. 客户端(写端)退出, 服务端(读端)也退出.
image-20230423180048142

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/452712.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开放原子训练营(第三季)RT-Thread Nano学习营线下学习心得

前言 目前市面上有很多种RTOS&#xff08;Real-time operating system&#xff0c;实时操作系统&#xff09;&#xff0c;还在犹豫选择学习哪种RTOS的小伙伴&#xff0c;推荐你们学习RT-Thread操作系统&#xff0c;这也是本人最喜欢的一个RTOS&#xff0c;喜欢的原因很简单&am…

[chapter27][PyTorch][Overfitting under fitting]

前言&#xff1a; 机器学习模型性能度量标准之一&#xff1a; 泛化能力 泛化能力强的模型才是好的模型&#xff0c;在评价泛化能力 时候&#xff0c;我们经常遇到过拟合和欠拟合问题 目录&#xff1a; 1&#xff1a; Overfitting 2: underfitting 一 Overfitting refers …

Revit中如何制作一个台阶?

简易台阶制作&#xff0c;比绘制轴线复杂些&#xff0c;运用到的快捷键也比较多&#xff0c;因此需要多多操作&#xff0c;接下来我们就开始吧。 1、开始我们的第一步&#xff0c;此处我们首先新建一个族而不是项目。然后点击公制常规模型。 2、进入族后&#xff0c;我们可以看…

大孔树脂型号,A-722,ADS500,ADS600,ADS750,ADS800

一、产品介绍 基于吸附功能的聚苯乙烯特种树脂 Tulsimer ADS-600 是一款没有离子官能基的&#xff0c;由交联聚苯乙烯合成的功能强大的吸附型树脂。 Tulsimer ADS-600 主要应用于水溶液中吸附酚及其化合物&#xff0c;氯代烃等含氯物质&#xff0c;表面活性剂&#xff0…

苦中作乐 ---竞赛刷题41-50(15-20)

&#xff08;一&#xff09;目录 L1-043 阅览室 L1-044 稳赢 L1-046 整除光棍 L1-048 矩阵A乘以B L1-049 天梯赛座位分配 L1-050 倒数第N个字符串 &#xff08;二&#xff09;题目 L1-043 阅览室 天梯图书阅览室请你编写一个简单的图书借阅统计程序。当读者借书时&…

MES管理系统助力数字化转型,实现智能化制造

MES管理系统不仅可以优化制造流程&#xff0c;还可以整合企业资源&#xff0c;从而提高企业效益。MES管理系统具有数据实时采集、数据分析与决策支持等功能&#xff0c;有利于企业管理层及时掌握生产运营情况&#xff0c;进行战略决策。 对于工厂&#xff0c;MES管理系统…

2023 IT市场权威榜单|美创数据库防火墙斩获“新一代信息技术创新产品”

4月20日&#xff0c;由赛迪顾问主办的“2023 IT市场权威榜单”评选结果正式发布&#xff0c;美创数据库防火墙斩获新一代信息技术创新产品&#xff01; 美创数据库防火墙是一款抵御并消除由于应用程序业务逻辑漏洞或者缺陷所导致的数据库安全问题的专业级数据库安全产品&#x…

【SVN】windows SVN安装使用教程(服务器4.3.4版本/客户端1.11.0版本)

介绍 这里是小编成长之路的历程&#xff0c;也是小编的学习之路。希望和各位大佬们一起成长&#xff01; 以下为小编最喜欢的两句话&#xff1a; 要有最朴素的生活和最遥远的梦想&#xff0c;即使明天天寒地冻&#xff0c;山高水远&#xff0c;路远马亡。 一个人为什么要努力&a…

什么是矩阵式项目管理?

矩阵式项目管理组织&#xff0c;是结构形式呈矩阵状的组织&#xff0c;项目管理人员由企业有关职能部门派出并进行业务指导&#xff0c;受项目经理的直接领导。对于任何从事多个技能要求相似或重叠的项目的企业来说&#xff0c;这是一个理想的策略。 矩阵组织如何运作&#xf…

Windows下版本控制器(SVN) - 1、开发中的实际问题+2、版本控制简介

文章目录 基础知识-Windows下版本控制器(SVN)1、开发中的实际问题2、版本控制简介2.1 版本控制[Revision control]2.2 Subversion2.3 Subversion 的优良特性2.4 SVN 的工作原理&#xff1a;2.5 SVN 基本操作 基础知识-Windows下版本控制器(SVN) 1、开发中的实际问题 小明负责…

虚拟机配置工作环境

一、安装cmake apt-get install cmake 二、配置JAVA环境 1、将这两个文件放入虚拟机文件系统中&#xff0c;拖进去即可 2、执行安装命令./ 3、拉代码svn rootmlw-virtual-machine:~/svnCode# svn --username wangmiaolin co https://10.200.20.20/svn/TIASDev/Devsrc/Branch…

docker容器:Docker-Compose

目录 一、Docker-Compose 1、Docker-Compose使用场景 2、Docker-Compose简介 3、Docker-Compose安装部署 4、YML文件编写注意事项 5、Compose配置常用字段 6、 Docker Compose 常用命令 7、Docker Compose 文件结构 8、docker Compose撰写nginx 镜像 9、docker Compos…

了解 WhatsApp 和 WhatsApp Business 之间的区别

实时讯息 app 是所有支援管道中客户满意度最高的一种&#xff0c;且其使用率已大幅攀升。事实上&#xff0c;根据我们的 2021 年客户体验趋势报告&#xff0c;在 2020 年&#xff0c;光是 WhatsApp 的工单量就增加了 101%。 客户想要使用他们惯常与亲友连络用的管道和企业互动…

stm32在SDIO模式下SD写入错误的问题

1、问题描述 使用FAT32 f_write 多次执行写操作时&#xff0c;会报FR_DISK_ERR错误&#xff0c;而且是刚开始写不报错&#xff0c;写几次后会一直报错。 设断点跟踪到HAL_SD_WriteBlocks中&#xff0c;在调用SDMMC_CmdWriteMultiBlock时&#xff0c;会报SDMMC_ERROR_TX_UNDERR…

016:Mapbox GL加载geojson数据,显示line,自定义颜色、宽度等

第016个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中加载geojson数据,显示线条。paint中可以设置的参数:line-blur,line-cap,line-color,line-dasharray,line-gap-width,line-gradient,line-join,line-miter-limit,line-offset,line-opacity,line-…

缓存空间优化实践

导读 缓存 Redis&#xff0c;是我们最常用的服务&#xff0c;其适用场景广泛&#xff0c;被大量应用到各业务场景中。也正因如此&#xff0c;缓存成为了重要的硬件成本来源&#xff0c;我们有必要从空间上做一些优化&#xff0c;降低成本的同时也会提高性能。 下面以我们的案…

【MySQL】数据库完整性和安全性

目录 一、完整性 1.概念 2.sql语言支持的两种约束 2.1静态约束 撤销追加约束 断言 2.3动态约束 触发器 二、安全性 用DBMS对数据库实现的两个特性 一、完整性 1.概念 指dbms保证的db的一种特性&#xff0c;在任何情况下的正确性、有效性、一致性 原理图 广义完整性&…

深度学习第J7周:ResNeXt-50算法思考

目录 一、问题 二、思考分析 &#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学啊* 所有&#xff09; &#x1f356; 作者&#xff1a;[K同学啊] 查看j6周代码&#xff0c;思考解决问题。 一、问题 &#x1f4cc;你需要解决的…

自然语言处理实战项目5-文本数据处理输入模型操作,以命名实体识别为例,打通NLP模型训练从0到1

大家好&#xff0c;我是微学AI&#xff0c;今天给大家带来自然语言处理实战项目5-文本数据处理输入模型操作&#xff0c;以命名实体识别为例。今天我给出的案例是命名实体识别&#xff0c;假设我们有一个命名实体识别任务&#xff0c;需要从文本中识别人名、地点和组织等实体。…

快速找出满足所需比值的一对电阻值 - Python 函数实现

常用的5% 和1% 精度电阻的阻值满足E24 数系&#xff0c;基数只能在这个数系里取&#xff0c;再乘上10 的n 次幂。E24 数系如下图&#xff1a; 之前我都是人肉一个一个试的&#xff0c;凭运气挑&#xff0c;终于忍不住想整个一劳永逸的小工具。 代码 对于给定的比值&#xff0…