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

news2024/11/29 14:53:21

目录

前文

一,命名管道

创建命名管道

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. 由于共享内存没有访问控制(读写端独立),容易引发并发问题(两端数据不一致问题)。

三,信号量概念

概念引入 

小结

我们需要理解这个概念

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

下期预告:信号!!

结语

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

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

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

相关文章

matlab中Signal Builder模块的用法总结

目录 前言方法一方法二参考文章 前言 今天在用matlab中Signal Builder的模块时&#xff0c;不知道怎么去得到想要的信号源&#xff0c;于是上网查了一下&#xff0c;并记录一下 方法一 如图所示&#xff0c;打开自定义 上面一行是横坐标&#xff0c;下面一行是纵坐标 [0,1…

SolidWorks二次开发 C#-读取基于Excel的BOM表信息

SolidWorks二次开发 C#-读取基于Excel的BOM表信息 问题点来源解决方案及思路相关引用链接 问题点来源 这是一位粉丝问的一个问题&#xff0c;他说到: 老师&#xff0c;请问Solidworks二次开发工程图中"基于Excel的材料明细表"怎么读取里面的数据&#xff1f; Ps:这…

ActionCLIP:A New Paradigm for Video Action Recognition

文章目录 ActionCLIP: A New Paradigm for Video Action Recognition动机创新点相关工作方法多模态框架新范式预训练提示微调 实验实验细节消融实验关键代码 总结相关参考 ActionCLIP: A New Paradigm for Video Action Recognition 论文&#xff1a;https://arxiv.org/abs/21…

vue使用el-tag完成添加标签操作

需求&#xff1a;做一个添加标签的功能&#xff0c;点击添加后输入内容后回车可以添加&#xff0c;并且标签可以删除 1.效果 2.主要代码讲解 鼠标按下后触发handleLabel函数&#xff0c;根据回车的keycode判断用户是不是按下的回车键&#xff0c;回车键键值为13&#xff0c;用…

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

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

Nginx配置请求头携带原始请求信息

在浏览器向nginx发送请求时&#xff0c;nginx会将请求转发给SpringBoot&#xff0c;此时由于是nginx给SpringBoot发送的请求&#xff0c;所以SpringBoot获取到的请求IP是192.168.1.2&#xff0c;而并非是浏览器的192.168.1.1&#xff0c;如果想要获取原始的请求IP&#xff0c;应…

【小程序】-【

swiper、swiper-item轮播图 swiper是滑块视图容器。其中只可放置swiper-item组件。部分常用属性如下&#xff0c;其余属性详见&#xff1a;官方文档 <view class"banner"><swiperprevious-margin"30rpx"circularautoplayinterval"3000&q…

Python glob

参考文章&#xff1a; Python 中glob.glob()、glob.iglob&#xff08;&#xff09;的使用-CSDN博客 Python 中glob.glob()的使用 glob.glob(path)的功能&#xff1a; 返回符合path格式的所有文件的路径&#xff0c;以list存储返回。 path的表示方法&#xff1a; 利用匹配符…

服务器挖矿木马识别与清理

一、什么是挖矿木马 挖矿木马会占用CPU进行超频运算,从而占用主机大量的CPU资源,严重影响服务器上的其他应用的正常运行。黑客为了得到更多的算力资源,一般都会对全网进行无差别扫描,同时利用SSH爆破和漏洞利用等手段攻击主机。部分挖矿木马还具备蠕虫化的特点,在主机被成…

【C语言加油站】qsort函数的模拟实现

qsort函数的模拟实现 导言一、回调函数二、冒泡排序2.1 冒泡排序实现升序 三、qsort函数3.1 qsort函数的使用3.2 比较函数 四、通过冒泡排序模拟实现qsort函数4.1 任务需求4.2 函数参数4.3 函数定义与声明4.4 函数实现4.4.1 函数主体4.4.2 比较函数4.4.3 元素交换 4.5 my_qsort…

Linux下I2C调试工具--for--Zynq MPSOC/Jetson Xavier

Linux下I2C调试工具 1、简介 i2c-tools是一个专门调试i2c的工具&#xff0c;无需编写任何代码即可轻松调试IC设备&#xff0c;可获取挂载的设备及设备地址&#xff0c;还可以在对应的设备指定寄存器设置值或者获取值等功能。i2c-tools有如下几个常用测试命令i2cdetect, i2cdu…

Stable LM Zephyr 3B:手机上的强大LLM助手

概览 最近&#xff0c;Stability.ai宣布开源了Stable LM Zephyr 3B&#xff0c;这是一个30亿参数的大语言模型&#xff08;LLM&#xff09;&#xff0c;专为手机、笔记本等移动设备设计。其突出的特点是参数较小、性能强大且算力消耗低&#xff0c;能够自动生成文本、总结摘要…

arcgis更改服务注册数据库账号及密码

最近服务器数据库密码换了&#xff0c;gis服务也得换下数据库连接密码。传统官方的更改方式&#xff08;上传连接配置文件&#xff09;&#xff1a; ArcGIS Server数据库注册篇(I) — 更新数据库密码_arcgis server sde换密码-CSDN博客 方式太麻烦了&#xff0c;需要安装ArcG…

Tektronix泰克TCP303示波器电流探头

主要特点和优点&#xff1a; ● 交流/直流测量功能 ● DC~100MHz电流探头放大器&#xff08;TCPA300&#xff09;&#xff0c;当使用&#xff1a; - DC~100MHz, 30A DC&#xff08;TCP312&#xff09; - DC~50MHz, 50A DC&#xff08;TCP305&#xff09; - DC~5MHz, 150A DC&a…

使用案例总结Vlookup函数的30种用法

1 基础用法 =VLOOKUP(A12,B$1:D$8,3,0) 2 批量查找 =VLOOKUP(A11:A13,A2:C8,3,0) 3 模糊查找 =VLOOKUP("*"&D2&"*",A:B,2,0) 4 模糊查找2 =VLOOKUP(D10&"??",A:B,2,0) 5 模糊查找3 =

GAMES101-Lec10~12几何 曲线 曲面网格

目录 1.几何的表示1.1显示1.1.1更多显示表示方法1.1.1.1点云1.1.1.2多边形网格 1.2隐示1.2.1更多隐示表达法1.2.1.1代数曲面1.2.1.2 CSG1.2.1.3距离函数SDF1.2.1.4水平集1.2.1.5分型几何 2.曲线2.1贝塞尔曲线2.2 计算方法2.3代数表示2.4性质2.5逐段贝塞尔曲线 3.曲面3.1贝塞尔曲…

如何使用Java在Excel中添加动态数组公式?

本文由葡萄城技术团队发布。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言 动态数组公式是 Excel 引入的一项重要功能&#xff0c;它将 Excel 分为两种风格&#xff1a;Excel 365 和传统 …

C语言使用posix正则表达式库

在C语言中&#xff0c;你可以使用 POSIX 正则表达式库&#xff08;regex.h&#xff09;来进行正则表达式的模式匹配。POSIX 正则表达式库提供了一组函数来编译、执行和释放正则表达式。 下面是使用 POSIX 正则表达式库的基本步骤&#xff1a; 包含头文件 <regex.h>&…

C语言——完数难题(头歌编程刷题)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 生命如同寓言&#xff0c;其价值不在于…

【react.js + hooks】基于事件机制的跨组件数据共享

跨组件通信和数据共享不是一件容易的事&#xff0c;如果通过 prop 一层层传递&#xff0c;太繁琐&#xff0c;而且仅适用于从上到下的数据传递&#xff1b;建立一个全局的状态 Store&#xff0c;每个数据可能两三个组件间需要使用&#xff0c;其他地方用不着&#xff0c;挂那么…