linux进程间通信:system V 共享内存

思维导图如下

在这里插入图片描述

通信原理
  • 多个进程共享物理内存的同一块区域(通常称之为“段”:segment)
  • 抛弃了内核态消息转存处理的过程,让两个进程直接通过一块内存进行通信

我们普通的像PIPE,FIFO,消息队列等的通信方式如下图:
在这里插入图片描述
这种方式的通信不论读写,都需要内核态(系统调用 read,write,pipe,mkfifo,msgget,msgsnd,msgrcv等)的介入,而且都需要经过数据从虚拟地址空间到物理地址空间的拷贝。
而共享内存的通信方式则都避免了以上的通信问题,直接为两个进程开辟相同的内存空间进行数据交互。
在这里插入图片描述

优势
  • 减少了内存的拷贝(从用户拷贝到内核,从内核拷贝到用户)
  • 减少了2次系统调用(系统调用比较消耗性能,因为CPU处理系统调用时需要从用户态切换到内核态),提高了系统性能
运行流程
  • 获取共享内存对象的ID
  • 将共享内存映射至本进程虚拟内存空间的某个区域(每个用户进程操作系统都有3G的虚拟进程空间)
  • 不同进程对这块内存进行读写、传输数据
  • 当进程不再使用这块共享内存时,解除映射关系
  • 当没有进程需要共享内存的时候,则删除该共享内存
编程接口
  • 获取共享内存的对象ID ,创建或打开一个共享内存对象
    a. 头文件<sys/types.h> <sys/shm.h>
    b. int shmget (key_t key, size_t size, int shmflg);
    c. 函数参数

    • key IPC对象的键值,一般为IPC_PRIVATE 或者ftok返回的key值
    • size 共享内存的大小,一般为物理内存页(4K)的整数倍
    • shmflg
      IPC_CREAT :如果不存在指定的key值,那么就创建一个
      IPC_EXCL: 若key值指定的内存存在,且指定了IPC_CEAT,则回复EXIST错误
      IPC_HUGETLB 使用巨页(huge page)

    d. 返回值:共享内存的标识ID

  • 映射共享内存:
    a. 头文件 <sys/shm.h>
    b. void *shmat(int shmid,const void *shmaddr,int shmflg);
    c. 功能:将shmid标识的共享内存引入到当前进程的虚拟地址空间
    d. 函数参数:

    • shmid 共享内存的IPC对象ID
    • shmaddr
      若为NULL:共享内存会被attach到一个合适的虚拟地址空间,建议使用NULL
      不为NULL:系统会根据参数以及地址边界对齐等分配一个合适的地址
    • shmflg
      IPC_RDONLY:附加只读权限,不指定的话返回默认是读写权限
      IPC_REMAP:替换位于shmaddr处的任意既有映射(共享内存的段或者内存映射)
      SHM_RND:将shmaddr四舍五入为SHMMLBA字节的倍数

    e. 返回值:共享内存段的地址

  • 解除共享内存映射
    a. 头文件 <sys/shm.h>
    b. int shmdt(const void * shmaddr);
    c. 功能:解除内粗映射,将共享内存分离出当前进程的地址空间
    d. 函数参数:

    • shmaddr 共享内存地址

    e. TIPS:

    • 通过fork创建的子进程会继承父进程所附加的共享内存,父子进程可以通过共享内存进行IPC通信,在exec系统调用中,所有附加的共享内存段都会被分离
    • 函数shmdt仅仅是使进程和共享内存脱离关系,将共享内存的引用计数是减1
    • 当共享内存的引用计数为0的时候,调用shmctl的IPC_RMID命令才会删除共享内存
  • 设置共享内存属性
    a. 头文件 <sys/shm.h>
    b. int shmctl(int shmid,int cmd,struct shmid_ds *buf);
    c. 函数功能:获取/设置共享内存对象属性
    d. 函数参数
    - shmid 共享内存对象的ID
    - cmd:
    IPC_RMID :删除共享内存及关联的shmid_ds数据结构
    IPC_STAT: 将该共享内存关联的shmid_ds数据结构拷贝到参数buf中
    IPC_SET: 使用buf中的数据更新与该共享内存对象相关联的shmid_ds
    IPC_INFO:获取系统共享内存相关的信息
    IPC_LOCK: 将一个共享内存段锁进内存,防止被swap出去
    IPC_UNLOCK: 将一个共享内存解锁

  • shmid_ds *buf数据结构如下

    struct shmid_ds {
         struct ipc_perm  shm_perm;     /* operation permissions */
         int              shm_segsz;    /* size of segment in bytes */
         pid_t            shm_lpid;     /* pid of last shm op */
         pid_t            shm_cpid;     /* pid of creator */
         short            shm_nattch;   /* # of current attaches */
         time_t           shm_atime;    /* last shmat() time*/
         time_t           shm_dtime;    /* last shmdt() time */
         time_t           shm_ctime;    /* last change by shmctl() */
         void            *shm_internal; /* sysv stupidity */
     };
    
编程实例

写端shm_write.c:

#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main()
{
    //key_t key = ftok("./",111);
    key_t key = 12345;
    //根据key值生成共享内存对象的唯一标识
    int shm_id = shmget(key,4096,IPC_CREAT|0666);

	//将当前进程加入共享内存,这里设置共享内存地址为NULL时
	//系统会自动为当前进程分配一个合适的内存空间,并返回共享内存的地址
    char *shm_p = shmat(shm_id , NULL, 0);
    memset (shm_p,0,4096);

	//从标准输入获取内容到共享内存的地址
    fgets(shm_p,4096,stdin);
	
	//等待输入完成,删除共享内存
    sleep(10);
	
	//当前进程创建的共享内存引用计数为0的时候,删除共享内存
    shmctl(shm_id,IPC_RMID,NULL);
    return 0;
}

读端shm_read.c

#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main() {
//    key_t key = ftok("./",111);
    key_t key = 12345;
    int shm_id = shmget(key, 4096, 0666);

	//和写端访问到了相同的共享内存区域,并获取内容存放到指针变量
    char *shm_p = shmat(shm_id , NULL, 0);
    printf("From SHM: %s\n",shm_p);
	
	//删除当前进程与共享内存的映射关系,同时共享内存的引用计数减一
    shmdt(shm_p);

    return 0;
}

通过命令ipcs -m可以查看系统共享内存
在这里插入图片描述
需要先执行写入,再去运行读端读出;以上输出如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值