• Linux下进程间的通信--共享内存


    共享内存概述:

    共享内存是进程间通信的一种方式,它允许两个或多个进程共享一个给定的存储区。共享内存是最快的一种IPC形式,因为它允许进程直接对内存进行读写操作,而不需要数据在进程之间复制。

    共享内存是进程间通信(IPC)中效率非常高的一种方式,因为它允许多个进程直接访问同一块内存区域,从而避免了数据在进程间复制的开销。这种直接访问内存的方式减少了数据传输的时间,提高了通信效率。

     在 Linux 系统中通过 ipcs -m 查看所有的共享内存

    1.创建共享内存:

    shmget() 是一个在 Linux 系统上用于创建或获取共享内存段的系统调用

    shmget()函数描述:

    1. 函数头文件:
    2. #include
    3. #include
    4. 函数原型:
    5. int shmget(key_t key, size_t size, int shmflg);
    6. 函数参数:
    7. key_t key: 这是一个键值,用于标识共享内存段。如果key是IPC_PRIVATE,则创建一个新的共享内存段,并且不与其他任何共享内存段关联。
    8. size_t size: 这是共享内存段的大小,以字节为单位。这个大小必须是一个系统页的整数倍。
    9. int shmflg: 这是一组标志位,用于控制共享内存段的权限和控制选项。
    10. 常见的标志位包括:
    11. IPC_CREAT: 如果指定了这个标志,并且key指定的共享内存段不存在,则创建一个新的共享内存段。
    12. IPC_EXCL: 与IPC_CREAT一起使用,如果共享内存段已经存在,则shmget()调用失败。
    13. 0666 (八进制): 这是权限掩码,用于设置共享内存段的权限。默认情况下,所有用户都可以读写共享内存段。
    14. 函数返回值:
    15. 成功:返回一个有效的共享内存标识符
    16. 失败:返回-1,并设置errno来指示错误
    17. 错误原因:
    18. EACCES: 没有权限访问共享内存段。
    19. EEXIST: IPC_CREAT | IPC_EXCL 被设置,但共享内存段已经存在。
    20. EINVAL: size不是系统页大小的整数倍,或者key是无效的。
    21. ENOMEM: 系统内存不足,无法创建新的共享内存段

     扩充知识--系统页大小:

    在Linux系统中,"系统页面大小"(也称为"页大小"或"分页大小")是指操作系统用于管理内存的分配单元的大小。这个大小是虚拟内存系统中页表条目的大小,也是内存管理单元(MMU)分页机制的基础。

    系统页面大小通常是4KB(即4096字节),但这个值可能会根据不同的体系结构和操作系统的实现而有所不同。例如,在某些64位体系结构上,页面大小可能是8KB或更大。这个大小对于性能优化很重要,因为它决定了内存映射和页表条目的效率。

     示例代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. int main() {
    7. size_t size = 1024; // 分配1024字节
    8. int shmflg = 0666 | IPC_CREAT; // 设置权限并创建共享内存
    9. int shmid = shmget(IPC_PRIVATE, size, shmflg);
    10. if (shmid == -1) {
    11. fprintf(stderr, "shmget failed: %s\n", strerror(errno));
    12. return 1;
    13. }
    14. printf("Shared memory ID: %d\n", shmid);
    15. return 0;
    16. }

     2.附加共享内存:

     shmat() 是一个在 Linux 系统中用于将共享内存段附加到调用进程的地址空间的系统调用。这个函数是共享内存 IPC 机制的一部分,它允许进程访问由 shmget() 创建的共享内存段

    shmat()函数描述:

    1. 函数头文件:
    2. #include
    3. #include
    4. 函数原型:
    5. void *shmat(int shmid, const void *shmaddr, int shmflg);
    6. 函数参数:
    7. int shmid: 这是由shmget()函数返回的共享内存段的标识符。
    8. const void *shmaddr: 这是一个可选的指针,指定共享内存段应该被附加到进程地址空间的哪个位置。
    9. 如果设置为NULL,系统会自动选择一个合适的地址。
    10. int shmflg: 这是一个标志位,用于控制共享内存的附加行为。
    11. 可能的标志包括:
    12. SHM_RDONLY: 将共享内存附加为只读。
    13. SHM_RND: 将共享内存附加到一个页面边界地址,这可以提高内存访问的效率。
    14. 函数返回值:
    15. 成功:返回附加的共享内存段的地址
    16. 失败:返回(void *) -1,并设置errno以指示错误的原因。
    17. 错误原因
    18. EACCES 调用进程没有请求的附加类型所需的权限,
    19. 并且在管理IPC命名空间的用户命名空间中没有CAP_IPC_OWNER功能。
    20. EIDRM shmid指向一个已删除的标识符。
    21. EINVAL 无效的shmid值,未对齐(即未对齐页面且未指定SHM_RND)或无效的shmaddr值,
    22. 或者不能在shmaddr上附加段,或者指定了SHM_REMAP而shmaddr被指定NULL。
    23. ENOMEM 无法为描述符或页表分配内存。

     示例代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main() {
    8. int shmid; // 共享内存标识符
    9. char *shared_memory; // 共享内存的指针
    10. // 创建一个共享内存段
    11. shmid = shmget(IPC_PRIVATE, 1024, 0666 | IPC_CREAT);
    12. if (shmid == -1) {
    13. perror("shmget");
    14. exit(1);
    15. }
    16. 将共享内存附加到其地址空间
    17. shared_memory = (char *)shmat(shmid, NULL, 0);
    18. if (shared_memory == (char *)(-1)) {
    19. perror("shmat");
    20. exit(1);
    21. }
    22. // 写入一些数据到共享内存
    23. strcpy(shared_memory, "Hello, World!");
    24. return 0
    25. }

     3.分离共享内存:

    当进程不再需要访问共享内存时,可以使用 shmdt 系统调用将其从进程的地址空间中分离

     shmdt()函数描述: 

    1. 函数头文件:
    2. #include
    3. #include
    4. 函数原型:
    5. int shmdt(const void *shmaddr);
    6. 函数参数:
    7. shmaddr: 这是之前通过shmat()函数附加的共享内存段的地址。
    8. 函数返回值
    9. 成功:返回0
    10. 失败:返回-1,并设置errno以指示错误。
    11. 错误原因:
    12. EINVAL: 在shmaddr上没有附加共享内存段;或者shmaddr没有在页边界上对齐

    4.读写共享内存:

    一旦共享内存段被附加到进程的地址空间,进程就可以通过指针直接访问和操作共享内存中的数据。这使得共享内存成为进程间通信(IPC)中非常快速和高效的一种方式。

    一旦共享内存段被附加,进程就可以通过指针 shmaddr 读写共享内存。这部分内存就像普通的内存一样使用

    示例代码:

    1. void* addr = shmat(shmid,NULL,0);
    2. if(addr == (void*)-1)
    3. {
    4. perror("shmat");
    5. exit(EXIT_FAILURE);
    6. }
    7. char buf[1024]={0};
    8. memcpy(buf,addr,10);
    9. printf("share memory content:%s\n",buf);
    10. shmdt(addr); //分离共享内存

    5.控制共享内存:

    在Linux中,共享内存的控制通常通过 shmctl 系统调用来实现。shmctl 函数可以对共享内存段进行多种操作,包括获取共享内存的状态、设置共享内存的状态以及删除共享内存段

    shmctl()函数描述:

    1. 函数头文件:
    2. #include
    3. #include
    4. 函数原型:
    5. int shmctl(int shmid, int cmd, struct shmid_ds *buf);:
    6. 函数参数:
    7. shmid:共享内存段的标识符,这个标识符是通过shmget()函数创建共享内存段时返回的。
    8. cmd:指定要执行的控制操作。
    9. 它可以是以下命令之一:
    10. IPC_STAT:获取共享内存段的状态信息,buf指向的结构会被填充。
    11. IPC_SET:设置共享内存段的属性,buf指向的结构包含了要设置的值。
    12. IPC_RMID:删除共享内存段,只有拥有适当权限的用户(通常是创建者或超级用户)可以执行此操作。
    13. SHM_LOCK:锁定共享内存段,防止其被交换出物理内存。
    14. SHM_UNLOCK:解锁共享内存段,允许其被交换。
    15. IPC_INFO:获取系统范围内共享内存的信息(Linux 特定)。
    16. SHM_INFO:获取系统范围内共享内存的详细信息(Linux 特定)。
    17. SHM_STAT_ANY:获取任何用户的共享内存段的状态信息(Linux 特定,自 Linux 4.17 起支持)。
    18. buf:指向shmid_ds结构的指针,该结构用于存储共享内存段的信息或新属性
    19. 函数返回值:
    20. 成功:0
    21. 失败:-1,并设置errno以指示错误原因

     shmid_ds结构体:

    示例代码:

    1. #include
    2. #include
    3. #include
    4. int main() {
    5. int shmid;
    6. struct shmid_ds shminfo;
    7. shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | 0666);
    8. // 获取共享内存段的信息
    9. if (shmctl(shmid, IPC_STAT, &shminfo) == -1) {
    10. perror("shmctl IPC_STAT");
    11. return 1;
    12. }
    13. printf("Size: %ld\n", (long)shminfo.shm_segsz);
    14. printf("Last attached: %ld\n", (long)shminfo.shm_nattch);
    15. // 删除共享内存段
    16. if (shmctl(shmid, IPC_RMID, &shminfo) == -1) {
    17. perror("shmctl IPC_RMID");
    18. exit(EXIT_FAILURE);
    19. }
    20. return 0;
    21. }

     结语:

    无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力

  • 相关阅读:
    【限时免费】20天拿下华为OD笔试之【哈希集合】2023B-明明的随机数【欧弟算法】全网注释最详细分类最全的华为OD真题题解
    Javascript——数组常用的方法
    识别和定位 - 实现工业自动化及生产数字化,推动现代工业4.0
    “快看”cms代码审计
    内容互动性的提升策略:Kompas.ai的智能工具
    java计算机毕业设计springboot+vue企业的信息管理系统
    「数据结构详解·一」树的初步
    Mycat
    K8s---HPA弹性伸缩
    Windows Install PowerCLI
  • 原文地址:https://blog.csdn.net/2301_79695216/article/details/142107478