当前位置: 首页 > news >正文

18.进程间通信(四)

一、同步,互斥,临界资源,临界区概念

回归system V共享内存,IPC本质是:先让不同的进程看到同一份资源。但共享内存没有同步机制,因此对于数据没有保护机制,如何约束?解决方案:信号量。

多个执行流(进程),能看到同一份公共资源:共享资源

被保护起来的共享资源叫做 临界资源

在进程中涉及到互斥资源的程序叫做 临界区(访问临界资源对应的代码)

多个执行流访问临界资源具有一定的顺序性叫做 同步

要么做,要么不做叫做 原子性

对于临界区保护要采用加锁机制->锁本身也是共享资源,怎么保证锁的安全?->申请锁的时候,必须是原子的。

二、System V 信号量

1.信号量是什么?

本质是一个计数器,用来表明临界资源的数量。

2.理解信号量(电影院)

信号量实质:对于资源的预定机制。

想访问临界资源,必须先“买票”(申请信号量)->所有进程,访问临界资源的一小块,就必须申请信号量!

例子:把共享内存按照不同的区域,部分使用! -> 1.不要访问同一个位置 2.不要放入过多的进程进来 -> 并发访问,不出问题(多元信号量)

细节1:信号量本身就是共享资源,怎么保证安全?

申请--,原子性,P操作

sem++,原子性,V操作

细节2:信号量只有1和0两态的信号量,二元信号量。(就是互斥)

信号量与进程间通信

信号量和通信有什么关系?

1.先访问信号量P,每个进程得先看到同一个信号量(IPC本质:不同进程看到同一份资源)

2.不是传递数据,才是通信IPC,通知,互斥同步,也算!!(传递信号类,先后顺序)

熟悉信号量接口和系统调用:

int semget(key_t key, int nsems, int semflg);        //获取信号量

key:标识信号量唯一性。

nsems:信号量集合的信号量个数

semflg:IPC_CREAT,IPC_EXCL,新建要带权限。

返回值:成功返回信号量集合标识符,失败返回-1,错误码被设置。

int semctl(int semid, int semnum, int cmd, ...);   //初始化指定信号量,删除所有信号量

semid:信号量集合标识符

semnum:指定要控制的信号量集合中信号量的下标

cmd: SET_VAL 初始化指定信号量集合中下标为semnum的信号量,IPC_RMID 删除信号量

...:可变参数,SET_VAL时要传入结构体对象,用作参数初始化。结构如下:

union semun
{int val;               /* Value for SETVAL */struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */
};

其中val为信号量计数器的数量。

int semop(int semid, struct sembuf *sops, size_t nsops); //对指定的信号量集合指定下标的信号量进行PV操作

semid:信号量集合标识符

sops:传入的结构体信息

nsops:信号量集合的信号量个数

sops传入的结构体如下:

struct sembuf
{unsigned short sem_num; /* semaphore number */short sem_op;           /* semaphore operation */short sem_flg;          /* operation flags */
};

sem_num为信号量集合中信号量的下标。

semop为PV操作的类型,传入-1代表P操作,传入1代表V操作。

sem_flg为操作,设为SEM_UNDO。

信号量的结构以及信号量的组织方式

共享内存、消息队列、信号量->key区分唯一值!->OS中,共享内存、消息队列、信号量被当成了同一种资源!(System V IPC) 

信号量基础实例

