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

Linux进程8-共享内存概念机操作、shmget/shmat/shmdt/shmctl函数用法、空间大小修改

目录

1. 共享内存

1.1基本概念

1.2核心特点

2.共享内存操作

2.1 获得一个共享存储标识符shmget

应用场景

2.2 共享内存映射(shmat)

2.3 解除共享内存映射(shmdt)

2.4 共享内存控制shmctl

2.4.1 删除共享内存标识符

2.4.2 修改权限

2.5共享内存大小修改

2.5.1标准方法1-删除旧空间开辟新空间

2.5.2标准方法2-删除旧空间开辟新空间,并写入数据


1. 共享内存

1.1基本概念

共享内存(Shared Memory)‌ 是 Linux 系统中进程间通信(IPC)的一种高效方式,允许多个进程通过访问同一块物理内存区域实现数据共享,无需内核中转数据。

共享内存是进程间通信方式中效率最高 的,原因在于进程是直接在物理内存上进行操 作,将物理地址映射到用户进程这,所以只要对其地址进行操作,就是直接对物理地址操作。
  • 核心机制‌:操作系统分配共享内存后,各进程将其映射到自身虚拟地址空间,直接读写同一物理内存区域
  • 优势‌:数据无需复制,通信速度极快,适合大规模或高频数据交换。
  • 局限性‌:需手动实现同步机制(如信号量)以避免数据竞争。

使用 shell 命令操作共享内存:
ipcs -m //查看共享内存
ipcrm -m shmid //删除共享内存

1.2核心特点

‌1.高效性

直接操作内存,避免内核介入和数据复制,性能显著优于管道、消息队列等。

2‌.全局性

所有关联进程均可访问同一内存区域,实现数据全局共享。

‌3.同步依赖

需结合信号量、互斥锁等机制保证数据一致性

2.共享内存操作

2.1 获得一个共享存储标识符shmget

应用场景
  1. 创建新共享内存段

    int shmid = shmget(ftok("/tmp", 100), 1024, IPC_CREAT | 0666);通过 ftok 生成 key,并指定权限为所有用户可读写。

    ftok函数函数原型:详细链接https://blog.csdn.net/weixin_45842280/article/details/136384000

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id); 参数:
pathname 参数是一个指向字符串的指针,用于指定一个已经存在的文件的路径名。proj_id 参数是一个用户定义的整数,可以是 0 到 255 的范围内的任意值。  ftok 函数会根据指定的 pathname 和 proj_id 生成一个唯一的键值,
用于创建或访问 System V IPC 的资源,如共享内存、信号量和消息队列。  实际上,ftok 函数根据 pathname 参数对应文件的 inode 号和 proj_id 
参数生成一个 32 位的键值,最高 8 位存放 proj_id,低 24 位存放 pathname 
对应文件的 inode 号的后 24 位。这样可以保证相同的 pathname 和 proj_id 
参数生成相同的键值。   需要注意的是,在使用 ftok 函数时要确保 pathname 
参数是一个已经存在的文件的路径名,否则会导致生成的键值不唯一。另外,
proj_id 参数的取值范围是 0 到 255,超出这个范围可能导致键值重复。 综上所述,ftok 函数是用于生成 System V IPC 键值的一个实用工具函数,
可以通过指定文件路径和项目标识来确保生成唯一的键值,用于进程间通信的各种资源的创建和访问。

2‌.获取已有内存段

int shmid = shmget(existing_key, 0, 0); // 无需指定 size 和权限

3‌.强制创建新内存段

int shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT | IPC_EXCL | 0600);使用 IPC_PRIVATE 确保唯一性,防止其他进程获取

shmget函数原型:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size,int shmflg);功能:
创建或打开一块共享内存区参数:
key:IPC 键值
size:该共享存储段的长度(字节)
shmflg:标识函数的行为及共享内存的权限。IPC_CREAT:如果不存在就创建IPC_EXCL:如果已经存在则返回失败位或权限位:共享内存位或权限位后可以设置共享内存的访问权限,格式和 open 函数的 mode_t 一样,但可执行权限未使用。返回值:
成功:返回共享内存标识符。
失败:返回-1。

程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main(int argc, char const *argv[])
{//使用ftok函数获取键值key_t mykey;if((mykey = ftok(".", 100)) == -1){perror("fail to ftok");exit(1);}//通过shmget函数创建或者打开一个共享内存,返回一个共享内存的标识符int shmid;//创建一个共享内存,500字节,不存在创建,权限:可读可写if((shmid = shmget(mykey, 500, IPC_CREAT | 0666)) == -1){perror("fail to shmget");exit(1);}printf("shmid = %d\n", shmid);system("ipcs -m");//执行ipcs -m 命令return 0;
}

运行结果:

由于共享内存段过多,使用grep命令进行过滤。

2.2 共享内存映射(shmat)

函数原型:

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);功能:
将一个共享内存段映射到调用进程的数据段中。参数:
shmid:共享内存标识符。
shmaddr:共享内存映射地址(若为 NULL 则由系统自动指定),推荐使用 NULL。
shmflg:共享内存段的访问权限和映射条件0:共享内存具有可读可写权限。SHM_RDONLY:只读。SHM_RND:(shmaddr 非空时才有效)
没有指定 SHM_RND 则此段连接到 shmaddr 所指定的地址上(shmaddr 必需页对齐)。
指定了 SHM_RND 则此段连接到 shmaddr- shmaddr%SHMLBA 所表示的地址上。返回值:
成功:返回共享内存段映射地址
失败:返回 (void *)-1

2.3 解除共享内存映射(shmdt)

函数原型:

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:
将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)。
参数:
shmaddr:共享内存映射地址。
返回值:
成功返回 0,失败返回 -1。

程序:

(1)写入数据

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>typedef struct
{int a;char b;
}MSG;int main(int argc, char const *argv[])
{//使用ftok函数获取键值key_t mykey;if((mykey = ftok(".", 100)) == -1){perror("fail to ftok");exit(1);}//通过shmget函数创建或者打开一个共享内存,返回一个共享内存的标识符int shmid;if((shmid = shmget(mykey, 500, IPC_CREAT | 0666)) == -1){perror("fail to shmget");exit(1);}printf("shmid = %d\n", shmid);system("ipcs -m");//执行ipcs -m 命令,查看共享内存//使用shmat函数映射共享内存的地址//char *text;MSG *text;if((text = shmat(shmid, NULL, 0)) == (void *)-1){perror("fail to shmat");exit(1);}//通过shmat的返回值对共享内存操作//strcpy(text, "hello world");text->a = 100;text->b = 'w';//操作完毕后要解除共享内存的映射if(shmdt(text) == -1){perror("fail to shmdt");exit(1);}system("ipcs -m");return 0;
}

(2)读取数据

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>typedef struct
{int a;char b;
}MSG;int main(int argc, char const *argv[])
{//使用ftok函数获取键值key_t mykey;if((mykey = ftok(".", 100)) == -1){perror("fail to ftok");exit(1);}//通过shmget函数创建或者打开一个共享内存,返回一个共享内存的标识符int shmid;if((shmid = shmget(mykey, 500, IPC_CREAT | 0666)) == -1){perror("fail to shmget");exit(1);}system("ipcs -m");//执行ipcs -m 命令,查看共享内存//映射共享内存的地址//char *text;MSG *text;if((text = shmat(shmid, NULL, 0)) == (void *)-1){perror("fail to shmat");exit(1);}//获取共享内存中的数据//printf("text = %s\n", text);printf("a = %d, b = %c\n", text->a, text->b);//解除共享内存映射if(shmdt(text) == -1){perror("fail to shmdt");exit(1);}system("ipcs -m");return 0;
}

运行结果:

(1)写入数据

(2)读取数据

2.4 共享内存控制shmctl

函数原型:

shmid_ds结构体结构体详细链接:https://zhuanlan.zhihu.com/p/91033039

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);功能:
共享内存空间的控制。参数:
shmid:共享内存标识符。
cmd:函数功能的控制。IPC_RMID:删除。IPC_SET:设置 shmid_ds 参数。IPC_STAT:保存 shmid_ds 参数。SHM_LOCK:锁定共享内存段(超级用户)。SHM_UNLOCK:解锁共享内存段。
buf:shmid_ds 数据类型的地址,用来存放或修改共享内存的属性。返回值:
成功返回 0,失败返回 -1。shmid_ds结构体:
struct shmid_ds 
{// 权限相关struct ipc_perm shm_perm;	/* operation perms */// 共享内存的大小int	shm_segsz;		/* size of segment (bytes) */time_t	shm_atime;		/* last attach time */time_t	shm_dtime;		/* last detach time */time_t	shm_ctime;		/* last change time */// 创建该结构体的进程unsigned short	shm_cpid;	/* pid of creator */unsigned short	shm_lpid;	/* pid of last operator */// 当前使用该共享内存的进程数short	shm_nattch;		/* no. of current attaches *//* the following are private */// 共享内存的页数unsigned short   shm_npages;	/* size of segment (pages) */// 指向共享的物理内存的指针unsigned long   *shm_pages;	/* array of ptrs to frames -> SHMMAX */ // 使用该共享内存的进程信息struct vm_area_struct *attaches; /* descriptors for attaches */
}

2.4.1 删除共享内存标识符

程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main(int argc, char const *argv[])
{//使用ftok函数获取键值key_t mykey;if((mykey = ftok(".", 100)) == -1){perror("fail to ftok");exit(1);}//通过shmget函数创建或者打开一个共享内存,返回一个共享内存的标识符int shmid;if((shmid = shmget(mykey, 500, IPC_CREAT | 0666)) == -1){perror("fail to shmget");exit(1);}printf("shmid = %d\n", shmid);system("ipcs -m");//执行ipcs -m 命令,查看共享内存//通过shmctl函数删除共享内存if(shmctl(shmid, IPC_RMID, NULL) == -1){perror("fail to shmctl");exit(1);}system("ipcs -m");return 0;
}

运行结果:

(1)第一次 ipcs -m 命令

(2)第二次 ipcs -m 命令

终端执行 ipcs -m | grep 98355 命令,无输出,已删除。

2.4.2 修改权限

程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main(int argc, char const *argv[])
{//使用ftok函数获取键值key_t mykey;if((mykey = ftok(".", 100)) == -1){perror("fail to ftok");exit(1);}//通过shmget函数创建或者打开一个共享内存,返回一个共享内存的标识符int shmid;if((shmid = shmget(mykey, 500, IPC_CREAT | 0666)) == -1){perror("fail to shmget");exit(1);}printf("shmid = %d\n", shmid);//system("ipcs -m");//执行ipcs -m 命令,查看共享内存struct shmid_ds info;int ctl = shmctl(shmid, IPC_STAT, &info);  // 获取当前状态信息if(ctl == -1){perror("fail to shmctl");exit(1);}printf("权限 = %o\n", info.shm_perm.mode);info.shm_perm.mode = 0644;  // 修改权限ctl = shmctl(shmid, IPC_SET, &info);// 提交修改if(ctl == -1){perror("fail to shmctl");exit(1);}printf("提交修改后 权限 = %o\n", info.shm_perm.mode);// 验证修改是否生效struct shmid_ds new_info;ctl = shmctl(shmid, IPC_STAT, &new_info);if(ctl == -1){perror("fail to shmctl");exit(1);}printf("新修改  权限 = %o\n" new_info.shm_perm.mode);system("ipcs -m");//执行ipcs -m 命令,查看共享内存//通过shmctl函数删除共享内存if(shmctl(shmid, IPC_RMID, NULL) == -1){perror("fail to shmctl");exit(1);}return 0;
}

运行结果:

2.5共享内存大小修改

2.5.1标准方法1-删除旧空间开辟新空间

程序:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>//场景一:标准方法(删除旧内存段 + 重新创建)
int main() 
{// 1. 创建初始共享内存(100字节)int shmid = shmget(IPC_PRIVATE, 100, 0666 | IPC_CREAT);if (shmid == -1) {perror("shmget failed");return -1;}struct shmid_ds info;int ctl = shmctl(shmid, IPC_STAT, &info);  // 获取当前状态信息if(ctl == -1){perror("fail to shmctl");exit(1);}printf("空间大小 = %d\n", info.shm_segsz);// 2. 删除旧内存段(需确保无进程附加)if (shmctl(shmid, IPC_RMID, NULL) == -1){perror("shmctl IPC_RMID failed");return -1;}// 3. 创建新内存段(200字节)int new_shmid = shmget(IPC_PRIVATE, 200, 0666 | IPC_CREAT);if (new_shmid == -1) {perror("shmget for new size failed");return -1;}ctl = shmctl(new_shmid, IPC_STAT, &info);  // 获取当前状态信息if(ctl == -1){perror("fail to shmctl");exit(1);}printf("新 空间大小 = %d\n", info.shm_segsz);// 4. 清理资源shmctl(new_shmid, IPC_RMID, NULL);  // 标记删除新内存段return 0;
}

