IO进程线程(十二)进程间通信 共享内存 信号灯集

news2024/11/14 13:24:43

文章目录

  • 一、共享内存 shared memory(shm)
    • (一)特点
    • (二) 相关API
      • 1. 创建共享内存
      • 2. 映射共享内存到当前的进程空间
      • 3. 取消地址映射
      • 4. 共享内存控制
    • (三)使用示例
    • (四) 属性
  • 二、信号灯集---控制进程间同步
    • (一)特点
    • (二) 相关API
      • 1. 创建一个信号灯集
      • 2. 信号灯集控制函数
      • 3. 信号灯集的操作函数
    • (三)封装函数

一、共享内存 shared memory(shm)

(一)特点

在内核中创建共享内存,让进程A和进程B都能够访问到,通过这段内存进行数据的传递。
共享内存是所有进程间通信方式中效率最高的(不需要来回进行数据的拷贝)
在这里插入图片描述

(二) 相关API

1. 创建共享内存

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

int shmget(key_t key, size_t size, int shmflg);
功能:
    创建共享内存
参数:
    key:键值
        key 通过ftok获取
        IPC_PRIVATE:只能用于亲缘进程间的通信
    size:共享内存的大小  PAGE_SIZE(4k)的整数倍
    shmflg:共享的标志位
        IPC_CREAT|0666 
        或 
        IPC_CREAT|IPC_EXCL|0666
返回值:
    成功 共享内存编号
    失败 -1 重置错误码
  • 注:
  • 共享内存大小必须要4k的整数倍,因为一页是4k。如果申请时不要求4
    的整数倍,分配时也是分配4k的整数倍。
  • 内核空间越界会直接段错误
  • 同一个key值可以同时用于消息队列,共享内存,信号灯集

2. 映射共享内存到当前的进程空间

#include <sys/ipc.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:
    映射共享内存到当前的进程空间
参数:
    shmid:共享内存编号
    shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL,让系统自动分配
    shmflg:共享内存操作方式
        0    读写
        SHM_RDONLY    只读
返回值:
    成功 指向共享内存的地址
    失败 (void *)-1 重置错误码

3. 取消地址映射

#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:
    取消地址映射
参数:
    shmaddr:指向共享内存的指针
返回值:
    成功 0
    失败 -1 重置错误码

4. 共享内存控制

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:
    共享内存控制的函数
参数:
    shmid:共享内存编号
    cmd:操作的命令码
        IPC_STAT:获取
        IPC_SET:设置
        IPC_RMID:删除共享内存
            标记要销毁的段。实际上,只有在最后一个进程将其分离之后 
            (也就是说,关联结构shmid_ds的shm_nattch成员为零时), 
            段才会被销毁。
            调用者必须是段的所有者或创建者,或具有特权。buf参数被忽略。
    buf:共享内存属性结构体指针
返回值:
    成功 0
    失败 -1 重置错误码

(三)使用示例

read.c

#include <my_head.h>

#define SHM_PAGE 1024*4
int main(int argc, char const *argv[])
{
    //获取键值
    key_t key = ftok("/home/linux/05work",'A');
    if(-1 == key)
        ERR_LOG("ftok error");

    //创建共享内存
    int shmid = shmget(key, SHM_PAGE,IPC_CREAT|0666);
    if(-1 == shmid)
        ERR_LOG("shmget error");
    
    //映射共享内存
    char *addr = (char *)shmat(shmid,NULL,SHM_RDONLY);

    while(1){
        getchar();//防止刷屏,回车一次打印一次  
        if(!strcmp(addr,"quit")){
            break;
        }    
        printf("%s",addr);
    }
    //解除映射
    if(-1 == shmdt(addr)){
        ERR_LOG("shmid error");
    }

    //第三个参数会被忽略
    if(-1 == shmctl(shmid,IPC_RMID,NULL)){
        if(EINVAL == errno){
            return 0;
        }
        ERR_LOG("shmctl error");
    }
    return 0;
}

write.c

#include <my_head.h>

#define SHM_PAGE 1024*4
int main(int argc, char const *argv[])
{
    //获取键值
    key_t key = ftok("/home/linux/05work",'A');
    if(-1 == key)
        ERR_LOG("ftok error");

    //创建共享内存
    int shmid = shmget(key, SHM_PAGE,IPC_CREAT|0666);
    if(-1 == shmid)
        ERR_LOG("shmget error");
    
    //映射共享内存
    char *addr = (char *)shmat(shmid,NULL,0);

    while(1){
        printf("请输入要发送的内容:");
        scanf("%s",addr);
        if(!strcmp(addr,"quit")){
            break;
        }
    }
    //解除映射
    if(-1 == shmdt(addr)){
        ERR_LOG("shmid error");
    }
    //销毁
    if(-1 == shmctl(shmid,IPC_RMID,NULL)){
        if(EINVAL == errno){
            return 0;
        }
        ERR_LOG("shmctl error");
    }
    return 0;
}

(四) 属性

struct shmid_ds{
    struct ipc_perm shm_perm;    //权限结构体
    size_t shm_segsz;             //共享内存大小,单位是字节 
    __time_t shm_atime;         //最后一次映射的时间 
    __pid_t shm_cpid;             //创建共享内存进程的pid 
    __pid_t shm_lpid;             //最后一次操作共享内存进程的pid 
    shmatt_t shm_nattch;         //共享内存映射的次数
};
struct ipc_perm{
    __key_t __key;                //ftok获取的key
    __uid_t uid;                 //用户的ID
    __gid_t gid;                 //组ID
    __uid_t cuid;                //创建共享内存的用户的ID
    __gid_t cgid;                //创建共享内存的组的ID
    unsigned short int mode;     //消息队列的权限
};

二、信号灯集—控制进程间同步

(一)特点

信号灯集:又叫做信号量数组,他是实现进程间同步的机制

在一个信号灯集中可以有很多个信号灯,这些信号灯之间工作相互互不干扰。
一般使用时使用的都是二值信号灯

(二) 相关API

1. 创建一个信号灯集

#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
功能:
    创建一个信号灯集
参数:
    key:键值
    	IPC_PRIVATE/key
    nsems:信号灯集合中信号灯的个数
    semflag:创建的标志位
        IPC_CREAT|0666 或 IPC_CREAT|IPC_EXCL|0666
返回值:
    成功 semid
    失败 -1  重置错误码
  • 补充:使用IPC_PRIVATE创建的IPC对象, key值属性为0,和IPC对象的编号就没有了对应关系。这样毫无关系的进程,就不能通过key值来得到IPC对象的编号(因为这种方式创建的IPC对象的key值都是0)。因此,这种方式产生的IPC对象,和无名管道类似,不能用于毫无关系的进程间通信。但也不是一点用处都没有,仍然可以用于有亲缘关系的进程间通信。

2. 信号灯集控制函数

int semctl(int semid, int semnum, int cmd, ...);

功能:信号灯集的控制函数

参数:
    semid信号灯集的ID
    senum:信号灯的编号 从0开始
    cmd:命令码
        SETVAL:设置信号灯的值 --->第四个参数val选项
        GETVAL:获取信号灯的值 --->不需要第四个参数
        IPC_STAT:获取信号灯集的属性--->第二参数被忽略,第四个参数buf选项
        IPC_SET :设置信号灯集的属性--->第二参数被忽略,第四个参数buf选项
        IPC_RMID:删除信号灯集 第二参数被忽略,第4个参数不用填写 
    @...:可变参
    union semun{
        int val; /* Value for SETVAL */
        struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
    };

返回值:
    成功:
        GETVAL:成功返回信号灯的值
        其余的命令码成功返回0
    失败 -1  重置错误码
  • 注:*初始化操作在两个进程中必须只能进行一次,因为进程之间运行是没有顺序的,可以出现其中一个进程已经进行了信号灯集的相关操作后,另一个进程进行初始化而导致出错。

3. 信号灯集的操作函数

int semop(int semid, struct sembuf *sops, size_t nsops);

功能:
    信号灯集中信号灯的操作函数

参数:
    semid:信号灯集的编号
    sops:操作方式
        struct sembuf{
            unsigned short sem_num; //信号灯的编号
            short sem_op; //操作方式(PV)-1:P操作,申请资源 1:V操作,释放资源
            short sem_flg; //操作的标志位 0:阻塞 IPC_NOWAIT:非阻塞方式操作
        }
    nsops:本次操作信号灯的个数

返回值:
    成功 0
    失败 -1  重置错误码
  • 注:同时对多个信号灯进行操作时,可以定义一个结构体数组

(三)封装函数

原生函数直接使用会比较繁琐,因此会做二次封装。

初始化(解决可能重复初始化的问题):

int sem_init_pack(key_t key, int nsem){
    int semid = 0;
    //不存在创建,存在退出返回semid
    if(-1 == (semid = semget(key,nsem,IPC_CREAT|IPC_EXCL|0666))){
        if(EEXIST == errno){//已存在导致的错误
            if((-1 == (semid = semget(key,nsem,IPC_CREAT|0666)))){
                return -1;
            }
            return semid;
        }else{//不是已存在导致的错误,说明出错
            return -1;
        }
    }
    //初始化,第一个置1,其余置0
    if(semctl(semid,0,SETVAL,1)){
        return -1;
    }
    for(int i = 1; i < nsem; i++){
        if(semctl(semid,i,SETVAL,0)){
            return -1;
        }
    }
    return semid;
}

单信号灯PV操作

