C语言 多进程编程(六)共享内存

简介: 本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。

多进程(六)

共享内存

  • 共享内存是将分配的物理空间直接映射到进程的⽤户虚拟地址空间中, 减少数据在内核空间缓存
  • 共享内存是⼀种效率较⾼的进程间通讯的⽅式

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

共享内存的模型:

img_44.png

共享内存的创建

  • 使用 shmget() 函数创建共享内存

函数头文件:

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

int shmget (key_t __key, size_t __size, int __shmflg)
  • key 是一个整数值, 用于标识共享内存块, 必须唯一
  • size 是一个整数值, 表示共享内存块的大小, 单位为字节 4K(一个内存页)的整数倍
  • shmflg 共享内存标志,是一个整数值, 用于设置共享内存的访问权限, 可以取值:
    • IPC_CREAT :创建共享内存块,如果 key 已经存在,则返回错误
    • IPC_EXCL :和 IPC_CREAT 相反,如果 key 已经存在,则返回错误
    • 0 :访问权限为读写

返回值:

  • 如果成功, 则返回一个非负整数, 该整数是共享内存块的标识符
  • 如果出错, 则返回 -1, 并设置 errno 变量

示例:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define SHM_PATH "/home/gopher"
#define SHM_ID 1234
int main() {
   
   

    key_t key;//消息队列的key
    //通过文件路径和ID生成key,
    key= ftok(SHM_PATH,SHM_ID);
    if(key==-1){
   
   
        printf("ftok() error\n");
        exit(EXIT_FAILURE);
    }
    printf("key: %d\n",key);

    //创建共享内存
    int shmid;
    shmid=shmget(key,1024,IPC_CREAT|0666);
    printf("shmid: %d\n",shmid);
    return 0;
}

运行结果:

key: -769608541
shmid: 1

img_45.png

共享内存删除

可以通过命令删除共享内存:

ipcrm -m <共享内存标识符>

也可以通过 shmctl() 函数删除共享内存:

函数头文件:

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

int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf)

函数功能:
共享内存控制函数,功能由具体的功能命令字决定, 用于控制共享内存的创建、删除、设置和获取信息等。

  • shmid 共享内存标识符, 由 shmget() 函数返回
  • cmd 功能命令字, 用于指定控制命令, 可以取值:
    • IPC_RMID :删除共享内存块,使用时候第三个参数为 NULL ,标记为删除,实际上会等到,其他映射都删除才会删除
    • IPC_SET :设置共享内存块的属性
    • IPC_STAT :获取共享内存块的属性
  • buf 指向 shmid_ds 结构体(共享内存数据结构)的指针, 用于设置或获取共享内存块的属性
  • 返回值:
    • 如果成功, 则返回 0
    • 如果出错, 则返回 -1, 并设置 errno 变量
  • shmid_ds 结构体:
  • struct shmid_ds {
         
         
      struct ipc_perm shm_perm; /* 共享内存权限 */  
      size_t shm_segsz; /* 共享内存段大小 */
      pid_t shm_lpid; /* 最后一个 attach 进程的 PID */
      pid_t shm_cpid; /* 当前 attach 进程的 PID */
      unsigned long shm_nattch; /* 当前 attach 进程的数量 */
      time_t shm_atime; /* 上次 attach 时间 */
      time_t shm_dtime; /* 上次 detach 时间 */
      time_t shm_ctime; /* 创建时间 */
      void *shm_internal; /* 内部指针 */
    };
    

删除示例:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define SHM_PATH "/home/gopher"
#define SHM_ID 1234
int main() {
   
   

    key_t key;//消息队列的key
    //通过文件路径和ID生成key,
    key= ftok(SHM_PATH,SHM_ID);
    if(key==-1){
   
   
        printf("ftok() error\n");
        exit(EXIT_FAILURE);
    }
    printf("key: %d\n",key);

    //创建共享内存
    int shmid;
    shmid=shmget(key,1024,IPC_CREAT|0666);
    printf("shmid: %d\n",shmid);


    int  ret=shmctl(shmid,IPC_RMID,NULL); //删除共享内存
    if(ret==-1){
   
   
        printf("shmctl() error\n");
        exit(EXIT_FAILURE);
    }



    return 0;


}

共享内存映射

  • 共享内存映射是将共享内存中的数据映射到进程的虚拟地址空间中, 使得进程可以直接访问共享内存中的数据
  • 共享内存映射是⼀种效率较⾼的进程间通讯的⽅式

共享内存映射的创建

  • 使用 shmat() 函数创建共享内存映射
  • 函数头文件:
  • #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    void *shmat (int __shmid, const void *__shmaddr, int __shmflg)
    

    函数功能:
    将进程地址空间中的一个区域映射到共享内存中, 并返回映射的地址。

  • shmid 共享内存标识符, 由 shmget() 函数返回

  • shmaddr 映射到共享内存中的地址, 可以为 NULL, 表示由系统选择映射地址
  • shmflg 映射标志, 可以取值:
    • SHM_RDONLY :只读映射
    • SHM_RND :映射地址随机
    • SHM_REMAP :允许映射到已存在的共享内存
    • SHM_EXEC :允许映射到可执行内存
    • 0 :默认值, 允许读写映射

返回值:

  • 如果成功, 则返回映射到共享内存中的地址
  • 如果出错, 则返回 (void *)-1, 并设置 errno 变量

解除共享内存映射

  • 使用 shmdt() 函数解除共享内存映射
  • 函数头文件:
  • #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    int shmdt (const void *__shmaddr)
    
    函数功能:

将进程地址空间中的一个区域与共享内存的映射解除。

  • shmaddr 映射到共享内存中的地址
  • 返回值:
  • 如果成功, 则返回 0
  • 如果出错, 则返回 -1, 并设置 errno 变量