运行结果:

2.5.2标准方法2-删除旧空间开辟新空间,并写入数据

程序:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>//场景一:标准方法(删除旧内存段 + 重新创建)
int main() 
{// 1. 创建初始共享内存(100字节)int shmid = shmget(IPC_PRIVATE, 100, 0666 | IPC_CREAT);if (shmid == -1) {perror("shmget failed");return -1;}// 2. 写入初始数据char *data = shmat(shmid, NULL, 0);strcpy(data, "Hello, World!");printf("旧空间数据: %s\n", data);struct shmid_ds info;int ctl = shmctl(shmid, IPC_STAT, &info);  // 获取当前状态信息if(ctl == -1){perror("fail to shmctl");exit(1);}printf("空间大小 = %d\n", info.shm_segsz);// 3. 删除旧内存段(需确保无进程附加)if (shmctl(shmid, IPC_RMID, NULL) == -1){perror("shmctl IPC_RMID failed");return -1;}// 4. 创建新内存段(200字节)int new_shmid = shmget(IPC_PRIVATE, 200, 0666 | IPC_CREAT);if (new_shmid == -1) {perror("shmget for new size failed");return -1;}ctl = shmctl(new_shmid, IPC_STAT, &info);  // 获取当前状态信息if(ctl == -1){perror("fail to shmctl");exit(1);}printf("新 空间大小 = %d\n", info.shm_segsz);// 5. 迁移数据到新内存段char *new_data = shmat(new_shmid, NULL, 0);//映射内存strcpy(new_data, data);printf("新空间数据: %s\n", new_data);// 6. 清理资源shmdt(data);//结束映射shmdt(new_data);shmctl(new_shmid, IPC_RMID, NULL);  // 标记删除新内存段return 0;
}

运行结果:

http://www.xdnf.cn/news/4270.html

相关文章:

  • 【LLIE专题】基于 CLIP 的无监督背光增强算法
  • 【HarmonyOS 5】鸿蒙用户头像编辑功能实践
  • HA: Natraj靶场渗透测试
  • 2024 ICPC武汉邀请赛暨湖北省赛 题解
  • Vue 自定义指令输入校验过滤
  • AI Agent开发第57课-AI用在销售归因分析场景中-用随机森林从0构建自己的“小模型”
  • 亿级流量系统架构设计与实战(四)
  • Select Rows组件研究
  • 指针的应用
  • SQL注入总结
  • 【C++】C++中的命名/名字/名称空间 namespace
  • 【东枫科技】代理英伟达产品:智能网卡的连接线
  • 在 Win11 下安装 Wireshark 的详细步骤
  • ROS学习——IMU惯性测量单元节点的原理与编写(含C++和Python代码)
  • 【iOS】源码阅读(二)——NSObject的alloc源码
  • CSS网格布局
  • SQL 与 Python:日期维度表创建的不同选择
  • 人工智能与生命科学的深度融合:破解生物医学难题,引领未来科技革命
  • Linux远程管理
  • 2025年软件工程与数据挖掘国际会议(SEDM 2025)
  • Android 开发中JDK 的使用和配置详解
  • 机器视觉的PCB激光褪镀层的应用
  • 【基于SSM+Vue+Maven+Mysql】音乐管理系统(附源码+数据库脚本)
  • 决 策 树
  • 《深度学习实践教程》[吴微] ch-5 3/5层全连接神经网络
  • 使用Java和LangChain4j实现人工智能:从分类到生成式AI
  • CentOS 7 安装指定版本 Docker 及镜像加速/配置优化攻略
  • 候选码 主属性 非主属性
  • STM32--RCC--时钟
  • 【大模型系列篇】开启AI自主科研新时代:WebThinker开源深度研究框架