进程通信知识基础【Linux】——下篇

目录

前文

一,命名管道

创建命名管道

1. getline——c++库

2. unlink——系统接口

实践代码

common.hpp

client.cpp

server.cpp

Log.cpp

二,共享内存(system V接口)

1. 创建共享内存

shmget接口

2. 删除共享内存

常见ipc指令

shmctl接口

3. 映射到虚拟内存(挂起)

shmat接口

去关联

shmdt接口

小结:

三,信号量概念

概念引入 

小结


嘿!收到一张超美的风景图,希望你每天都能开心顺心

 

前文

经过进程池的实践(哈希思想应用【C++】(位图,布隆过滤器,海量数据处理面试题)-CSDN博客),我们对管道有了一定的理解,本篇继续给大家分享其余一些进程通信的知识。

一,命名管道

管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。如果我们想在 不相关的进程之间交换数据,可以使用 FIFO文件来做这项工作,它经常被称为 命名管道
命名管道是一种 特殊类型文件

创建命名管道

在指令端创建

$  mkfifo   filename

在程序中创建

#include <sys/types.h>
#include <sys/stat.h>

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

其中,pathname管道文件路径mode权限掩码,mkfifo函数返回值为0表示成功-1表示失败,错误原因存储在errno中。

下面我们来一个例子:

创建好一个命名管道后,执行下面指令,我们会遇到光标不动的现象。

echo "hello" > FilePipe

