Linux进程间通信:共享内存

本文详细介绍了共享内存如何实现在进程间快速通信,包括其原理、管理共享内存的数据结构以及涉及的系统调用函数如ftok、shmget、shmctl和shmat。还通过示例展示了进程间通信的代码实现和资源管理的重要性。
摘要由CSDN通过智能技术生成

一.共享内存实现进程间通信的原理

        共享内存实际操作系统实际物理内存中开辟的一段内存。       

        共享内存实现进程间通信,是操作系统在实际物理内存开辟一块空间,一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间上的共享区的一块地址空间形成映射关系

        当一个进程往该空间写入内容时,另外一进程访问该空间,会得到写入的值,即实现了进程间的通信

        注意:要实现进程间通信就必须要两个进程之间看到相同的物理空间,系统开辟的共享内存就是两进程看到的同一块物理空间。 

        共享内存是进程间通信中最快的。

二,管理共享内存的数据结构

        共享内存不仅仅可以实现两个进程之间的通信,还可以实现多进程之间的通信。要实现多进程之间的通信,就必须让操作系统将这些进程管理起来

        通过先描述在组织将通信进程之间的信息管理起来。

查看内核代码描述共享内存的数据结构如下:

/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct shmid_ds {
	struct ipc_perm		shm_perm;	/* operation perms */
	int			shm_segsz;	/* size of segment (bytes) *///共享内存空间大小
	__kernel_time_t		shm_atime;	/* last attach time *///挂接时间
	__kernel_time_t		shm_dtime;	/* last detach time *///取消挂接时间
	__kernel_time_t		shm_ctime;	/* last change time *///改变时间
	__kernel_ipc_pid_t	shm_cpid;	/* pid of creator */
	__kernel_ipc_pid_t	shm_lpid;	/* pid of last operator */
	unsigned short		shm_nattch;	/* no. of current attaches *///进程挂接数
	unsigned short 		shm_unused;	/* compatibility */
	void 			*shm_unused2;	/* ditto - used by DIPC */
	void			*shm_unused3;	/* unused */
};

描述共享内存的数据结构里保存了一个ipc_perm结构体,这个结构体保存了IPC(进程将通信)的关键信息。

/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct ipc_perm
{
	__kernel_key_t	key;//共享内存的唯一标识符
	__kernel_uid_t	uid;
	__kernel_gid_t	gid;
	__kernel_uid_t	cuid;
	__kernel_gid_t	cgid;
	__kernel_mode_t	mode; //权限
	unsigned short	seq;
};

key是共享内存的唯一标识符。

三,实现共享内存所需要的系统调用函数

ftok函数:

        作用:以路径名称项目id为参数,运用算法算出一个key值,如果算出的key值与其他值冲突,就更改路径名称或者项目id。

      

 返回值:ftok如果成功返回一个key值,如果失败返回-1。

shmget函数:

作用:创建一个共享内存 

参数:

        key:为共享内存的名字,一般是ftok的返回值。

        size:共享内存的大小,以page为单位,大小为4096的整数倍。

        shmflg:权限标志,常用两个IPC_CREAT和IPC_EXCL,一般后面还加一个权限,相当于文件的权限。

                                IPC_CREAT:创建一个共享内存返回,已存在打开返回

                                IPC_EXCL:配合着IPC_CREAT使用,共享内存已存在出错返回。

                                使用:IPC_CREAT | IPC_EXCL | 0666

返回值:

        成功返回一个非负整数,即共享内存的标识码,失败返回-1。

        为什么已经有一个key来标识共享内存,还需要一个返回值来标识共享内存?因为key是内核级别的,供内核标识,shmget返回值是用户级别的,供用户使用的。

        我们发现当进程创建了一个共享内存,没有释放,进程结束后,共享内存还在,所以第二次执行程序,会报错(报错是因为IPC_EXCL)。

        这里得出一个结论:IPC(进程将通信)资源生命周期不随进程,而是随内核的,不释放会一直占用,除非重启。所以,shmget创建的共享内存要释放掉,不然会内存泄漏

        可以用命令行来释放共享内存:ipcrm -m shmid(shmget返回值)

我们不能每次进行完共享内存通信之后再手动的将共享内存释放,可以用下面的函数来释放共享内存。

shmctl函数:

作用:用于控制共享内存

参数:        shmid:共享内存的标识

                   cmd:以什么方式来控制共享内存。IPC_RMID是释放共享内存

                   buf:指向一个共享内存的数据结构 。struct shmid_ds

返回值:成功返回0,失败返回-1。

shmat函数:

作用:使创建的共享内存与调用该函数进程的进程地址空间参数关联。

参数:

        shmid:共享内存的标识,shmget的返回值。

        shmaddr:指定进程地址空间连接的地址。如果设置为null,默认让系统定要关联的地址。

        shmflg: 权限,常见有两个SHM_RDONLY(只读)和SHM_REMAP(重新映射一个进程地址空间没这样shmaddr不能为空)。设为0,系统默认。

 返回值:

        返回映射到进程地址空间共享区的开始地址。

shmdt函数:

 作用:删除共享内存与进程地址空间的映射关系,将页表映射关系删除,释放进程地址空间。

参数:

        shmaddr:共享内存映射到进程地址空间的地址。shmat返回值。

返回值:

        成功返回0,失败返回-1

四,进程间通信的代码实现

comm.h

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
int createShm(int size);
int destroyShm(int shmid);
int getShm(int size);

comm.c

static int commShm(int size, int flags)
{
key_t _key = ftok(PATHNAME, PROJ_ID);
if(_key < 0){
perror("ftok");
return -1;
}
int shmid = 0;
if( (shmid = shmget(_key, size, flags)) < 0){
perror("shmget");
return -2;
}
return shmid;
}
int destroyShm(int shmid)
{
if(shmctl(shmid, IPC_RMID, NULL) < 0){
perror("shmctl");
return -1;
}
return 0;
}
int createShm(int size)
{
return commShm(size, IPC_CREAT|IPC_EXCL|0666);
}
int getShm(int size)
{
return commShm(size, IPC_CREAT);
}

server.c

#include "comm.h"
int main()
{
int shmid = createShm(4096);
char *addr = shmat(shmid, NULL, 0);
sleep(2);
int i = 0;
while(i++<26){
printf("client# %s\n", addr);
sleep(1);
}
shmdt(addr);
sleep(2);
destroyShm(shmid);
return 0;
}

client.c

#include "comm.h"
int main()
{
int shmid = getShm(4096);
sleep(1);
char *addr = shmat(shmid, NULL, 0);
sleep(2);
int i = 0;
while(i<26){
addr[i] = 'A'+i;
i++;
addr[i] = 0;
sleep(1);
}
shmdt(addr);
sleep(2);
return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

挣扎的泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值