示例:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define SHM_PATH "/home/gopher"
#define SHM_ID 1234
int main() {
   
   

    key_t key;//消息队列的key
    //通过文件路径和ID生成key,
    key= ftok(SHM_PATH,SHM_ID);
    if(key==-1){
   
   
        printf("ftok() error\n");
        exit(EXIT_FAILURE);
    }
    printf("key: %d\n",key);

    //创建共享内存
    int shmid;
    shmid=shmget(key,1024,IPC_CREAT|0666);
    printf("shmid: %d\n",shmid);

//    int  ret=shmctl(shmid,IPC_RMID,NULL); //删除共享内存
//    if(ret==-1){
   
   
//        printf("shmctl() error\n");
//        exit(EXIT_FAILURE);
//    }

    //映射共享内存
    void* addr=NULL;
    addr=shmat(shmid,NULL,0);
    if(addr==(void*)-1){
   
   
        printf("shmat() error\n");
        exit(EXIT_FAILURE);
    }
    printf("addr: %p\n",addr);
    //修改共享内存中的数据
    memset(addr,'A',10);

    //解除映射
    int ret=shmdt(addr);
    if(ret==-1) {
   
   
        printf("shmdt() error\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}

写入和读取共享内存中的数据

写入:


#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define SHM_PATH "/home/gopher"
#define SHM_ID 1234
int main() {
   
   

    key_t key;//消息队列的key
    //通过文件路径和ID生成key,
    key= ftok(SHM_PATH,SHM_ID);
    if(key==-1){
   
   
        printf("ftok() error\n");
        exit(EXIT_FAILURE);
    }
    printf("key: %d\n",key);

    //创建共享内存
    int shmid;
    shmid=shmget(key,1024,IPC_CREAT|0666);
    printf("shmid: %d\n",shmid);

//    int  ret=shmctl(shmid,IPC_RMID,NULL); //删除共享内存
//    if(ret==-1){
   
   
//        printf("shmctl() error\n");
//        exit(EXIT_FAILURE);
//    }

    //映射共享内存
    void* addr=NULL;
    addr=shmat(shmid,NULL,0);
    if(addr==(void*)-1){
   
   
        printf("shmat() error\n");
        exit(EXIT_FAILURE);
    }
    printf("addr: %p\n",addr);
    //修改共享内存中的数据
    memset(addr,'A',10);

    //解除映射
    int ret=shmdt(addr);
    if(ret==-1) {
   
   
        printf("shmdt() error\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}

读取:


#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define SHM_PATH "/home/gopher"
#define SHM_ID 1234
int main() {
   
   

    key_t key;//消息队列的key
    //通过文件路径和ID生成key,
    key= ftok(SHM_PATH,SHM_ID);
    if(key==-1){
   
   
        printf("ftok() error\n");
        exit(EXIT_FAILURE);
    }
    printf("key: %d\n",key);

    //创建共享内存
    int shmid;
    shmid=shmget(key,1024,IPC_CREAT|0666);
    printf("shmid: %d\n",shmid);





//    int  ret=shmctl(shmid,IPC_RMID,NULL); //删除共享内存
//    if(ret==-1){
   
   
//        printf("shmctl() error\n");
//        exit(EXIT_FAILURE);
//    }

    //映射共享内存
    void* addr=NULL;
    addr=shmat(shmid,NULL,0);
    if(addr==(void*)-1){
   
   
        printf("shmat() error\n");
        exit(EXIT_FAILURE);
    }
    printf("addr: %p\n",addr);
    //读取共享内存中的数据
    char str[1024];
    memcpy(str,addr,1024);
    printf("str: %s\n",str);
    printf("strlen(str): %d\n",strlen(str));
    //解除映射
    int ret=shmdt(addr);
    if(ret==-1) {
   
   
        printf("shmdt() error\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}

运行结果

key: -769608541
shmid: 2
addr: 0x7f724c65a000
str: AAAAAAAAAA
strlen(str): 10

大致操作流程:

img_46.png

相关文章
|
3天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
35 13
|
18天前
|
存储 编译器 程序员
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
在C语言中,内存布局是程序运行时非常重要的概念。内存布局直接影响程序的性能、稳定性和安全性。理解C程序的内存布局,有助于编写更高效和可靠的代码。本文将详细介绍C程序的内存布局,包括代码段、数据段、堆、栈等部分,并提供相关的示例和应用。
30 5
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
|
16天前
|
C语言 开发者 内存技术
探索操作系统核心:从进程管理到内存分配
本文将深入探讨操作系统的两大核心功能——进程管理和内存分配。通过直观的代码示例,我们将了解如何在操作系统中实现这些基本功能,以及它们如何影响系统性能和稳定性。文章旨在为读者提供一个清晰的操作系统内部工作机制视角,同时强调理解和掌握这些概念对于任何软件开发人员的重要性。
|
15天前
|
Linux 调度 C语言
深入理解操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅,从进程管理的基本概念出发,逐步探索到内存管理的高级技巧。我们将通过实际代码示例,揭示操作系统如何高效地调度和优化资源,确保系统稳定运行。无论你是初学者还是有一定基础的开发者,这篇文章都将为你打开一扇了解操作系统深层工作原理的大门。
|
18天前
|
消息中间件 Unix Linux
【C语言】进程和线程详解
在现代操作系统中,进程和线程是实现并发执行的两种主要方式。理解它们的区别和各自的应用场景对于编写高效的并发程序至关重要。
46 6
|
18天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
48 6
|
21天前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
40 6
|
28天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
100 13
|
22天前
|
大数据 C语言
C 语言动态内存分配 —— 灵活掌控内存资源
C语言动态内存分配使程序在运行时灵活管理内存资源,通过malloc、calloc、realloc和free等函数实现内存的申请与释放,提高内存使用效率,适应不同应用场景需求。
|
28天前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
57 11