Linux学习之system V

目录

一,system V共享内存

快速认识接口

 shmget(shared memory get)

shmat(shared memory attach)

shmdt(shared memory delete)

 shmctl (shared memory control)

 编写代码

综上那么共享内存与管道通信有什么区别?

system v消息队列

system v信号量


一,system V共享内存

除了管道通信方式,对于操作系统自己本身,操作系统为了内核中的通信单独设计了通信模块。

进程通信的前提:让不同进程看到同一份资源

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到 内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

首先我们从shared memory,共享内存开始,所谓的共享内存,本质上就是将同一份资源,通过加载到物理内存,同时映射到两个进程的内存空间,实现资源共享。

此外,在操作系统中一定会允许多个共享内存,此时就需要操作系统来管理这些共享内存。

快速认识接口

 shmget(shared memory get)

创建共享内存 既能创建,又能获取

参数一为创建的key,参数二为空间大小,

参数三为标记位:

 IPC_CREAT   to create a new segment.  If this flag is not used, then shmget() will find the segment associated with key and check to see . if  the user has permission to access the segment.(没有就创建shm(共享内存))

PC_EXCL    used with IPC_CREAT to ensure failure if the segment already exists.(通常与IPC_CREAT结合使用,不存在就创建,存在就出错返回不创建)。

通过这两个标志位使得我们创建的内存是全新的。

shmat(shared memory attach)

将共享内存挂接到地址空间当中

参数一位shmid,参数2一般设置位nullptr,参数三为标志位表示已什么样的方式挂在,一般也用0。

返回值位起始地址。通过引用计数来计算有多少进程挂在到当前共享内存中。

shmdt(shared memory delete)

去掉内存关联,将共享内存从进程地址空间中移除。

 shmctl (shared memory control)

控制共享内存,第一个参数位shmid,第二个位操作指令,IPC_STAT,IPC_SET, IPC_RMID(删除) ,IPC_INFO

 编写代码

和命名管道通信的方式一样,我们创建了三个文件server,client,commant.cpp,用来观察进程通信:

首先对于读端还是写端都会有同一个key,我们创建同一个key,此时进程就会把key写到地址空间当中,通过Key值操作系统可以找到两个进程的同一块地址空间,即key值指向的内存就是共享内存。

comman.hpp

