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

《Linux C编程实战》笔记:共享内存

共享内存是分配一块能被其他进程访问的内存。每个共享内存段在内核中维护一个内部数据结构shmid_ds(和消息队列、信号量一样),该结构定义在头文件linux/shm.h中,这是我从源码里抄的

#include<linux/shm.h>
struct shmid_ds {struct ipc_perm		shm_perm;	/* 操作许可 */int			shm_segsz;	/* size of segment (bytes) */__kernel_old_time_t	shm_atime;	/* 最后一个进程访问共享内存的时间 */__kernel_old_time_t	shm_dtime;	/* 最后一个进程离开共享内存的时间 */__kernel_old_time_t	shm_ctime;	/* last change time */__kernel_ipc_pid_t	shm_cpid;	/* pid of creator */__kernel_ipc_pid_t	shm_lpid;	/* pid of last operator */unsigned short		shm_nattch;	/* 当前使用该共享内存段的进程数量 */unsigned short 		shm_unused;	/* compatibility */void 			*shm_unused2;	/* ditto - used by DIPC */void			*shm_unused3;	/* unused */
};

但是总结了上一节的经验,我发现sys/shm.h也同样定义了该结构体,所以真正编写代码时可能只用包含sys/shm.h头文件

共享内存的创建与操作

共享内存区的创建

Linux下使用函数shmget来创建一个共享内存区,或者访问一个已经存在的共享内存区。该函数定义在头文件linux/shm.h中?

但是根据我自己的linux来看,linux/shm.h并没有该函数,在sys/shm.h中定义了该函数,所以以事实为准,认为该函数定义在sys/shm.h中。

并且根据实践,linux/shm.h和sys/shm.h同时包含的话编译不给过,所以下面就只用sys/shm.h了

#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
  • key:用于标识共享内存段的关键字。
  • size:要创建的共享内存段的大小(以字节为单位)。
  • shmflg:用于指定创建共享内存段的访问权限和其他标志位。

返回值:

  • 如果成功,返回一个非负整数,该整数是共享内存段的标识符(即共享内存的ID)。
  • 如果失败,返回 -1,并设置 errno 来指示错误的原因。

该函数用法与消息队列和信号量集的创建一样。key一般通过ftok得到,shmflg的话一般是IPC_CREATE或者IPC_EXCL|IPC_CREATE 再加上权限的组合。具体含义请到消息队列的那一节查看:《Linux C编程实战》笔记:消息队列-CSDN博客

如果是创建的话,size要大于0;如果只是访问,size置为0.

共享内存区的操作

在使用共享内存区之前,必须通过shmat函数将其附加到进程的地址空间。进程与共享进程就建立了连接。shmat调用成功后就会返回一个指向共享内存区的指针,使用该指针就可以访问共享内存区了,如果失败返回-1.该函数定义在sys/shm.h中(这也是我根据实际改的,书上还是说是linux/shm.h)

#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
  • shmid:要连接的共享内存段的标识符(由 shmget 函数返回的ID)。
  • shmaddr:指定共享内存连接到进程地址空间的起始地址,通常设置为 NULL,让系统自动选择一个合适的地址。
  • shmflg:用于指定连接共享内存的选项,通常为0。

返回值:

  • 如果成功,返回一个指向共享内存段第一个字节的指针。
  • 如果失败,返回 (void *) -1,并设置 errno 来指示错误的原因。

当进程结束时使用共享内存区时,要通过函数shmdt断开与共享区内存的连接。该函数声明在sys/shm.h

#include <sys/shm.h>int shmdt(const void *shmaddr);

参数shmaddr为shmget的返回值。该函数调用成功后,返回0,否则返回-1.进程脱离共享内存区后,数据结构shmid_ds中的shm_nattch就会减1.但是共享内存段依然存在,只有shm_nattch为0后,即没有任何进程再使用该共享内存区,共享内存区才应该在内核中被删除。一般来说,一个进程终止时,他所附加的共享内存区都会自动脱离。

共享内存区的控制

#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • shmid 是共享内存区的标识符,通常由 shmget 函数返回。
  • cmd 是要执行的操作,可以是各种控制命令之一。
  • buf 是一个指向 shmid_ds 结构的指针,用于传递或获取与共享内存相关的信息。

常用的控制命令包括:

  • IPC_STAT:获取共享内存区的状态信息,并将其存储在 buf 中。
  • IPC_SET:设置共享内存区的状态信息,buf 中包含了要设置的信息。
  • IPC_RMID:删除共享内存区。

如果你想删除共享内存区,可以将 cmd 参数设置为 IPC_RMID,并将共享内存区的标识符 shmid 作为 shmctl 函数,buf设置为NULL。

示例程序

本例通过读写者问题(不考虑优先级)来演示共享内存和呃呃信号量如何配合使用。这里的读者写者问题要求一个进程读共享内存的时候,其他进程不能写内存;当一个进程写共享内存的时候,其他进程不能读内存

首先定义了一个包含公用函数的头文件sharemem.h

#pragma once//这是C++的,毕竟我用的都是g++编译器
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include<errno.h>
#define SHM_SIZE 1024
union semun{int val;struct semid_ds *buf;unsigned short *array;
};
int createsem(const char *pathname,int proj_id,int members,int init_val){//创建信号量集key_t msgkey;int index,sid;union semun semopts;if((msgkey=ftok(pathname,proj_id))==-1){perror("ftok error!\n");return -1;}if((sid=semget(msgkey,members,IPC_CREAT|0666))==-1){//创建信号量集perror("semget call failed.\n");return -1;}semopts.val=init_val;for(index=0;index<members;index++){semctl(sid,index,SETVAL,semopts);//设置信号量的初值}return sid;
}
int opensem(const char *pathname,int proj_id){//打开信号量集key_t msgkey;int sid;if((msgkey=ftok(pathname,proj_id))==-1){perror("ftok error!\n");return -1;}if((sid=semget(msgkey,0,IPC_CREAT|0666))==-1){perror("semget call failed.\n");return -1;}return sid;//返回信号量集的id
}
int sem_p(int semid,int index){//p操作,消耗信号量struct sembuf buf={0,-1,IPC_NOWAIT};if(index<0){perror("index of array cannot equals a minus value");return -1;}buf.sem_num=index;if(semop(semid,&buf,1)==-1){perror("a wrong operation to semaphore occurred!");return -1;}return 0;
}
int sem_v(int semid,int index){//v操作,增加信号量struct sembuf buf={0,+1,IPC_NOWAIT};if(index<0){perror("index of array cannot equals a minus value");return -1;}buf.sem_num=index;if(semop(semid,&buf,1)==-1){perror("a wrong operation to semaphore occurred!");return -1;}return 0;
}
int sem_delete(int semid){//删除信号量集return semctl(semid,0,IPC_RMID);
}
int wait_sem(int semid,int index){//等待信号量while (semctl(semid,index,GETVAL)==0)//如果信号量等于0,也就是没有资源,就等待{sleep(1);}return 1;
}
int createshm(const char *pathname,int proj_id,size_t size){//创建共享内存key_t shmkey;int sid;if((shmkey=ftok(pathname,proj_id))==-1){perror("ftok error!\n");return -1;}if((sid=shmget(shmkey,size,IPC_CREAT|0666))==-1){perror("shmget call failed.\n");return -1;}return sid;
}

writer和reader程序,两程序在进入共享内存区之前,都要检查信号量的值是否为1(相当于是否能进入共享内存区),如果不为1,调用sleep()进入睡眠状态直到信号值变为1。进入共享内存区之后,将信号的值减1(相当于加锁),这样就实现了互斥的访问共享资源。在退出共享内存区的时候,将信号量加1(相当于解锁)。

writer:

#include"sharemem.h"
#include<string.h>
int main(){int semid,shmid;char *shmaddr;char write_str[SHM_SIZE];if((shmid=createshm(".",'m',SHM_SIZE))==-1){//创建或打开共享内存exit(1);}if((shmaddr=(char *)shmat(shmid,NULL,0))==(char *)-1){//获取共享内存的地址perror("attach shared memory error!\n");exit(1);}if((semid=createsem(".",'s',1,1))==-1){//创建信号量集exit(1);}while(1){wait_sem(semid,0);//先等信号量解锁sem_p(semid,0);//在获取资源,给信号量上锁printf("writer:");fgets(write_str,1024,stdin);//从标准输入读入int len=strlen(write_str)-1;write_str[len]='\0';strcpy(shmaddr,write_str);//写入共享内存sleep(10);sem_v(semid,0);//解锁sleep(10);}
}

reader:

#include"sharemem.h"
#include<string.h>
int main(){int semid,shmid;char *shmaddr;char write_str[SHM_SIZE];if((shmid=createshm(".",'m',SHM_SIZE))==-1){exit(1);}if((shmaddr=(char *)shmat(shmid,NULL,0))==(char *)-1){perror("attach shared memory error!\n");exit(1);}if((semid=opensem(".",'s'))==-1){//打开信号量集exit(1);}while(1){printf("reader:");wait_sem(semid,0);sem_p(semid,0);printf("%s\n",shmaddr);//读入共享内存sleep(10);sem_v(semid,0);sleep(10);}
}

编译,因为是自己编写的头文件,需要加-I选项,这里我的头文件和cpp文件是同一路径,所以用的就是.,请根据自己的路径来。

最后在运行

reader会在writer发送信息10s后打印信息。

写在最后

由于本人要考研了,播客可能不会再长更。这本《Linux C编程实战》其实也差不多完结了,还剩网络编程章节没有讲,我肯定是没时间讲了,可能考研失败了会回来继续。《Primer C++》的课后题还有第八章的存货,后续章节只能随缘更新。Qt部分真烂尾了,写Qt项目的注释实在太累了。最有可能更新的部分是力扣题讲解,因为写来准备复试上机。

最后祝大家,也祝我一切顺利

相关文章:

《Linux C编程实战》笔记:共享内存

共享内存是分配一块能被其他进程访问的内存。每个共享内存段在内核中维护一个内部数据结构shmid_ds(和消息队列、信号量一样)&#xff0c;该结构定义在头文件linux/shm.h中,这是我从源码里抄的 #include<linux/shm.h> struct shmid_ds {struct ipc_perm shm_perm; /* 操…...

【GitHub】修改默认分支

GitHub的默认分支为main&#xff0c;但我们常常习惯使用master作为默认分支&#xff0c;那在GitHub上如何将master修改为默认分支呢&#xff1f; 全局修改 点击头像&#xff0c;选择菜单栏中的设置 输入master作为默认分支&#xff0c;然后执行updating即可&#xff01; 单项…...

常用Linux 命令汇总

1、基本命令 uname -m 显示机器的处理器架构 uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 (SMBIOS / DMI) hdparm -i /dev/hda 罗列一个磁盘的架构特性 hdparm -tT /dev/sda 在磁盘上执行测试性读取操作系统信息 arch 显示机器的处理器架构 uname -m 显示…...

13 双口 RAM IP 核

双口 RAM IP 核简介 双口 RAM IP 核有两个端口&#xff0c;它又分为伪双端口 RAM 和真双端口 RAM&#xff0c;伪双端口 RAM 一个端口只能读&#xff0c;另一个端口只能 写&#xff0c;真双端口 RAM 两个端口都可以进行读写操作。同时对存储器进行读写操作时就会用到双端口 RAM…...

【高级数据结构】Trie树

原理 介绍 高效地存储和查询字符串的数据结构。所以其重点在于&#xff1a;存储、查询两个操作。 存储操作 示例和图片来自&#xff1a;https://blog.csdn.net/qq_42024195/article/details/88364485 假设有这么几个字符串&#xff1a;b&#xff0c;abc&#xff0c;abd&…...

国际化 Vue-i18n的安装与使用 (Vue2.0 / Vue3.0)

国际化 Vue-i18n的安装与使用 &#xff08;Vue2.0 / Vue3.0&#xff09; 一、Vue-i18n是什么&#xff1f; Vue-I18n是 Vue.js 的国际化插件。它可以轻松地将一些本地化功能集成到你的 Vue.js 应用程序中。简单来说就是可以帮助用户进行语言的切换” 二、使用步骤 1.引入库 代码…...

Linux 学习笔记(8)

八、 启动引导 1 、 Linux 的启动流程 1) BIOS 自检 2) 启动 GRUB/LILO 3) 运行 Linux kernel 并检测硬件 4) 挂载根文件系统 5) 运行 Linux 系统的第一个进程 init( 其 PID 永远为 1 &#xff0c;是所有其它进程的父进程 ) 6) init 读取系统引导配置文件…...

【python】1.python3.12.2和pycharm社区版的安装指南

欢迎来CILMY23的博客喔&#xff0c;本篇为【python】1.python3.12.2和pycharm社区版的安装指南&#xff0c;感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 目录 一、python3.12.2的下载与安装 1.1下载 1.2安装 二、pycharm的安装 2.1下载安装 2…...

Ubuntu将c++编译成.so文件并测试

一、准备cpp和h文件 创建test.cpp 在cpp中定义相加的函数funcAdd&#xff0c;给出函数的细节代码 #include <iostream> using namespace std;int funcAdd(int x, int y) {return xy; }创建test.h 在h中声明定义的函数&#xff0c;不需要任何细节 #ifndef __TEST__ #…...

数据分析-Pandas数据的探查面积图

数据分析-Pandas数据的探查面积图 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表&…...

美团分布式 ID 框架 Leaf 介绍和使用

一、Leaf 在当今日益数字化的世界里&#xff0c;软件系统的开发已经成为了几乎所有行业的核心。然而&#xff0c;随着应用程序的规模不断扩大&#xff0c;以及对性能和可扩展性的需求不断增加&#xff0c;传统的软件架构和设计模式也在不断地面临挑战。其中一个主要挑战就是如…...

Ubuntu20.04: UE4.27 中 Source Code 的编辑器下拉框没有 Rider选项

问题描述 最近想用 Rider 作为 UE4 开发的 IDE&#xff0c;但安装好 Rider 后&#xff0c;发现编辑器下拉框中没有 Rider 的选项&#xff0c;我检查了 UE4 的插件&#xff0c;发现 Rider Integration 插件已经安装且启用的。 环境&#xff1a;Ubuntu 20.04 UE4.27 Rider2023…...

【论文阅读-PRIVGUARD】Day4:3节

3 PRIVANALYZER&#xff1a;强制执行隐私政策的静态分析 本节介绍PRIVANALYZER&#xff0c;这是一个用于强制执行由PRIVGUARD追踪的隐私政策的静态分析器**。我们首先回顾LEGALEASE政策语言&#xff0c;我们使用它来正式编码政策&#xff0c;然后描述如何静态地强制执行它们**…...

新一代电话机器人开源PHP源代码

使用easyswoole 框架开发的 新一代电话机器人开源PHP源码 项目地址&#xff1a;https://gitee.com/ddrjcode/robotphp 代理商页面演示地址 http://119.23.229.15:8080 用户名&#xff1a;c0508 密码&#xff1a;123456 包含 AI外呼管理&#xff0c;话术管理&#xff0c;CR…...

dockerdocker-copose_限制容器cpu和内存

本文目录 docker的限制方式限制CPU占用限制内存占用 docker-compose docker的限制方式 限制CPU占用 Docker使用--cpus参数来限制容器的CPU资源。该参数指定了分配给容器的CPU核心数量或百分比。 例子&#xff1a;限制CPU使用个数 docker run --cpus2 <imageName>以上…...

【leetcode】圆圈中最后剩下的数字

目录 1. 问题 2. 思路 3. 代码 4. 运行 1. 问题 本题即为典型的约瑟夫问题&#xff0c;通过递推公式倒推出问题的解。原始问题是从n个人中每隔m个数踢出一个人&#xff0c;原始问题变成从n-1个人中每隔m个数踢出一个人…… 示例 1&#xff1a; 输入: n 5, m 3 输出: 3…...

利用python批量将.shp文件转换坐标生成.geojson文件,再将.geojson转换成.csv文件,最后将csv文件插入数据库表

第一步&#xff1a;.shp批量转.geojson # author: JMY # 创建时间: 2024/2/26 17:12 # 批量将.shp文件生成geojson文件并转换坐标为3857import os import geopandas as gpd# 定义输入和输出文件夹路径 input_folder shp文件 output_folder geojson文件# 定义输入和输出坐标系…...

远程服务器Ubuntu 18.04安装VNC远程桌面

一、安装vnc 1.安装图形化界面工具 # 安装过程中会弹窗让选择配置&#xff0c;选lightdm sudo apt install ubuntu-desktop sudo apt-get install gnome-panel gnome-settings-daemon metacity nautilus gnome-terminal 2.安装vnc sudo apt-get install x11vnc3.安装LightD…...

30天自制操作系统(第23天)

23.1 编写malloc 参考第22天的内容&#xff0c;在绘制窗口前先分配了150*50个字节大小的内存&#xff0c;所以导致该文件经编译后有7.6k左右&#xff0c;能否在其中使用指针呢&#xff1f;当需要开辟空间时&#xff0c;移动指针即可。在之前的章节中也有函数memman_alloc函数可…...

基于Rust语言,和WebAssembly技术,与JavaScript结合,的具体应用场景

基于Rust语言与WebAssembly&#xff08;Wasm&#xff09;技术并与JavaScript结合&#xff0c;可以应用于多个场景&#xff0c;特别是在需要高性能和/或低级系统访问的情况下。下面是一些具体的应用场景&#xff1a; 性能密集型任务: Rust加上Wasm适合执行计算密集型任务&#x…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...