Linux下进程间的通信--管道

news2025/2/26 18:55:04

关于进程间的通信

Linux进程间通信(Inter-Process Communication,IPC)是指在多个进程之间传输数据或信号的一些方法。由于Linux中的进程有各自独立的地址空间,因此它们不能直接访问对方的内存。为了实现进程间的通信,Linux提供了多种机制:管道,信号,消息队列,共享内存,网络

Linux下的进程通信手段基本上是从Unix平台上继承而来的

进程间通讯分为单个计算机的进程间通讯和局域网的进程间通讯

管道

管道简介:

管道是一种最基本的IPC机制,它允许一个进程的输出成为另一个进程的输入。管道是半双工的,数据只能向一个方向流动

管道的本质:是操作系统内核提供的一种进程间通信机制,它通过内核缓冲区和文件描述符来实现进程间的数据交换和同步

管道分为无名管道与有名管道

一.无名管道

无名管道的特点:

无名管道只能实现单向通信,数据只能从一端流向另一端

无名管道通常用于父子进程之间的通信,因为它们需要在创建时就确定通信的两端

无名管道没有名字,它们在文件系统中不可见,仅在创建它们的进程及其子进程的生命周期内存在。

无名管道将读端与写端抽象成两个文件进行操作,在无名管道创建成功之后,则会返回将读端与写端的文件描述符存入数组

创建无名管道

pipe()函数

