小工具知识网

当前位置: 首页 > 行业知识 > 《Linux C编程实战》笔记:共享内存

行业知识

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

2026-10-31 14:57:31 来源:互联网转载

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

#includestruct shmid_ds {struct ipc_permshm_perm;/* 操作许可 */intshm_segsz;/* size of segment (bytes) */__kernel_old_time_tshm_atime;/* 最后一个进程访问共享内存的时间 */__kernel_old_time_tshm_dtime;/* 最后一个进程离开共享内存的时间 */__kernel_old_time_tshm_ctime;/* last change time */__kernel_ipc_pid_tshm_cpid;/* pid of creator */__kernel_ipc_pid_tshm_lpid;/* pid of last operator */unsigned shortshm_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 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 void *shmat(int shmid, const void *shmaddr, int shmflg);
      • shmid:要连接的共享内存段的标识符(由 shmget 函数返回的ID)。
      • shmaddr:指定共享内存连接到进程地址空间的起始地址,通常设置为 NULL,让系统自动选择一个合适的地址。
      • shmflg:用于指定连接共享内存的选项,通常为0。

        返回值:

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

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

          #include int shmdt(const void *shmaddr);

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

          共享内存区的控制

          #include 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#include#include#include#include#include#include#include#define SHM_SIZE 1024union 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

上一篇:主机怎么做服务器?

下一篇:【笔记】-编程语言以及应用领域