进程之间的通信方式有很多种,本文详细地介绍一种:共享内存。
1.定义
这里的共享内存是指两个或者多个进程,能够共同访问一段物理内存,每个进程对内存内部的数据操作,都会影响其他进程。例如A进程,在把内存段1的值定义为5,过了1s,B进程把内存段1的值改为了10,那么之后A再来访问时,只能取到10,它的5被别的进程修改了。就像黑板和笔记本一样,黑板是共享的,大家都可以擦除别人写的东西,然后写上自己的,而笔记本就不是共享的,只能由一个人使用。
这里我们可能会想到,那么A进程的参数被B进程改了,A又不知道,那岂不是会造成很多错误。是的,所以提到共享,我们还需要研究同步,进程锁也应运而生。
2.linux下c语言创建共享内存
1)创建共享内存——shmget()函数
它的原型为:
int shmget(key_t key, size_t size, int shmflg);
参数 | 含义 |
---|---|
key | 把一个已存在的路径名和一个整数标识符转换成IPC键值,这里的key是键值,它能有效地为共享内存段命名 |
size | 指定共享内存容量 |
shmflg | 权限标志,它可以做很多事情,例如shmget(key_t key, size_t size, IPC_CRET)表示如果共享内存不存在时,需要分配一个共享内存 |
返回值 | shmget成功时,返回一个与key相关的共享内存标识符,调用失败时返回-1 |
程序想使用共享内存?
程序用shmget+key生成共享内存标识符
程序利用共享内存标识符间接访问共享内存
这里有人会问了,这个shmget到底是生成共享内存标识符还是创建共享内存,这个函数是在有共享内存时,就不创建了,只根据key返回共享内存标识符。没有共享内存时,则创建共享内存,并返回标识符。
2)共享内存连接至进程地址空间——shmat()函数
它的原型为:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
参数 | 含义 |
---|---|
shm_id | 共享内存标识符 |
shm_addr | 指定共享内存连接当前进程内存位置,通常为空,让系统自主设置 |
shmflg | 权限标志,通常为0 |
返回值 | shmat成功时,返回一个指向共享内存首地址的指针,调用失败时返回-1 |
3)共享内存从进程分离——shmctl()函数
它的原型为:
int shmdt(const void *shmaddr);
参数 | 含义 |
---|---|
shmaddr | 调用函数shmat时,返回的指向共享内存首地址的指针。 |
4)控制共享内存——shmctl()函数
它的原型为:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
参数 | 含义 |
---|---|
shm_id | 共享内存标识符 |
command | 操作符:IPC_STAT:共享内存的当前关联值覆盖shmid_ds的值。IPC_SET:在权限允许的情况下,将shmid_ds的值赋值给共享内存当前关联值。IPC_RMID:删除共享内存段 |
buf | 它指向共享内存模式和访问权限的结构 |
shmid_ds结构
struct shmid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};
整体流程
这里设AAA为我们的一个结构体,我们的共享空间是为我们的结构体AAA服务的,因此共享空间的尺寸为sizeof(struct AAA)
1)创建共享内存
shmid = shmget((key_t)8888, sizeof(struct AAA), IPC_CREAT);
2)将共享内存连接到当前地址空间
shm = shmat(shmid, 0, 0);
3)利用共享空间首地址
我们得到的共享空间首地址指针为shm,这个shm可能不是我们想要的指针类型,因此我们需要把它强制转换成我们的结构体指针
shared = (struct AAA*)shm;
为结构体中的变量a赋值5,这里可以看到结构体指针和我们定义的结构体变量差不多,不同在于:
结构体指针需要用指向运算符(->)
结构体变量需要用成员运算符(.)。
shared->a = 5;
4)使用完毕,共享内存分离
shmdt(shm)
5)删除共享内存
shmctl(shmid, IPC_RMID, 0)