函数描述:pipe()创建了一个管道,这是一种单向数据通道,可用于进程间通信。数组 pipefd 用于返回指向管道两端的两个文件描述符。pipefd[0]`指向管道的读端,pipefd[1] 指向管道的写端。写入管道写端的数据将由内核缓冲,直到从管道读端读取。

函数头文件
#include <unistd.h>   

功能
管道创建之后,内核会将文件描述符存储到数组    

函数原型
int pipe(int pipefd[2]);

函数参数
pipefd:用于存储无名管道读端与写端的文件描述符的数组
pipefd[0]:读端文件描述符
pipefd[1]:写端文件描述符    

函数返回值:
成功:0
失败:-1,设置 errno,同时将pipefd保持不变

 示例代码:

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

int main()
{
        int pipefd[2];
        int num=pipe(pipefd);
        if(num == -1)
        {
                perror("pipe\n");
                exit(EXIT_FAILURE);
        }
        pid_t pid = fork(); // 创建子进程
        if (pid == -1)
        {
                perror("fork failed");
                return 1;
        }
        else if (pid == 0)
        {
                close(pipefd[1]);
                char buffer[64];
                size_t rbytes = read(pipefd[0],buffer,sizeof(buffer));
                if(rbytes == -1)
                {
                        perror("read fail\n");
                        close(pipefd[0]);
                }
                else
                {
                        printf("read : %s\n",buffer);
                        close(pipefd[0]);
                }
        }
        else
        {
                close(pipefd[0]);
                char buf[128]={0};
                printf("please to input information\n");
                fgets(buf,sizeof(buf),stdin);
                buf[strlen(buf)-1]='\0';
                size_t wbytes = write(pipefd[1],buf,sizeof(buf));
                if(wbytes==-1)
                {
                        perror("write fail\n");
                        close(pipefd[1]);
                }
                else
                {
                        printf("wbytes = %ld\n",wbytes);
                        close(pipefd[1]);
                }
        }
        waitpid(-1,NULL,0);
    return 0;
}

 代码解读:

使用pipe函数创建一个无名管道,并将文件描述符数组pipefd的两个元素分别设置为管道的读端和写端。如果创建失败,打印错误信息并退出

调用fork函数创建子进程。如果fork失败,打印错误信息并返回1。

如果pid为0,说明当前是子进程。关闭管道的写端文件描述符。

定义一个字符数组buffer,用于存储从管道读取的数据。

从管道的读端读取数据到buffer。如果读取失败,打印错误信息并关闭读端文件描述符。如果读取成功,打印读取的内容并关闭读端文件描述符。

如果pid不为0,说明当前是父进程。关闭管道的读端文件描述符。

定义一个字符数组buf,用于存储用户输入的数据。

将用户输入的数据写入管道的写端。如果写入失败,打印错误信息并关闭写端文件描述符。如果写入成功,打印写入的字节数并关闭写端文件描述符。

调用waitpid函数等待子进程结束。-1表示等待任意子进程,NULL表示不需要子进程的终止状态,0表示不设置任何选项。

二.有名管道

有名管道的特点:

有名管道在文件系统中以文件的形式存在,但不占用磁盘空间,即使创建它的进程已经退出,有名管道仍然存在,直到被显式删除。

有名管道允许任何知道管道名称的进程进行通信

有名管道提供了一种同步机制,写入操作在管道有足够空间时才会成功,读取操作在管道中有数据时才会成功

创建有名管道

mkfifo命令

mkfifo [选项]... 名字
  • -m 或 --mode=模式:设置创建的有名管道的权限。如果不设置,将使用默认的权限(通常是 0666,即允许所有用户读写)。
  • -p 或 --fifo=路径:指定有名管道的路径。如果路径不存在,mkfifo 会创建它。

mkfifo()系统调用

函数描述:

mkfifo()创建一个名为pathname的FIFO特殊文件。mode指定FIFO的权限。它由进程的umask以通常的方式修改:创建的文件的权限是(mode & ~umask)。

FIFO特殊文件类似于管道,只是其创建方式不同。通过调用mkfifo()将FIFO特殊文件输入到文件系统中,而不是作为匿名通信通道。

一旦你以这种方式创建了一个FIFO特殊文件,任何进程都可以打开它进行读写,就像普通文件一样。但是,在您可以对其进行任何输入或输出操作之前,它必须同时在两端打开。为读打开FIFO通常会阻塞,直到其他进程为写打开相同的FIFO,反之亦然。

函数头文件
#include <sys/types.h>
#include <sys/stat.h>

函数原型:
int mkfifo(const char *pathname, mode_t mode);

函数参数:
pathname:有名管道路径名
mode:有名管道文件访问权限, 常用0644

函数返回值:
成功:返回0
失败:返回-1,并设置errno

示例代码:

进程A:

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PATH "/home/linux/namepipe"
int main()
{
        int fr = open(PATH,O_RDONLY);
        if(fr==-1)
        {
                perror("open fail");
                exit(EXIT_FAILURE);
        }
        char buffer[128];
        ssize_t rbytes = read(fr,buffer,sizeof(buffer));
        if(rbytes==-1)
        {
                perror("read fail");
                close(fr);
                exit(EXIT_FAILURE);
        }
        printf("rbytes=%ld buffer=%s\n",rbytes,buffer);
        close(fr);
        return 0;
}

进程B:

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

#define PATH_NAME  "/home/linux/npipe"
int main()
{

        int result = access(PATH_NAME,F_OK);
        if(result == -1)
        {
                int ret = mkfifo(PATH_NAME,0666);
                if(ret==-1)
                {
                        perror("mkfifo:");
                        exit(EXIT_FAILURE);
                }
        }
        int fw = open(PATH_NAME,O_WRONLY);
        if(fw==-1)
        {
                perror("open:");
                exit(EXIT_FAILURE);
        }
        char buf[128]={0};
        printf("please to enter\n");
        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]='\0';
        ssize_t wbytes = write(fw,buf,sizeof(buf));
        if(wbytes==-1)
        {
                perror("write:");
                close(fw);
                exit(EXIT_FAILURE);
        }
        printf("wbytes=%ld buf=%s\n",wbytes,buf);
        close(fw);
        return 0;
}

代码解读:

进程B:

用于从有名管道(FIFO)中写入数据

定义有名管道的路径宏PATH_NAME

使用access函数检查有名管道是否存在。F_OKaccess函数的参数,用于检查文件是否存在。

如果有名管道不存在,使用mkfifo函数创建它。如果创建失败,使用perror打印错误信息,然后退出程序。

以只写模式(O_WRONLY)打开有名管道。如果打开失败,使用perror打印错误信息,然后退出程序。

定义一个字符数组buf,用于存储用户输入的数据。

使用fgets函数从标准输入读取一行数据到buffgets会读取直到换行符或缓冲区满,但不包括换行符。

buf数组中的最后一个字符(换行符)替换为字符串结束符\0

 使用write函数将用户输入的数据写入有名管道。如果写入失败,使用perror打印错误信息,关闭文件描述符fw,然后退出程序。

关闭文件描述符fw

进程A:

用于从有名管道(FIFO)中读取数据

使用open函数以只读模式(O_RDONLY)打开有名管道。如果打开失败,open函数返回-1。

如果open函数失败,使用perror打印错误信息,然后退出程序。

定义一个字符数组buffer,用于存储从有名管道读取的数据。

使用read函数从有名管道中读取数据到buffer。如果读取失败,read函数返回-1。

如果read函数失败,使用perror打印错误信息,关闭文件描述符fr,然后退出程序。

关闭文件描述符fr

结语:

无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力

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

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

相关文章

python探索分形和混沌

简单产生复杂&#xff0c;混沌孕育秩序 0. 引言 a. 分形 fractal 【也叫碎形】 分形是一种具有自相似性和复杂结构的几何图形。在分形结构中&#xff0c;无论放大多少次&#xff0c;局部的结构特征都与整体结构相似。这种特性在自然界中广泛存在&#xff0c;比如树木枝干、山…

目前AI 辅助代码生成是否将成为未来编程的主流?

最强AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量https://aitools.jurilu.com/ 我的答案是 肯定的 &#xff01; AI辅助代码生成正在迅速崛起&#xff0c;将会成为未来编程的主流趋势。 当然这里的问题是"…

代码随想录算法训练营第二十三天(回溯 二)

力扣题部分: 39. 组合总和 题目链接:. - 力扣&#xff08;LeetCode&#xff09; 题面: 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以…

Windows系统,查看本机端口被哪个进程占用

1 打开CMD 快捷键 WinR&#xff0c;输入cmd&#xff0c;确定&#xff0c;打开命令行窗口。 2 查看端口的进程PID netstat -aon|findstr "80" 3 根据进程PID查看应用名称 tasklist|findstr "1468" 4 如果想关掉该进程&#xff0c;在任务管理器结束进程既…

「数组」数组双指针算法合集:二路合并|逆向合并|快慢去重|对撞指针 / LeetCode 88|26|11(C++)

目录 概述 1.二路合并 思路 复杂度 Code 2.逆向合并 思路 复杂度 Code 3.快慢去重 思路 复杂度 Code 4.对撞指针 思路 复杂度 Code 总结 概述 数组的线性枚举是我们学习编程时遇到的第一种枚举手段。但是它看起来有点愚蠢&#xff1a;只有一个索引i承担全部…

美食攻略系统---附源码99630

摘要 本论文旨在探讨如何利用SpringBoot技术开发一个美食攻略系统。系统将按照软件开发流程&#xff0c;采用B/S架构和面向对象编程思想进行项目开发。在引言部分&#xff0c;将介绍美食攻略系统的背景和开发目的&#xff0c;后续章节将依据软件开发流程&#xff0c;对系统进行…

电路笔记(PCB):数字信号的带宽与上升沿时间经验公式 BW = \frac{0.35}{T_r}

数字信号的带宽由上升沿决定 1. 传输线路的带宽如果小于信号的带宽就会产生失真 带宽限制和失真&#xff1a;当信号通过带宽受限的传输线路时&#xff0c;如果线路的带宽小于信号的带宽&#xff0c;信号的高频成分将被削弱或完全滤除。这种削弱会导致信号失真&#xff0c;特别…

Luma 1.5正式发布,文生视频加强真实感,时长最长5秒

距离上次版本发布仅过去了两个月&#xff0c;Luma AI再次发布了升级版本Dream Machine 1.5。新版本具有更好的文本到视频转换、更智能地提示理解、自定义文本渲染和改进图像到视频的功能。 得益于Luma AI对所有公众开放&#xff0c;任何人都可以免费试用&#xff0c;平台用户在…

CANoe.DiVa的应用——生成TP层测试用例过程流程详解(二)

🙋‍♂️【Vector CANdelastudio配置CDD】文章合集💁‍♂️点击跳转 ——————————————————————————————————–—— 从0开始学习CANoe使用 从0开始学习车载测试 相信时间的力量 星光不负赶路者,时光不负有心人。 目录 一.概述2.经典CAN T…

基于x86 平台opencv的图像采集和seetaface6的人脸朝向姿态估计功能

目录 一、概述二、环境要求2.1 硬件环境2.2 软件环境三、开发流程3.1 编写测试3.2 配置资源文件3.2 验证功能一、概述 本文档是针对x86 平台opencv的图像采集和seetaface6的人脸朝向姿态估计功能,opencv通过摄像头采集视频图像,将采集的视频图像送给seetaface6的人脸朝向姿态…

JavaEE 第15节 JUC相关组件介绍

目录 前言&#xff1a; Callable ReentrantLock Semaphore CountDownLatch 前言&#xff1a; 在Java中&#xff0c;JUC&#xff08;包路径&#xff1a;java.util.concurrent&#xff09;是一个用于并发编程的包&#xff0c;提供了线程安全的集合类、同步工具、并发执行框…

OSPF配置学习笔记

1.OSPF基础配置命令 1.1&#xff08;系统视图&#xff09;创建并运行OSPF进程 [Huawei] ospf [ process-id | router-id router-id ] porcess-id用于标识OSPF进程&#xff0c;默认进程号为1。OSPF支持多进程&#xff0c;在同一台设备上可以运行多个不同的OSPF进程&#xff0…

[000-01-022].第09节:RabbitMQ中的消息分发策略

我的后端学习大纲 RabbitMQ学习大纲 1.不公平分发&#xff1a; 1.1.什么是不公平分发&#xff1a; 1.在最开始的时候我们学习到 RabbitMQ 分发消息采用的轮训分发&#xff0c;但在某种场景下这种策略并不是很好&#xff0c;比方说有两个消费者在处理任务&#xff0c;其中有个…

【机器学习】联邦学习技术

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 引言联邦学习的定义与原理联邦学习的用例联邦学习示例与代码 引言 在大数据时代&#xff0c;数据隐私和安全成为了一个日益重要的议题。传统的机器学习模型训练通常需要集中大量数据到一个中心服务器…

【自然语言处理】 构建文本对话系统

构建文本对话系统的框架如下&#xff1a; 根据聊天系统目的功用的不同&#xff0c;可分成三大类型&#xff1a; 闲聊式机器人&#xff1a;较有代表性的有微软小冰、微软小娜、苹果的 Siri、小 i 机器人等&#xff0c;主要以娱乐为目的。 **知识问答型机器人&#xff1a;**知识…

Linux别名与用户管理体系

一、别名 1、概述 别名&#xff0c;给命令进行设置 一般用于给命令设置一个昵称/爱称 主要应用场景: 给常用命令设置个快捷方式&#xff0c;使用简单方便给危险命令加上的防护措施 查看系统已有的别名 [rootyunwei ~]# alias alias cpcp -i alias egrepegrep --coloraut…

[alien Invasion]python小游戏阶段总结

以后可能还会进行代码重构&#xff0c;以最终版本为准 本篇文章旨在理清程序脉络&#xff0c;方便以后写类似的程序时提供一个习惯的思路 未经允许&#xff0c;禁止转载 实体区 ship.py import pygame class Ship():def __init__(self,screen,ai_settings):#储存以便后续使…

RocketMQ广播模式消费失败是否会重试?

文章目录 前言继续广播和集群模式的消费流程集群模式&#xff08;默认的&#xff09;广播模式小结 push和pull介绍源码展示 偏移量保存失败情况1. 网络问题2. Consumer本地问题3. 消费进度记录器问题4. 程序设计问题5. 异常终止6. 持久化策略问题7. 同步问题 源码解析OffsetSto…

亚马逊测评号生存法则:如何抵御亚马逊封号风波?

距离黑五购物狂欢节还剩99天&#xff0c;相信各位商家都在紧锣密鼓的筹备相关事宜&#xff0c;然而&#xff0c;亚马逊的封号风波再次席卷而来。那如何在这场风暴中让亚马逊矩阵测评号安全航行亦或是脱颖而出呢&#xff1f;本文将给你一个答案&#xff0c;并帮助你的亚马逊店铺…

【PyTorch快速入门教程】03 PyTorch基础知识

在PyTorch中&#xff0c;最小的计算单元是张量&#xff08;tensor&#xff09;。因此关于张量的学习还是至关重要的。通过本章节学习&#xff0c;希望你对张量有一个更清晰的了解。 文章目录 1 什么是Tensor2 PyTorch中Tensor使用2.1 创建Tensor2.1.1 直接创建Tensor2.1.2 间接…