#pragma once
#include<iostream>
#include<string>
#include<cstdlib>
#include<unistd.h>
const std::string pathname="/home/danchengwei/myfile/file7";
const int proj_id=0x11223344;
const int size=4096;
//管道文件
const std::string filename = "fifo";key_t getkey(){//转换pathname为keykey_t key=ftok(pathname.c_str(),proj_id);//convert a pathname and a project identifier to a System V IPC keyif(key<0){std::cerr<<"errno:"<<errno<<",strerror"<<strerror(errno)<<std::endl;return 1;}std::cout<<"key:"<<key<<std::endl;return key;}std::string tohex(int id){char buffer[1024];snprintf(buffer,sizeof(buffer),"0x%x",id);return buffer;}int creatshmHLPER(key_t key,int flag){int shmid=shmget(key,size,flag); //成光返回id,错误返回-1if(shmid<0){std::cerr<<"errno:"<<errno<<",strerror"<<strerror(errno)<<std::endl;exit(2);}std::cout<<"shmid:"<<shmid<<std::endl;return shmid;}int getshm(key_t key){return creatshmHLPER(key,IPC_CREAT | IPC_EXCL | 0644);}int creatshm(key_t key){return creatshmHLPER(key,IPC_CREAT);}bool MakeFifo()
{int n = mkfifo(filename.c_str(), 0666);if(n < 0){std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;return false;}std::cout << "mkfifo success... read" << std::endl;return true;
}

server

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include"comman.hpp"
using namespace std;int main()
{//创建共享内存key_t key=getkey();//key在应用层我们一般不去使用,我们使用的是shmid int shmid=creatshm(key);sleep(10);//挂载 ,即将共享内存映射到进程的地址空间当中。char*s=(char*)shmat(shmid,nullptr,0);//打开管道Start:int rfd=open(filename.c_str(),O_RDONLY);if(rfd<0){//cerr<<"errno:"<<errno<<",errstring:"<<strerror(errno)<<endl;if(MakeFifo()){goto Start;}else{return 1 ;}//return 2;}cout<<"打开管道成功..."<<endl;char buffer[1024];while(true){ssize_t s=read(rfd,buffer,sizeof(buffer)-1);if(s>0){buffer[s]=0;//表示以/0结尾cout<<"cilent reply: "<<buffer<<endl;}if(s<=0){cout<<"no data had reciveed"<<endl;//没有数据可读了,退出进程break;}}//关闭管道close(rfd);cout<<"关闭管道成功..."<<endl;sleep(5);//在地址空间当中移除共享内存shmdt(s); sleep(5);//按照id删除shmctl(shmid,IPC_RMID,nullptr);sleep(5);return 0;}

创建完成可以通过指令查看共享内存:

ipcs -m  //查看共享内存
ipcrm -m shmid  //删除共享内存

其中natch表示有几个进程挂接到该共享内存。

程序结束,共享内存被创建之后并没有释放。它的生命周期时内核决定的。

之后我们再完成与server一样的client的编写:

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include"comman.hpp"
using namespace std;int main()
{//与server一样//获取key值key_t key=getkey();//创建共享内存int shmid=creatshm(key);sleep(10);//把共享内存挂载到地址空间当中char *s=(char*)shmat(shmid,nullptr,0);sleep(5);//打开管道int wfd = open(filename.c_str(), O_WRONLY);//数据写入string message;while(true){cout<<"请输入你要发送的数据:"<<endl;getline(cin,message);//这里再写入管道时,我们使用c++string再转化为c字符串ssize_t t=write(wfd,message.c_str(),message.size());if(t<0){cerr<<"错误码:"<<errno<<",错误原因:"<<strerror(errno)<<endl;break;}}//关闭管道close(wfd);cout<<"关闭管道成功..."<<endl;//取消共享内存的挂载shmdt(s);return 0;
}

之后我们就可以同时执行两个进程并观察会发现 ,刚开始创建共享内存,server与client挂载到地址空间,natch增加,之后移除,之后删除.

有了共享内存,那么我们现在通过共享内存就可以实现进程之间的通信,和管道类似,我们可以增加一个管道文件;之后用一端作为读端,一端作为写端。

综上那么共享内存与管道通信有什么区别?

其实共享内存是比管道通信更加快速的,对于共享内存,考虑硬件:

第一次拷贝是从外设到共享内存,第二次是从共享内存写到显示器上,只需要拷贝两次,对于管道.

而对于管道,先是用户到键盘(外设)把数据读到缓冲区中,再把数据拷贝到管道当中(写入),之后再将管道的数据写道别的用户的缓冲区(读),最后再将数据给到显示器(外设)。对于管道考虑硬件需要拷贝四次数据。

system v消息队列

所谓的消息队列是提供让一个进程给另一个进程发送数据块的能力,使用的接口msgctl,使用方法基本和共享内存大致相同。

参数一为msqid即这里的key,参数二为如何去操作消息队列的指令,参数三一般为nullptr.

创建完成之后可以用指令ipcs -q来查看消息对列。根据接口的第二参数我们可以还选择删除消息队列。可以看到使用与共享内存非常的相似。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<iostream>
#include"comman.hpp"//消息队列
int main()
{//获取key值key_t key=getkey();//创建消息队列int msgid=msgget(key,IPC_CREAT | IPC_EXCL);std::cout<<"msgid:"<<msgid<<std::endl;sleep(10);//删除消息队列msgctl(msgid,IPC_RMID,nullptr);rteurn 0;
}

其次消息队列也可以存在多个,两个进程用多个消息队列进行数据交互,因此在内核当中,操作系统需要管理这些消息队列,先描述在组织,因此消息队列的管理就是消息队列加自身的属性。

关于消息队列大家感兴趣可以查看手册的使用。

system v信号量

信号量主要用于同步和互斥的,当进程们面对同一份资源时,我们的资源就需要被保护起来,而信号量就是为了保护我们的共享资源。

因此我么先来了解同步与互斥:

进程互斥
程互斥则是一种防止多个进程同时访问同一资源的机制。在进程互斥的机制下,当一个进程正在访问某个共享资源时,其他进程需要等待该进程释放该资源之后才能访问。实现进程互斥的主要方法有互斥锁和信号量。

进程同步

指的是多个进程在共享资源的过程中保持一致性的机制。其主要目的是避免进程间的竞争和冲突,确保多个进程对共享资源的访问按照一定的顺序和规则进行,从而避免资源的竞争和冲突。

进程同步的实现方法包括信号量、互斥锁、条件变量等。
 

对于linux系统就是用洗脑两盒互斥锁来解决同步与互斥的问题。

信号量(semaphore),我们在linux中也有对应的函数接口:

semget 获取创建信号量

semctl 控制信号量

数1为semid,参数二为编号(起始从0开始),参数三为指令。

semop 操作信号量

对信号量做加加减减操作

 

可以看到无论是共享内存,还是消息对列或者信号量,他们都是有统一的标准的接口,因此这里的函数我们也能使用了。

返回值一般我们叫做semid,参数一为key值,参数二为创建的个数,参数三为标志.

利用指令ipcs -s 可以查看创建的信号量,可以看到创建完进程结束,但信号量还在,因此生命周期还是随内核。

当然对于信号量,也是需要被操作系统管理,管理信号量及其信号量属性。

其次内核是怎样看待IPC资源(共享资源):

首先肯定有两部分:1.会有单独设计的一个模块。2.会有特定的保护机制。

所谓的信号量本身就是一个计数器,为了让进程通信,多个执行流共享同一份资源,而公共资源被并发访问就会产生数据不一致的问题,因此我们就需要保护我们的共享资源来面对同步与互斥的两个情况。

解决同步与互斥简单来说就是,互斥:任何一个时刻只允许一个执行流来使用该资源。同步:多个执行流都来使用该资源时,让他按照一定的顺序来执行。

信号量通俗点说,就是资源数目的计数器,每一个执行流想要访问资源内的某一份资源,不应该执行流直接访问,而是先申请信号量资源,(其实就是对计数器信号量--操作),申请成功后,就完成了对资源的预定机制,如果申请不成功,则执行流就进行阻塞。

我们在编写代码时如果想要访问这一份共享资源,先要申请信号量资源,如果成功,则返回之后的信号量,失败就阻塞。而阻塞的这一部分资源就是临界资源。

但也有特殊情况,只有一个信号量即只允许一个人访问的资源,这种信号量被叫做二元信号量,即就是一个锁的功能---互斥锁。

原子性:只有两种状态,要么不做,要么做完。

本次我们主要先 认识一下。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

运维管理制度优化:确保IT系统稳定运行的关键策略

1、总则 第一条&#xff1a;为保障公司信息系统软硬件设备的良好运行&#xff0c;使员工的运维工作制度化、流程化、规范化&#xff0c;特制订本制度。 第二条&#xff1a;运维工作总体目标&#xff1a;立足根本促发展&#xff0c;开拓运维新局面。在企业发展壮大时期&#x…

助你打通SwiftUI任督二脉

序言 开年的第一篇文章&#xff0c;今天分享的是SwiftUI&#xff0c;SwiftUI出来好几年&#xff0c;之前一直没学习&#xff0c;所以现在才开始&#xff1b;如果大家还留在 ​​iOS​​ 开发&#xff0c;这们语言也是一个趋势&#xff1b; 目前待业中.... 不得不说已逝的2023年…

多个版本的Python如何不冲突?

转载文章&#xff0c;防止忘记或删除 转载于&#xff1a;电脑中存在多个版本的Python如何不冲突&#xff1f; - 知乎 (zhihu.com) 如何安装多版本的Python并与之共存&#xff1f; 如果你的工作涉及到Python多版本之间开发或测试&#xff0c;那么请收藏本文&#xff0c; 如果你…

从Spring Boot应用上下文获取Bean定义及理解其来源

前言 在Spring框架中&#xff0c;Bean是组成应用程序的核心单元。特别是在Spring Boot项目中&#xff0c;通过使用SpringApplication.run()方法启动应用后&#xff0c;我们可以获得一个ConfigurableApplicationContext实例&#xff0c;这个实例代表了整个应用程序的运行时环境…

开源世界的学术问题

自由软件基金会是1983年成立的&#xff0c;到现在是41年。正好很有意思的是&#xff0c;在去年还有一篇文章&#xff08;CSDN 的翻译&#xff09;&#xff0c;专门在质疑说成立 40 年的自由软件基金会是不是已经快不行了&#xff0c;所以我们会用这个标题叫做兴衰发展历程来介绍…

2024!深入了解 大语言模型(LLM)微调方法(总结)

来源: AINLPer公众号&#xff08;每日干货分享&#xff01;&#xff01;&#xff09; 编辑: ShuYini 校稿: ShuYini 时间: 2024-2-28 引言 众所周知&#xff0c;大语言模型(LLM)正在飞速发展&#xff0c;各行业都有了自己的大模型。其中&#xff0c;大模型微调技术在此过程中起…

[vue2] 使用provide和inject时,无法获取到实时更新的数据

一、场景 当vue文件中存在多级的父子组件传值&#xff08;即&#xff1a;祖先向下传递数据&#xff09;、多个子组件或孙子级组件都要使用顶级或父级的数据时&#xff0c;使用provide 和 inject 组合无疑是很方便的一种做法了&#xff0c;但如此只是注入的初始值&#xff0c;并…

如何使用便签快速分类工作待办事项

在日常工作和生活中&#xff0c;我们经常需要处理各种各样的待办事项。而有效地分类这些任务&#xff0c;可以帮助我们更好地管理时间和提高工作效率。使用便签是一种简单而实用的方法&#xff0c;下面将介绍如何利用好用便签来快速分类工作待办事项。 首先&#xff0c;你可以…

【前端入门】设计模式+单多页+React

设计模式是一种解决特定问题的经验总结&#xff0c;它提供了经过验证的解决方案&#xff0c;可以在软件开发过程中使用。设计模式可以帮助前端开发人员更有效地组织和管理代码&#xff0c;并提供一种共享的语言和框架&#xff0c;以便与其他开发人员进行交流。 以下是一些常见…

航海雷达练习软件SPx Radar Trainer

产品简介 航海雷达练习软件SPx Radar Trainer是一款基于计算机的模拟训练软件&#xff0c;为小型船舶、初步接触航海操作的学员提供了虚拟练习环境&#xff0c;让学员可以在计算机上熟悉雷达视频图像与实际船舶运行环境的结合、以便于更好的驾驶船舶避撞。 产品特色 【模拟小…

python脚本实现全景站点矩阵转欧拉角

效果 脚本 import re import numpy as np import math import csv from settings import * # 以下是一个示例代码,可以输入3*3旋转矩阵,然后输出旋转角度:# ,输入3*3旋转矩阵# 计算x,y,z旋转角def rotation_matrix_to_euler_angles(R):

c++异常机制(1) -- 什么是异常机制

情况一: 之前&#xff0c;我们提到了防御性编程&#xff0c;就是说我们预判一些可能出现的问题&#xff0c;然后进行相应的处理。 但是&#xff0c;这些处理往往是在函数中进行的。 比如&#xff0c;判断数组下标是否合法。 -》 if(下标 < 0){ 相应的处理 }; 如上&…