int sem_p_pack(int semid,int semnum){
    struct sembuf sembuff={
        .sem_num=semnum,
        .sem_op=-1,
        .sem_flg=0};
    if(-1 == semop(semid,&sembuff,1)){
        return errno;
    }
    return 0;
}

int sem_v_pack(int semid,int semnum){
    struct sembuf sembuff={
        .sem_num=semnum,
        .sem_op=1,
        .sem_flg=0
    };
    if(-1 == semop(semid,&sembuff,1)){
        return errno;
    }
    return 0;
}

多信号灯PV操作
传入一个int型数组指针,数组成员就是要进行p操作的信号灯编号

int sem_p_set_pack(int semid, int *semnum, int num){
    struct sembuf sembuff[num];

    for(int i=0; i<num; i++){
        sembuff[i].sem_flg=1;
        sembuff[i].sem_op=-1;
        sembuff[i].sem_num=semnum[i];

        if(-1 == semop(semid,sembuff+i,1)){
            return errno;
        }
    }
    return 0;
}

int sem_v_set_pack(int semid, int *semnum, int num){
    if(NULL==semnum) return -1;
    struct sembuf sembuff[num];

    for(int i=0; i<num; i++){
        sembuff[i].sem_flg=1;
        sembuff[i].sem_op=1;
        sembuff[i].sem_num=semnum[i];

        if(-1 == semop(semid,sembuff+i,1)){
            return errno;
        }
    }
    return 0;
}

销毁

int sem_destroy_pack(int semid){
    if(-1 == semctl(semid,0,IPC_RMID))
    {
        //如果出现这个错误说明是已经销毁过了,无视这个错误
        if(EINVAL == errno){
            return 0;
        }
        //否则就是出错了,返回错误信息
        printf("semop error:%s",strerror(errno));
        return -1;
    }
    return 0;
}

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

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

相关文章

08-指针与数组的结合——数组指针与指针数组的区别

指针与数组的结合 示例 1:指针访问数组元素 通过指针访问数组元素的例子&#xff1a; #include <stdio.h>int main() {int arr[5] {1,2,3,4,5};//int *p1 &arr;int *p1 (int *)&arr; // 需要强制类型转换int *p2 arr;printf("*p1:%d\n", *(p1 …

【SQL】牛客网SQL非技术入门40道代码|练习记录

跟着刷题&#xff1a;是橘长不是局长哦_哔哩哔哩_bilibili 6查询学校是北大的学生信息 select device_id, university from user_profile where university 北京大学 7查找年龄大于24岁的用户信息 select device_id, gender, age, university from user_profile where age…

大模型训练的艺术:从预训练到增强学习的四阶段之旅

文章目录 大模型训练的艺术&#xff1a;从预训练到增强学习的四阶段之旅1. 预训练阶段&#xff08;Pretraining&#xff09;2. 监督微调阶段&#xff08;Supervised Finetuning, SFT&#xff09;3. 奖励模型训练阶段&#xff08;Reward Modeling&#xff09;4. 增强学习微调阶段…

关于 Redis 中集群

哨兵机制中总结到&#xff0c;它并不能解决存储容量不够的问题&#xff0c;但是集群能。 广义的集群&#xff1a;只要有多个机器&#xff0c;构成了分布式系统&#xff0c;都可以称之为一个“集群”&#xff0c;例如主从结构中的哨兵模式。 狭义的集群&#xff1a;redis 提供的…

C++~~期末复习题目讲解---lijiajia版本

目录 1.类和对象 &#xff08;3&#xff09;创建对象的个数 &#xff08;3&#xff09;全局变量&#xff0c;局部变量 &#xff08;4&#xff09;构造函数的执行次数 &#xff08;5&#xff09;静态动态析构和构造顺序 &#xff08;6&#xff09;初始化顺序和声明顺序 &a…

Postman环境变量以及设置token全局变量!

前言百度百科解释&#xff1a; 环境变量&#xff08;environment variables&#xff09;一般是指在操作系统中用来指定操作系统运行环境的一些参数&#xff0c;如&#xff1a;临时文件夹位置和系统文件夹位置等。 环境变量是在操作系统中一个具有特定名字的对象&#xff0c;它…

【微服务】springcloud-alibaba 配置多环境管理使用详解

目录 一、前言 二、配置多环境问题概述 2.1 什么是微服务多环境配置管理 2.1.1 微服务多环境配置管理问题起源 2.2 为什么要做多环境配置管理 2.3 微服务多环境配置管理解决方案 三、springboot 配置多环境管理解决方案 3.1 前置准备 3.1.1 搭建一个springboot工程 3.…

第五十六周:文献阅读

目录 摘要 Abstract 文献阅读&#xff1a;应用于地表水总磷浓度预测的可解释CEEMDAN-FE-LSTM-Transformer混合模型 一、现有问题 二、提出方法 三、方法论 1、CEEMDAN&#xff08;带自适应噪声的完全包络经验模式分解&#xff09; 2、FE&#xff08;模糊熵 &#xff09…

