【从浅学到熟知Linux】进程间通信之共享内存(含共享内存挂接原理、ftok/shmget/shmat/shmdt/shmctl详解、共享内存实现Client&Server)

在这里插入图片描述

🏠关于专栏:Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程等内容。
🎯每天努力一点点,技术变化看得见


共享内存引入及原理

在前面的文章中,我们介绍了匿名管道和命名管道,下面通过对比这两种通信方式,以引出共享内存通信方式。↓↓↓

匿名管道方式
其中匿名管道由操作系统在内核帮助用户维护一份匿名管道文件,在若子进程与父进程通信时,子进程将数据写入printf缓冲区,再调用系统调用将printf缓冲区内容拷贝到内核中的匿名管道文件;而父进程读取数据时,不能直接读取内核维护的匿名管道文件,需要将匿名管道文件拷贝到父进程中的临时缓冲区。因而,匿名管道在每次通信时,都会发生两次拷贝。同时,匿名管道通信时,进程读取或写入数据时,需要进行用户态到内核态的相互转换,而这种转换会产生额外开销。
在这里插入图片描述

命名管道通信方式
为了避免访问公享资源时需要转换状态(用户/内核态),命名管道方式应运而生。命名管道通过在内存创建一个具备单向通信的信道文件,当A、B进程通信时可以直接对内存中的FIFO文件进行操作,不需要用户/内核态转换;整个通信过程中,FIFO文件不会与磁盘进行任何交互(落盘),故命名管道比普通文件快。但A、B进程无法直接将数据写入该文件或直接从该文件进行读取,故写入进程需要维护一个写入缓冲区,写入完毕后,再将缓冲区的内容拷贝到FIFO文件中;读取进程需要维护一个读取缓冲区,将FIFO文件中的内容拷贝到缓冲区,再进行读取,需要两次拷贝
在这里插入图片描述

共享内存的提出
可不可以实现通信的两个进程A、B,A进程直接向内存中写入,另一个进程直接从该内存中读取,避免上述的两次拷贝呢?

共享内存方式,就是让两个通信进程直接访问同一块内存空间,直接对该空间执行读取或写入,避免了拷贝。共享的建立过程是:①在内存中申请一块空间;②将该内存挂接(映射)到通信进程的进程地址空间的共享区。

当A进程需要与B进程通信时,只需要通过共享区虚拟地址映射到物理地址,对该物理地址直接进行写入;而B进程则是通过象取虚拟地址映射到物理地址,从该物理地址直接进行读取。这样就可以避免2次拷贝。
在这里插入图片描述

那共享内存的提供者是谁呢?由于内存属于系统硬件资源,故共享内存的提供者必然是操作系统。由于通信的进程可能很多,申请的共享内存也可能很多,故操作系统需要对各个共享内存先描述再组织。即操作系统需要创建对应的内核数据结构,对系统中的共享内存的各个属性进行描述,再通过管理这些内核数据结构,实现对共享内存的管理。由此,我们可以得出结论:共享内存=共享内存块+对应的共享内存的内核数据结构

 //共享内存对应的内核数据结构
 struct shmid_ds {
   
    struct ipc_perm shm_perm;    /* Ownership and permissions */
    size_t          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 */
    pid_t           shm_cpid;    /* PID of creator */
    pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
    shmatt_t        shm_nattch;  /* No. of current attaches */
    ...
};

★ps:共享内存也存在用户态与内核态的转换,但一旦对应内存映射到进程的地址空间,则进程间通信就不会涉及到用户态与内核态的转换。换句话说,进程后序不再通过执行内核的系统调用来转递彼此的数据。

★ps:共享内存是进程间通信最快的方式,一方面用它通信时,拷贝没有缓冲区拷贝问题;另一方面,共享内存通信时,仅涉及开始时的一次系统调用(即一次用户态和内核态的转换)。

共享内存相关函数及使用实例

获取唯一key值——ftok

在命名管道中,通过文件路径的唯一性,以保证多个通信进程能够看到同一个共享资源。在共享内存也需要保证多个通信进程看到同一份共享资源,即使用key来保证资源的唯一性。key需要使用ftok函数自动生成↓↓↓

在这里插入图片描述
第一个参数pathname是值文件路径,第二个参数是项目id(用户自己指定,没有明确要求)。返回计算得到的key值,但如果传入的路径不存在,则会生成失败,返回-1。

如果多个进程需要通信,只需要传入同一个文件目录及项目id就能确定唯一的key值,即能够找到同一份共享内存资源。

★ps:谈谈key:
1.key是一个数字,这个数字是多少并不重要,关键是它必须在内核中具有唯一性,就能让不同的进程进行唯一性标识;
2.第一个进程可以通过key创建共享内存,第二个及以后的进程只要拿着同一个key值,就可以和第一个进程看到同一个共享内存;
3.对于一个已经创建好的共享内存,key在哪呢?答案是:key在共享内存的内核数据结构中(共享内存的描述对象);
4.为了保证多个进程能够看到同一个共享内存,需要约定唯一的pathname和项目id,避免找到不是同一个共享内存;
5.key和路径一样,具有唯一性。

下面给出ftok创建key值的实例代码↓↓↓

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>

int main()
{
   
	key_t key1 = ftok("/home/xiaoming", 5);
	key_t key2 = ftok("/home/xiaoming", 5);
	key_t key3 = ftok("/home/xiaoming/gitcode", 5);
	key_t key4 = ftok("/home/xiaoming/", 666);
	printf("key1 = %u\n", key1);
	printf("key2 = %u\n", key2);
	printf("key3 = %u\n", key3);
	printf("key4 = %u\n", key4);
	return 0;
}

在这里插入图片描述

创建共享内存——shmget

在这里插入图片描述
创建共享内存第一个参数key用于保证共享内存的唯一性,第二个参数标识共享内存的大小(单位:字节),下面表格给出了第三个参数的说明↓↓↓

shmflg取值 描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值