原因是:我们只是向命名管道进行写入没有打开命名管道的读端(从命名管道中读取数据), 进入了一种阻塞状态。(这也是管道的一种控制

解决方法也很简单,就是在另一个窗口,对命名管道进行读数据,这时就会中断写入的阻塞状态。

cat  <  FilePipe 

实践包含一些其他接口:

1. getline——c++库

istream& getline (istream& is, string& str, char delim);

其中,is是输入流,str是存储读取结果的字符串,delim是指定的分隔符(可选,默认为'\n'cin, scanf等是以空格和换行符为分隔符)。函数的返回值是输入流is的引用

getline函数的作用是从输入流is中读取一行文本,并将其存储到字符串str中。如果读取成功,则返回输入流is的引用。如果读取失败,则返回输入流的状态为false。

2. unlink——系统接口

int unlink(const char *pathname);

参数pathname为要删除的文件的路径名。当调用unlink接口时,系统会删除指定路径的文件。如果文件不存在或者无法访问,则会返回-1,并设置errno变量来指示错误类型。

下面头文件中: 

_COMM_H(名字自定义) 宏则是用来防止头文件的重复包含,通过定义这个宏可以在编译过程中检测到头文件是否被多次包含,避免出现重复定义的错误。

实践代码

common.hpp

#ifndef _COMM_H_
#define _COMM_H_
#include <iostream>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include "Log.hpp"

using namespace  std;

#define MOODE 0666
#define SIZE 128
#define CHNUMS 3

string Pipename = "FilePipe";
string PipeAddress = "./FilePipe";

#endif

client.cpp

#include "common.hpp"

int main()
{
   // 1. 客户向服务端,传输请求
   int fd = open(PipeAddress.c_str(), O_WRONLY);
   if (fd < 0 )
   {
    cout << "open fail" << endl;
   }
   
   string buff;
   while (1)
   {
      cout << " Please set your information :> ";
      getline(cin, buff);  //getline接口的优势在于它可以读取一行文本,并且自动将换行符(\n)作为行结束符
      write(fd, buff.c_str(), buff.size());
   }
   
    
    // 关闭命名管道
    close (fd);
    // 用户信息读取完后,需要我们删除这个命名管道文件。
    unlink(Pipename.c_str());
    //unlink接口是用于删除一个指定的文件或符号链接的系统调用。 
    return 0;
}

server.cpp

#include "common.hpp"
#include <sys/wait.h>

static void Getmessage(int fd) // 设置成静态函数的目的是,让其他文件,无法使用该函数
{
   char buff[SIZE];
   Log("处理数据开始", Test) << "[" << getpid() << "] "<< " step 3 " << endl;
   while (1)
   {
    memset(buff, '\0', sizeof(buff));
    ssize_t byte = read(fd, buff, sizeof(buff) - 1);
    if (byte > 0)
    {
        cout << "[" << getpid() << "] " << buff << endl;
    }else if ( byte == 0)
    {
        cout << " write close " << endl;
        break;
    }else
    {
        perror(" read fail");
        cout << "[" << getpid() << "] " << " read error "  << endl;
        break;
    }
   }
}


int main()
{
   // 1. 创建命名管道
   if (mkfifo(PipeAddress.c_str(), MOODE) < 0 )
   {
      perror( " mkfifo fail ");
   }

   Log("创建命名管道成功", debug) << "step 1" << endl;
   // 2. 文件操作
   int fd = open(PipeAddress.c_str(), O_RDONLY);
   if (!fd)
   {
      perror(" open fail");
      exit(-1);
   }
    Log("文件操作成功", debug) << "step 2" << endl;

   pid_t id;
   vector<int> FdList;
   for (int i = 0; i < CHNUMS; i++)
   {
       id = fork();
       if (id == 0)
       {
           Getmessage(fd);
           exit(1);  // 子进程完美退出,等待父进程接收
       }
   }

   // 父进程接受子进程退出
   int status = 0;
   for (int i = 0; i < CHNUMS; i++)
   {
       // waitpid(FdList[i], nullptr, 0);  这样子写会些坑,不知道哪一个子进程先状态改变
       waitpid(-1, &status, 0); // -1则是随机等待一个子进程状态改变
   } 
   
   // 4. 关闭文件
   close(fd);
   unlink(PipeAddress.c_str());
   Log("关闭文件", warning) << "step 4" << endl;
   return 0;
}

Log.cpp

#ifndef _LOG_
#define _LOG_
#include <iostream>
#include <ctime>
#include <string>
using namespace std;

#define debug 0
#define warning 1
#define Test 2

string list_level[3] = {
   "debug",
   "warning",
   "Test"  
};
   ostream&  Log(const char* str, int level)
{
    cout <<  str << " | " << (unsigned)time(0) << " | " << list_level[level] << " ";
    return cout;
}

#endif

下面是上面代码功能的形象图:

二,共享内存(system V接口)

共享内存是一种操作系统进程通信的方式,它允许多个进程共享同一块物理内存区域,从而实现高效的数据交换和共享。

共享内存的原理是将一块物理内存映射到多个进程的虚拟地址空间中,这样多个进程就可以直接读写这块内存,而不需要通过复制数据或者消息传递等方式进行通信

共享内存通常使用信号量来实现进程间的同步和互斥访问。

多个进程共享一块物理内存,操作系统一定得这个操作进行有序管理,而一旦涉及管理,必然要进行 先描述,再组织。

所有共享内存 =  共享内存块 +  对应共享内存块的内核数据结构。

1. 创建共享内存

shmget接口

返回值:一个操作系统运行时,肯定不只一个共享内存,众多的共享内存也被一个内核数据结构管理起来,而返回值就类似文件描述中的fd

1.   int shmflg:  IPC_CREAT(单独用以0替代) and IPC_EXCL 两个宏,前者单独用,共享内存不存在则创建;存在,则返回之。    后者,单独用没有意义,两者通过按位与一起用,底层不存在则创建;存在则出错返回(意义:保证每次共享内存都是最新的)。

2.   key_t key: 我们知道一个操作系统中有众多的共享内存,而key通过特殊的算法规则处理,形成唯一的通行码,找到精确共享内存。

那如何获取 key,通过ftok接口(就是一个对数据进行算法规则处理)。

 

  • pathname:  一个我们可以访问的路径(操作系统会访问其inode值(一个存储标识值)); 
  • proj_id :    就是一个int值。如果想获取一个相同的key值,proj_id也得相同。

返回值:-1失败;大于0则是key值。

3.   size_t size :   共享空间的字节大小。最后是页(4096)的整数倍。原因:假设要4097,则操作系统会创建8192字节,但中间空间我们又无法访问这就是浪费资源了。

2. 删除共享内存

输入指令: 查看共享内存资源

手动删除:

ipcs -m      

然后输入:ipcrm  -m  shmid值

 这里我们要注意的是:共享内存的生命周期随内核!!,除非重启!

perms: 权限值,为0时,谁也无法访问该共享内存,需要在shmget时需要输入权限,比如: shmget(123,  4096 , IPC_CREAT | IPC_EXCL | 0666),权限同文件一样

常见ipc指令

1. ipcs - 显示系统中的IPC资源信息
2. ipcrm - 删除IPC资源
3. ipcs -q - 显示消息队列信息
4. ipcs -s - 显示信号量信息
5. ipcs -m - 显示共享内存信息 

代码删除:

shmctl接口

cmd:  可以理解为功能选项,我们可以选择删除,获取共享空间属性信息等等。IPC_STAT, IPC_RMID等等

shmid:理解为共享内存的标识符吧。

*buf  :  这是管理共享内存的数据结构指针,可用通过buf获取或者修改其属性。

返回值: -1表示失败。

3. 映射到虚拟内存(挂起)

shmat接口

shmid:  共享内存唯一的id 

shmaddr:  去设置一个虚拟地址。(由于我们不清楚虚拟地址的使用情况,这里建议null/nullptr,让计算机来产生

shmflg: 挂接方式,以只读(SHM_R默认0),只写(SHM_W),还是读写(SHM_R | SHM_W )方式挂接。

返回值: 就是计算机返回共享内存虚拟地址的开头; 返回0则失败。

当一旦挂起成功,进程关联数就会增加

去关联

shmdt接口

shmaddr:   共享内存虚拟地址起始地址。 

返回值:-1失败; 0成功,成功删除后同时,进程关联数减一。

成功创建共享内存后位置分布的逻辑图

代码示例:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main() {
    key_t key = ftok("shmfile",65);  // 创建一个key
    int shmid = shmget(key,1024,IPC_CREAT|0666);  // 创建共享内存区

    char *str = (char*) shmat(shmid,(void*)0,0);  // 将共享内存区连接到当前进程的地址空间

    printf("Write data : ");
    gets(str);

    printf("Data written in memory: %s\n",str);

    shmdt(str);  // 分离共享内存区
    shmctl(shmid,IPC_RMID,NULL);  // 删除共享内存区

    return 0;
}

这里介绍一个比较重要的格式打印数据的接口: snprintf()接口 

小结:

1. 共享内存是进程通信(IPC)中,速度最快的。因为这个方法不需要过多的拷贝数据,而且不用经过操作系统,在用户层直接读取修改。

2. 由于共享内存没有访问控制(读写端独立),容易引发并发问题(两端数据不一致问题)。

三,信号量概念

概念引入 

小结

我们需要理解这个概念

没理解?没事,这个我们在多线程里,用代码的形式进行解释。

下期预告:信号!!

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力

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

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

相关文章

PMP项目管理 - 相关方管理

系列文章目录 PMP项目管理 - 质量管理 PMP项目管理 - 采购管理 PMP项目管理 - 资源管理 PMP项目管理 - 风险管理 PMP项目管理 - 沟通管理 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wing…

【一种用opencv实现高斯曲线拟合的方法】

背景&#xff1a; 项目中需要实现数据的高斯拟合&#xff0c;进而提取数据中标准差&#xff0c;手头只有opencv库&#xff0c;经过资料查找验证&#xff0c;总结该方法。 基础知识&#xff1a; 1、opencv中solve可以实现对矩阵参数的求解&#xff1b; 2、线的拟合就是对多项…

【深度强化学习】确定性策略梯度算法 DDPG

前面讲到如 REINFORCE&#xff0c;Actor-Critic&#xff0c;TRPO&#xff0c;PPO 等算法&#xff0c;它们都是随机性策略梯度算法&#xff08;Stochastic policy&#xff09;&#xff0c;在广泛的任务上表现良好&#xff0c;因为这类方法鼓励了算法探索&#xff0c;给出的策略是…

探索 Vim:一个强大的文本编辑器

引言&#xff1a; Vim&#xff08;Vi IMproved&#xff09;是一款备受推崇的文本编辑器&#xff0c;拥有强大的功能和高度可定制性&#xff0c;提供丰富的编辑和编程体验。本文将探讨 Vim 的基本概念、使用技巧以及为用户带来的独特优势。 简介和发展 1. Vim 的简介和历史 V…

【二分查找】自写二分函数的总结

作者推荐 【动态规划】【广度优先搜索】LeetCode:2617 网格图中最少访问的格子数 本文涉及的基础知识点 二分查找算法合集 自写二分函数 的封装 我暂时只发现两种&#xff1a; 一&#xff0c;在左闭右开的区间寻找最后一个符合条件的元素&#xff0c;我封装成FindEnd函数。…

力扣刷题-二叉树-平衡二叉树

110 平衡二叉树 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a;一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 示例 1: 给定二叉树 [3,9,20,null,null,15,7] 返回 true 。 给定二叉树 [1…

AUTOSAR ComM模块配置以及代码

ComM模块配置以及代码执行流程 1、基本的一个通道的配置列表 ComMNmVariant 概念的个人理解&#xff1a; FULL&#xff1a; 完全按照AUTOSAR NM方式进行调用 LIGHT &#xff1a;设置一个超时时间&#xff0c;在请求停止通信的时候开始计时&#xff0c;超时之后才会进入FULLCOM…

运维实践|采集MySQL数据出现many connection errors

文章目录 问题出现问题分析当前环境问题分析 解决方案1 检查调度事件任务是否开启2 开启调度事件任务3 创建一张日志表4 创建函数存储过程5 创建事件定时器6 开启事件调度任务7 检查核实是否创建 总结 问题出现 最近在做OGG结构化数据采集工作&#xff0c;在数据采集过程中&am…

将博客搬至微信公众号了

一、博客搬家通知 各位码友们好&#xff0c;大家是不是基本很少看 CSDN 了呢&#xff1f;平时开发是不都依靠着 chatGPT 来解决工作中的技术问题了&#xff0c;不过我觉得在工作中的使用场景各式各样的&#xff0c;具体问题还得自己具体去梳理逻辑&#xff0c;再考虑用什么样的…

C语言:求和1+1/2-1/3+1/4-1/5+……-1/99+1/100

#include<stdio.h> int main() {int i 0;double sum 0.0;int flag 1;for (i 1;i < 100;i){sum 1.0 / i * flag;flag -flag;}printf("sum%lf\n", sum);return 0; }

SpringIOC之@Primary

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

力扣刷题-二叉树-找树左下角的值

513 找树左下角的值 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1&#xff1a; 示例 2&#xff1a; 思路 层序遍历 直接层序遍历&#xff0c;因为题目说了是最底层&#xff0c;最左边的值&a…

【数据结构—队列的实现】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、队列 1.1队列的概念及结构 二、队列的实现 2.1头文件的实现—Queue.h 2.2源文件的实现—Queue.c 2.3源文件的测试—test.c 三、测试队列实际数据的展示 3.…

mysql使用st_distance_sphere函数报错Incorrect arguments to st_distance_sphere

前言 最近使用空间点位查询数据时函数报错Incorrect arguments to st_distance_sphere报错。 发现问题 因为之前是没有问题的&#xff0c;所以把问题指向了数据&#xff0c;因为是外部数据&#xff0c;不是通过系统打点获取&#xff0c;发现是因为经纬度反了&#xff0c;loc…

VRRP(虚拟路由冗余协议)

一.VRRP简介 1.VRRP是什么 Virtual route Redundancy Protocol&#xff0c;也叫虚拟路由器冗余协议。 利用VRRP&#xff0c;一组路由器协同工作&#xff0c;单只有一个处于Master状态&#xff0c;处于该状态的路由器&#xff08;的接口&#xff09;承担实际的数据流量转发任…

MapReduce序列化实例代码

1 &#xff09;需求&#xff1a;统计每个学号该月的超市消费、食堂消费、总消费 2 &#xff09;输入数据格式 序号 学号 超市消费 食堂消费 18 202200153105 8.78 12 3 &#xff09;期望输出格式 key &#xff08;学号&#xff09; value &#xff08; bean 对象&#xf…

二分查找算法的概念、原理、效率以及使用C语言循环和数组的简单实现

二分查找的概念 二分查找也称折半查找&#xff08;Binary Search&#xff09;&#xff0c;它是一种效率较高的查找方法。但是&#xff0c;折半查找要求线性表必须采用顺序存储结构&#xff0c;而且表中元素按关键字有序排列。 实现原理 首先&#xff0c;假设表中元素是按升序…

深度学习项目实战:垃圾分类系统

简介&#xff1a; 今天开启深度学习另一板块。就是计算机视觉方向&#xff0c;这里主要讨论图像分类任务–垃圾分类系统。其实这个项目早在19年的时候&#xff0c;我就写好了一个版本了。之前使用的是python搭建深度学习网络&#xff0c;然后前后端交互的采用的是java spring …

VLAN间的通讯---三层交换

一.三层交换 1.概念 使用三层交换技术实现VLAN间通信 三层交换二层交换 三层转发 2.基于CEF的MLS CEF是一种基于拓补转发的模型 转发信息库&#xff08;FIB&#xff09;临接关系表 转发信息库&#xff08;FIB&#xff09;可以理解为路由表 邻接关系表可以理解为MAC地址表…

js获取日期

目录 Date 对象 1. 获取当前时间 2. 获取特定日期时间 Date 对象的方法 1. 获取各种日期时间组件 2. 获取星期几 3. 获取时间戳 格式化日期时间 1. 使用 toLocaleString() 方法 2. 使用第三方库 UNIX 时间戳 内部表示 时区 Date 对象 JavaScript中内置的 Date 对象…