k8s离线部署Calico网络(2续)

下载离线镜像 百度网盘 链接&#xff1a;https://pan.baidu.com/s/14ReJW-ZyYZFLbwSEBZK6mA?pwdi6ct 提取码&#xff1a;i6ct 1.将离线镜像上传至所有服务器并解压&#xff1a; [rootmaster ~]# tar xf calico.tar.gz [rootmaster ~]# cd calico 2.所有服务器使用for循环导入…

Python 基础语法详解(三)

Python 基础语法详解&#xff08;三&#xff09; Python 运算符Python 算术运算符①理论讲解&#xff1a;②实操讲解&#xff1a; Python 比较运算符①理论讲解&#xff1a;②实操讲解&#xff1a; Python 赋值运算符Python 位运算符①理论讲解&#xff1a;②实操讲解&#xff…

JVM学习-JVM运行时参数

JVM参数选项 标准参数选项 特点 稳定&#xff0c;后续版本不会变化以【-】开头 各种选项 运行java或者java -help可以看到所有的标准选项 补充内容 -server&#xff1a;64位机器上只支持Server模式的JVM&#xff0c;适用于需要大内存的应用程序&#xff0c;默认用并行垃圾回…

【微信小程序】事件传参的两种方式

文章目录 1.什么是事件传参2.data-*方式传参3.mark自定义数据 1.什么是事件传参 事件传参:在触发事件时&#xff0c;将一些数据作为参数传递给事件处理函数的过程&#xff0c;就是事件传参 在微信小程序中&#xff0c;我们经常会在组件上添加一些自定义数据&#xff0c;然后在…

【前端】使用window.print() 前端实现网页打印详细教程(含代码示例)

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。 &#x1f913; 同时欢迎大家关注其他专栏&#xff0c;我将分享Web前后端开发、人工智能、机器学习、深…

云服务平台仿真-身份认证/授权/申请和释放IT资源[云计算3]

实验名称 云服务平台仿真--身份认证/授权/申请和释放IT资源 实验目的 通过CloudSim仿真&#xff0c;熟悉云服务平台的构建。通过CloudSim仿真&#xff0c;熟悉各种云机制在云服务中的实现原理。 实验原理 CloudSim可以模拟云服务的各类底层方法。在省略技术细节的前提下&a…

tkinter菜单栏

tkinter菜单栏 菜单栏效果代码 菜单栏 在 Tkinter 中&#xff0c;Menu 组件用于创建菜单栏、下拉菜单和上下文菜单&#xff0c;是构建图形用户界面&#xff08;GUI&#xff09;应用程序的常见需求。 效果 代码 import tkinter as tk from tkinter import messagebox# 创建主…

linux安装jdk + docker+dockercompose+aliyunACR

下载安装包 链接&#xff1a;https://pan.baidu.com/s/1AyFvPA5qwy4IxfZoTQohrQ 提取码&#xff1a;6666 安装jdk jdk-8u411-linux-x64.tar.gz 链接&#xff1a;https://pan.baidu.com/s/1BZ7J4L5PY-9nuQyxBMDGTA 提取码&#xff1a;6666 1、解压jdk tar -xvf jdk-8u411-li…

上市公司短视主义数据集(2001-2022年)

数据简介&#xff1a;上市公司短视主义是指公司管理层过于关注短期业绩和股价表现&#xff0c;而忽视公司的长期发展和战略规划。这种短视行为可能会导致公司投资决策的失误&#xff0c;影响公司的长期竞争力。 在上市公司年度报告年度中&#xff0c;通过已有的反映管理者“短…

SpringBoot的Mybatis-plus实战之基础知识

文章目录 MybatisPlus 介绍一、MyBatisPlus 集成步骤第一步、引入依赖第二步、定义mapper 二、注解TableNameTableldTableField 三、配置文件四、加解密实现步骤 在SpringBoot项目中使用Mybatis-plus&#xff0c;记录下来&#xff0c;方便备查。 MybatisPlus 介绍 为简化开发而…

C语言 图形化界面方式连接MySQL【C/C++】【图形化界面组件分享】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;MySQL之旅_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一.配置开发环境 二…

晨控CK-UR12-E01与欧姆龙NX/NJ系列EtherNet/IP通讯手册

晨控CK-UR12-E01与欧姆龙NX/NJ系列EtherNet/IP通讯手册 晨控CK-UR12-E01 是天线一体式超高频读写器头&#xff0c;工作频率默认为902MHz&#xff5e;928MHz&#xff0c;符合EPC Global Class l Gen 2&#xff0f;IS0-18000-6C 标准&#xff0c;最大输出功率 33dBm。读卡器同时…