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
应用场景
-
创建新共享内存段
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;
}
运行结果: