Linux-进程间通信(进程间通信介绍、匿名管道原理及代码使用、命名管道原理及代码使用)

一、进程通信介绍

1.1进程间通信的目的
  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事( 如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变。
1.2 进程间如何通信

进程=内核数据结构+代码和数据

每个进程都拥有自己独立的PCB与数据,由于进程与进程间是相互独立的,互相看不到也不想看到对方的数据,所以进程间通信的前提是让不同进程看到同一份资源。进程间通信一定是某个进程需要通信,让操作系统创建一个共享资源,而用户不能直接操控操作系统,所以操作系统需要提供许多的系统调用,系统调用不同就会创建出不同的共享资源,那么进程间的通信方式也会不同

1.3进程间通信的方式

管道

  • 匿名管道pipe
  • 命名管道

System V IPC

  • System V 消息队列
  • System V 共享内存
  • System V 信号量

POSIX IPC

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

二、匿名管道

2.1匿名管道原理

当一个进程以读和写的方式分别打开一个文件,由于打开方式的不同,就会创建两个struct file,每个文件都有自己的属性、数据、更重要的是存在内核级的文件缓冲区,两个struct file除了打开方式字段不同,其余信息都是相同的,所以操作系统不会将文件的信息在拷贝一份,两个struct file指向的文件信息是一份。当该进程创建子进程,子进程会继承父进程的文件描述符表,此时父子进程可以对同一个文件进行读取操作,该文件中的缓冲区就相当于一个共享资源,这个缓冲区也成为道,两个进程间就可以进行通信了

当我们想让父进程写入数据,子进程读取数据时,我们只需要关闭父进程的读文件,关闭子进程的写文件即可,这样就可以做到子进程向管道中写数据,父进程向文件中读数据了

【注意】:

  1. 管道只能进行通单向信,及只能一个进程进行读一个进程写,不能让两个进程既可以读也可以写,所以我们需要关闭对应不需要功能的文件描述符(不关闭也是可以的,但是建议关掉,防止误写误读)
  2. 写端写入的数据会保存在内核级文件缓冲区中,直到被读走

2.2 pipe接口(创建管道)
#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码

int fd[ ]为输出型参数,会带回以读和写方式打开的文件的fd,由于我们知道文件的fd不知道文件的名字,所以称为匿名管道

2.3 代码理解(父进程读,子进程写)
#include<iostream>
#include<unistd.h>
#include<cerrno>
#include<cstring>
#include <sys/types.h>
#include<string>
#include<sys/wait.h>
#include <sys/types.h>
#define SIZE 1024

void chilewrite(pid_t wfd)
{
  while(1)
  {
    std::string message="father I am your child process!";
    write(wfd,message.c_str(),message.size());
    sleep(2);
  }
}

void fathread(pid_t rfd)
{
    char buffer[SIZE];
    while(1)
    {
        int n=read(rfd,buffer,sizeof(buffer)-1);
        if(n>0)
        {
            buffer[n]='\0';
            std::cout<<buffer<<std::endl;
        }
        else if(n==0)
        {
            // 如果read的返回值是0,表示写端直接关闭了,我们读到了文件的结尾
            std::cout<<"写端关闭了"<<std::endl;
            break;
        }
        else
        {
            std::cerr<<"read err"<<std::endl;
            break;
        }
    }
}

int main()
{
    //创建管道
    int pipefd[2];
    int n=pipe(pipefd);
    if(n<0)
    {
        std::cerr<<"errno:"<<errno<<" errstring:"<<strerror(errno)<<std::endl;
        return 1;
    }
    //创建子进程
    pid_t id=fork();
    if(id==0)
    {
        //子进程
        std::cout<<"子进程准备就绪,准备写入数据了"<<std::endl;
        close(pipefd[0]);
        chilewrite(pipefd[1]);
        close(pipefd[1]);
        exit(0);
    }
    //父进程
    std::cout<<"父进程准备就绪,准备读取数据了"<<std::endl;
    close(pipefd[1]);
    fathread(pipefd[0]);
    close(pipefd[0]);

    //进程等待
    int status=0;
    pid_t rid=waitpid(id,&status,0);
    if(rid>0)
    {
        std::cout << "wait child process done, exit sig: " << (status&0x7f) << std::endl;
        std::cout << "wait child process done, exit code(ign): " << ((status>>8)&0xFF) << std::endl;
    }
    return 0;
}
2.4 管道的四种情况
  1. 如果管道内部是空的并且写端没有关闭,此时读取条件不具备,读端会被阻塞,等待写入数据
  2. 如果管道内部被写满了并且读端没有关闭,此时写入条件不具备,写端会被阻塞,等待读端读取数据
  3. 如果管道一直在读,但是已经关闭了写端,此时读端read返回值会一直读到0,表示读到了文件末尾
  4. 如果管道一直在写,但是已经关闭了读端,此时OS会直接使用13号信号杀死进程,代表进程异常
2.5 管道的五种特征
  1. 匿名管道只能进行有血缘关系的进程之间进行通信,因为匿名管道是依靠子进程继承父进程的文件描述符表实现的(通常用于实现父子进程之间的通信)
  2. 管道内部自带进程的同步机制,多执行流执行代码时具有明显的顺序性,写入管道的数据直到被读取之前会保持在管道缓冲区中(如果缓冲区未满),而读取操作则会等待直到有数据可读,这种机制避免了同时读写导致的数据损坏问题
  3. 管道文件的生命周期是随进程的,当所有打开该文件的进程都退出后,该文件资源也会被释放
  4. 管道文件在进行通信时是面向字节流的,读与写的次数不是一 一匹配的,数据没有明确的分割,一次拿多少数据都行
  5. 管道通信是一种特殊的半双工模式。半双工通信允许数据在两个方向上传输,但不能同时进行。这意味着在任何时候,数据只能在一个方向流动。一旦一方开始发送数据,另一方必须等待接收完毕后才能开始发送。全双工通信允许数据同时在两个方向上进行传输,无需等待。由于半双工模式是可以双向传输数据的,但是管道只能单向通信,所以是特殊的半双工模式
2.6 管道的读写规则
  • 当没有数据可读时

        O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止

        O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

  • 当管道满的时候

        O_NONBLOCK disable: write调用阻塞,直到有进程读走数据

        O_NONBLOCK enable:调用返回-1,errno值为EAGAIN 如果所有管道写端对应的文件描述符被关闭,则read返回0

  • 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程 退出
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

PIPE_BUF一般至少为512个字节,在Linux下,PIPE_BUF为4096个字节,当写入的的数据量不大于PIPE_BUF时,写入操作是安全的,不会发生写一半数据就被读走的情况,如果写入的数据量大于PIPE_BUF,则可能会发写入的数据提前被读走一部分

三、命名管道

匿名管道只能实现具有血缘关系进程之间的通信,而命名管道可以实现两个毫无相关的进程之间的通信,下面先介绍一下命名管道的原理

3.1命名管道原理

        当一个进程以读的方式打开一个文件,该进程会有自己的进程PCB和文件描述符表,同时会创建一个struct file

        当另一个进程以写的方式也打开这个文件,该进程也会有自己的进程PCB、文件描述符表,并且也会创建一个struct file,但是由于这两个进程打开的文件是一样的,而struct file也就是打开文件的方式不同,所以两个struct file都是指向的同一份文件信息,也指向了同一个内核级文件缓冲区,那么这两个毫无关系的进程就指向了同一段空间,就可以进行通信了。

        怎么确保两个进程打开的是同一个文件呢?---------文件的路径

3.2 mkfifo接口
  • 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
mkfifo filename

  • 命名管道也可以从程序里创建,相关函数有:
int mkfifo(const char *filename,mode_t mode);

#参数:filename为文件名 mode为创建文件的权限
#头文件:#include <sys/types.h>
        #include <sys/stat.h>

#返回值:成功返回0,失败返回-1,并且设置错误码
3.2 代码使用举例

namedpipe.hpp:

#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <cerrno>
#include <unistd.h>
#include <iostream>
#include <fcntl.h>

#define Defaultfd -1
#define Creater 1
#define User 2
#define SIZE 128
#define Read O_RDONLY
#define Write O_WRONLY

const std::string path = "./myfifo";
class NamedPipe
{
private:
    bool OpenNamePipe(int mode)
    {
        _fd = open(_fifo_path.c_str(), mode);
        if (_fd < 0)
            return false;
        return true;
    }

public:
    NamedPipe(const std::string path, int who)
        : _fifo_path(path), _id(who), _fd(Defaultfd)
    {
        if (_id == Creater)
        {
            int ret = mkfifo(_fifo_path.c_str(), 0666);
            if (ret != 0)
            {
                std::perror("mkfifo");
            }
            std::cout << "Creater creat namedpipe!" << std::endl;
        }
    }
    bool OpenforRead()
    {
        return OpenNamePipe(Read);
    }

    bool OpenforWrite()
    {
        return OpenNamePipe(Write);
    }

    int ReadNamedPipe(std::string *out)
    {
            char buffer[SIZE];
            int n = read(_fd, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = '\0';
                *out = buffer;
            }
            return n;
    }
    int WriteNamedPipe(std::string& in)
    {
       return write(_fd,in.c_str(),in.size());
    }
    ~NamedPipe()
    {
        if (_id == Creater)
        {
            int ret = unlink(_fifo_path.c_str());
            if (ret != 0)
            {
                std::perror("unlink");
            }
            std::cout << "Creater free namedpipe!" << std::endl;
        }
    }
private:
    std::string _fifo_path;
    int _id;
    int _fd;
};

client.cpp:

#include "namedpipe.hpp"
int main()
{
    // 以使用者的身份打开
    NamedPipe fifo(path, User);
    if (fifo.OpenforWrite())
    {
        std::cout << "Client open fifopipe for write!" << std::endl;
        while (true)
        {
            std::cout << "Please enter message" << std::endl;
            std::string message;
            std::getline(std::cin, message);
            fifo.WriteNamedPipe(message);
        }
    }
    return 0;
}

server.cpp:

#include "namedpipe.hpp"
int main()
{
    // 以创建者的身份打开
    NamedPipe fifo(path, Creater);
    if (fifo.OpenforRead())
    {
        std::cout << "Server open fifopipe for read!" << std::endl;
        while (true)
        {
            std::string message;
            int n = fifo.ReadNamedPipe(&message);
            if (n > 0)
            {
                std::cout << "Client:" << message << std::endl;
            }
            else if (n == 0)
            {
                std::cout << "写端关闭了,读端也要关闭!" << std::endl;
                break;
            }
            else if (n < 0)
            {
                std::perror("read"); 
                break;
            }
        }
    }
    return 0;
}

结果展示:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

值得买科技新思路,导购电商的终点是“AI+出海”?

在以往&#xff0c;大众普遍认为品牌的消费者大多是高度忠诚人群&#xff0c;而事实上&#xff0c;非品牌忠诚者相比重度消费者&#xff0c;对促进品牌增长更为重要。 这类非品牌忠诚者被定义为摇摆的消费者群体&#xff0c;也就是那些购买品牌产品概率在20%-80%之间的消费者。…

【Unity动画系统】Animator组件的属性

介绍Animator组件的全部属性 Controller&#xff1a;动画控制器 Avatar&#xff1a;人物骨骼 Apply Root Motion&#xff1a;有一些动画片段自带位移&#xff0c;如果希望自带的位移应用在游戏对象上&#xff0c;那么就勾选&#xff1b;如果自己编写脚本&#xff0c;那么就不…

如何用智能获客开启新商机?揭秘赢销侠软件的奇效

在当今数字化竞争日益激烈的商业环境中&#xff0c;企业为了生存和发展&#xff0c;必须寻找新的途径以获取潜在客户。智能获客作为一种新型的营销方式&#xff0c;正以其高效、精准的特点改变着传统的市场开拓模式。而在这个过程中&#xff0c;自动获客软件的作用愈发凸显&…

LLM大语言模型原理、发展历程、训练方法、应用场景和未来趋势

LLM&#xff0c;全称Large Language Model&#xff0c;即大型语言模型。LLM是一种强大的人工智能算法&#xff0c;它通过训练大量文本数据&#xff0c;学习语言的语法、语义和上下文信息&#xff0c;从而能够对自然语言文本进行建模。这种模型在自然语言处理&#xff08;NLP&am…

杰发科技AC7840——SPI通信简介(1)_跑通Demo

0. 简介 一些配置项&#xff1a; CPHA&#xff1a;相序 CPLO&#xff1a;极性 看着demo需要按键&#xff0c;于是去掉按键&#xff0c;去掉打印&#xff0c;直接输出波形看逻辑分析仪的信号。 其实现在做这些demo测试应该都有逻辑分析仪&#xff0c;直接看波形更直观一点。…

分享:抖音老阳口中的选品师项目好做吗?

近年来&#xff0c;随着抖音等短视频平台的兴起&#xff0c;越来越多的博主通过分享自己的生活、知识和见解吸引了大量粉丝。其中&#xff0c;抖音博主老阳以其独特的选品眼光和专业的产品评测&#xff0c;在广大网友中树立了良好的口碑。那么&#xff0c;老阳口中的选品师项目…

【MySQL】MVCC的实现原理

【MySQL】MVCC的实现原理 MVCC简介事务的隔离级别读未提交&#xff08;Read Uncommitted&#xff09;概念分析 读已提交&#xff08;Read Committed&#xff09;概念分析结论 可重复读&#xff08;Repeatable Read&#xff09;概念分析结论 串行化&#xff08;Serializable &am…

实战—登录功能引发的逻辑漏洞

密码找回功能可能存在的漏洞 1.验证码发送后前端返回 2.验证码无次数限制可爆破 3.验证码可控/邮箱篡改为自己的接收短信验证码/手机号码篡改为自己的接收短信验证码 4.越权漏洞—>自己验证码通过改包然后修改他们密码 5.任意用户密码重置 6.密保问题在前端源码 实战…

Linux基础——Linux开发工具(上)_vim

前言&#xff1a;在了解完Linux基本指令和Linux权限后&#xff0c;我们有了足够了能力来学习后面的内容&#xff0c;但是在真正进入Linux之前&#xff0c;我们还得要学会使用Linux中的几个开发工具。而我们主要介绍的是以下几个&#xff1a; yum, vim, gcc / g, gdb, make / ma…

49. 字母异位词分组 128. 最长连续序列

49. 字母异位词分组 128. 最长连续序列 把集合里面的所有元素都放入set容器里面 定义结果最大连续数量 ans for循环遍历每个元素 先判断集合里面有没有比这个元素小1的 如果没有 说明这个元素就是序列的第一个元素 然后接着找集合里面有没有比这个元素大1的 while一直找 …

牛客NC353 回文子串的数量【中等 字符串,枚举,回文 C++/Java/Go/PHP 高频】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/3e8b48c812864b0eabba0b8b25867738 思路 参考答案C class Solution {public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可*** param str string字符串…

详解centos8 搭建使用Tor 创建匿名服务和匿名网站(.onion)

1 Tor运行原理&#xff1a; 请求方需要使用&#xff1a;洋葱浏览器&#xff08;Tor Browser&#xff09;或者Google浏览器来对暗&#xff0c;网网站进行访问 响应放需要使用&#xff1a;Tor协议的的Hidden_service 2 好戏来了 搭建步骤&#xff1a; 1.更新yum源 rpm -Uvh h…

OceanBase V4.3 发布—— 迈向实时分析 AP 的重要里程

OceanBase在2023年初&#xff0c;发布了4.x架构的第一个重要版本&#xff0c;V4.1。该版本采用了单机分布式一体化架构&#xff0c;并在该架构的基础上&#xff0c;将代表数据库可靠性的RTO降低至 8 秒以内&#xff0c;从而确保在意外故障发生后&#xff0c;系统能够在极短时间…

javafx如何一键打包成exe

javafx如何打包成exe JavaFX-Template-Native 集成jfoenix、commons-math、commons-lang3、netty&#xff0c;方便一些和底层做通信使用&#xff0c;不需要可以自行pom中去掉依赖当前使用的jdk17&#xff0c;理论上jdk14都支持采用模块化&#xff0c;支持一键打包生成很小的ex…

Threejs制作服务器机房冷却结构

这节再绘制一个机房的结构&#xff0c;因为内容比较简单&#xff0c;就只使用一个章节来介绍&#xff0c; 先来一张效果图&#xff0c; 需要两个模型&#xff1a;一个冷却设备&#xff0c;一个服务器机箱&#xff0c;我这里是从网上找来的&#xff0c;首先我们搭建一个场景&a…

solidworks出现slderrresu.dll错误如何解决?亲测有效

通过近来给客户安装SolidWorks发现&#xff0c;SolidWorks2010、SolidWorks2012、SolidWorks2014、SolidWorks2015、SolidWorks2016、SolidWorks2017都会出现这个slderrresu.dll安装错误问题&#xff1a; 其实这个错误很好解决,主要是因為安裝中文版solidworks沒有選擇安裝中文…

.NET操作 Access (MSAccess)

注意&#xff1a;新项目推荐 Sqlite &#xff0c;Access需要注意的东西太多了&#xff0c;比如OFFICE版本&#xff0c;是X86还是X64 连接字符串 ProviderMicrosoft.ACE.OleDB.15.0;Data Source"GetCurrentProjectPath"\\test.accdb//不同的office版本 连接字符串有…

Linux 虚拟主机切换php版本及参数

我使用的Hostease的Linux虚拟主机产品,由于网站程序需要支持高版本的PHP,程序已经上传到主机&#xff0c;但是没有找到切换PHP以及查看PHP有哪些版本的位置&#xff0c;因此咨询了Hostease的技术支持&#xff0c;寻求帮助了解到可以实现在cPanel面板上找到此切换PHP版本的按钮&…

亚马逊商品详情API接口:解锁亚马逊商品信息的全面视野

亚马逊商品详情API接口&#xff1a;解锁亚马逊商品信息的全面视野 在跨境电商和电商数据分析领域&#xff0c;亚马逊作为全球领先的电商平台&#xff0c;其商品信息对商家、开发者以及市场分析师来说至关重要。为了更高效地获取亚马逊平台上的商品详情&#xff0c;亚马逊商品详…

哨兵-1A与DInSAR技术监测尼泊尔地震前后地表形变

辽宁抚顺是一座以煤而兴的重工业城市&#xff0c;建国初期抚顺被誉为“煤都”&#xff0c;这里有闻名全国享誉世界的亚洲最大的露天煤矿——抚顺西露天矿。抚顺西露天矿地处抚顺煤田西部&#xff0c;矿坑东西长6.6公里&#xff0c;南北宽2.2公里&#xff0c;最终开采垂直深度47…