#ifndef SEM_V1_HPP
#define SEM_V1_HPP#include <iostream>
#include <memory>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>#define EXIT_ERROR(m) \do                \{                 \perror(m);    \exit(1);      \} while (0)class Sem
{
public:Sem(int semid) : _semid(semid){}~Sem(){int ret = semctl(_semid, 0, IPC_RMID);if (ret < 0){EXIT_ERROR("semctl");}std::cout << "~Sem() success!" << std::endl;}void P(){struct sembuf buf;buf.sem_num = 0;buf.sem_op = -1;buf.sem_flg = SEM_UNDO;int ret = semop(_semid, &buf, 1);if (ret < 0){EXIT_ERROR("semop");}}void V(){struct sembuf buf;buf.sem_num = 0;buf.sem_op = 1;buf.sem_flg = SEM_UNDO;int ret = semop(_semid, &buf, 1);if (ret < 0){EXIT_ERROR("semop");}}private:int _semid;
};#define SEM_CREAT (IPC_CREAT | IPC_EXCL | 0666)
#define SEM_GET (IPC_CREAT)const std::string path_default = ".";
const int proj_id_default = 0x11;class SemBuilder
{
public:SemBuilder(){}~SemBuilder(){}SemBuilder *SetVal(int val){_val = val;return this;}std::shared_ptr<Sem> Build(int flag){// 1.创建/获取信号量集合key_t key = ftok(path_default.c_str(), proj_id_default);int semid = semget(key, 1, flag);if (semid < 0){EXIT_ERROR("semget");}std::cout << "sem create success! semid:" << semid << std::endl;// 2.只有父进程要初始化信号量集合if (flag == SEM_CREAT){Init(semid);std::cout << "Init success" << std::endl;}// 3.返回Sem对象std::shared_ptr<Sem> sp = std::make_shared<Sem>(semid);return sp;}private:void Init(int semid){union semun{int val;               /* Value for SETVAL */struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */} sun;sun.val = _val;int ret = semctl(semid, 0, SETVAL, sun);if (ret < 0){EXIT_ERROR("semctl");}}private:int _val;
};#endif // SEM_V1_HPP

基于建造者模式的信号量

#ifndef SEM_V2_HPP
#define SEM_V2_HPP#include <iostream>
#include <memory>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>#define SEM_CREAT (IPC_CREAT | IPC_EXCL)
#define SEM_GET (IPC_CREAT)
#define EXIT_ERROR(m) \do                \{                 \perror(m);    \exit(1);      \} while (0)const std::string path_default = ".";
const int proj_id_default = 0x11;// 产品类:最终构建的复杂对象
class Sem
{
public:Sem(int semid) : _semid(semid){}~Sem(){int ret = semctl(_semid, 0, IPC_RMID);if (ret < 0){EXIT_ERROR("semctl");}std::cout << "~Sem() success!" << std::endl;}void P(){struct sembuf buf;buf.sem_num = 0;buf.sem_op = -1;buf.sem_flg = SEM_UNDO;int ret = semop(_semid, &buf, 1);if (ret < 0){EXIT_ERROR("semop");}}void V(){struct sembuf buf;buf.sem_num = 0;buf.sem_op = 1;buf.sem_flg = SEM_UNDO;int ret = semop(_semid, &buf, 1);if (ret < 0){EXIT_ERROR("semop");}}private:int _semid;
};// 抽象建造者:定义构建接口
class SemBuilder
{
public:virtual ~SemBuilder() = default;virtual void SetKey() = 0;virtual void SetVal(int val) = 0;virtual void SetMode(int mode) = 0;virtual void Build(int flag) = 0;virtual std::shared_ptr<Sem> GetSem() = 0;
};// 具体建造者1:游戏电脑建造者
class SemBuilder1 : public SemBuilder
{
public:virtual void SetKey() override{_key = ftok(path_default.c_str(), proj_id_default);}virtual void SetVal(int val) override{_val = val;}virtual void SetMode(int mode) override{_mode = mode;}virtual void Build(int flag) override{int real = _mode == 0 ? flag : flag | _mode;int semid = semget(_key, 1, real);if (semid < 0){EXIT_ERROR("semget");}std::cout << "sem create success! semid:" << semid << std::endl;if (flag == SEM_CREAT){Init(semid);}_sp = std::make_shared<Sem>(semid);}virtual std::shared_ptr<Sem> GetSem() override{return _sp;}private:void Init(int semid){union semun{int val;               /* Value for SETVAL */struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */} sun;sun.val = _val;int ret = semctl(semid, 0, SETVAL, sun);if (ret < 0){EXIT_ERROR("semctl");}}private:int _key;int _val;int _mode;std::shared_ptr<Sem> _sp;
};// 指挥者:控制构建流程
class SemEngineer
{
public:SemEngineer() = default;void Construct(std::shared_ptr<SemBuilder> spb,int flag, int mode = 0){spb->SetKey();spb->SetVal(1);spb->SetMode(mode);spb->Build(flag);}
};#endif // SEM_V2_HPP
http://www.xdnf.cn/news/989875.html

相关文章:

  • Python_day51
  • Future与CompletableFuture:异步编程对比
  • v4l2_subdev 与 /dev/videoX 的关联
  • Git不能更新以及提交代码,提示链接超时,本地凭证无问题
  • 6.11 MySQL面试题 日志 性能 架构
  • 深入理解TCP以及三次握手与四次挥手
  • 面对多个项目并行,协作机制如何建立?
  • Java 8 Stream 流详细教程 - 全面指南
  • 重塑未来的力量:人工智能的技术演进与产业变革
  • git的5种分支分别是干嘛的,git常用指令
  • git的常用方法
  • 交换机上抓包
  • 大模型与人工智能
  • 了解shell中的Fork炸弹
  • Java并发编程实战 Day 17:CompletableFuture高级应用
  • 种子音乐携手RBW 精心打造Solar颂乐首支中文单曲《Floating Free》
  • 单端的时钟阻抗设置为50欧姆,差分的时钟阻抗设置为100欧姆
  • echarts 地图 海南省全岛完整展示
  • Vuex 中Mutation 和Action介绍
  • Android SDK Manager 2025镜像,亲测好用
  • Windows为何总在不停更新补丁
  • EtherCAT转CANopen网关实现与伺服系统连通的配置实例探究
  • linux虚拟机磁盘容量不足?一个指令解决问题
  • 国内各种AI工具库
  • 大模型笔记_检索增强生成(RAG)
  • ABC 337
  • Arduino入门教程:0、课程介绍认识Arduino
  • 餐厅商家怎么做元宵节活动宣传海报?
  • C++ 精简知识点
  • 推荐算法介绍-基础算法