最快的进程间通信方式—— 共享内存 。
共享内存即多个进程共享指定的一块物理内存空间,一旦此物理内存映射到共享它的进程的虚拟地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据
创建一个共享内存对象并获得其标识符或打开一个共享内存标识符
#include
#include
#include
int shmget(key_t key, size_t size, int shmflg);
参数:
key:共享内存段名称。0(IPC_PRIVATE)表示建立新共享内存对象。若为其他数值则来源于ftok返回的IPC键值
size:共享内存的大小。内存分配以页为单位,所需大小和所占大小可能不同
shmflg:模式标志,与IPC对象存取权限(如0600)进行 | 运算来确定信号量集的存取权限,取值如下
0:取共享内存标识符,若不存在则函数会报错
IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个消息队列;如果存在这样的共享内存则报错
返回值:
成功:返回共享内存标识符
失败:返回 -1,错误值在error中
需要注意的是,Linux中对于创建的共享内存进行了初始化为:0x00
连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问
void *shmat(int shmid, const void * shmaddr,int shmflg);
参数:
shmid:共享内存标识符
shmaddr:指定共享内存映射到进程内存空间中的地址。
shmflg:SHM_RDONLY 只读模式,另一个值为SHM_RND,详见下解
返回值:
成功:返回一个指针,指向连接的共享内存的起始地址
失败:返回-1,错误值在error中
注意:
shmdt与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存
int shmdt(const void* shmadr);
参数:
shmadr:连接的共享内存的起始地址,即由shmat返回的指针
返回值:
成功:返回 0
失败:返回-1,错误值在error中
注意: 将共享内存段与当前进程脱离不等于删除共享内存段
共享内存中有一个映射链接数,进程调用shmat成功时该链接数值自动增加1。调用函数shmdt并不能删除共享内存,它仅仅删除共享内存在进程中的一个链接,并将该共享内存映射数减1 。
完成对共享内存的控制,包括删除共享内存、改变其状态等
int shmctl(int shmid, int cmd, struct shmid_ds * buf);
参数:
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
IPC_STAT:把shmid_ds结构中的数据设置为消息队列的当前关联值
IPC_SET:在进程有足够权限的前提下,把消息队列的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID:删除消息队列以及其上的所有数据
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:
成功:返回 0
失败:返回-1,错误值在error中
注意:
server.c
#include
#include
#include
#include
#include
#include
struct ipc{
int var;
char str[256];
};
void sys_err(const char* str)
{
perror(str);
exit(1);
}
int main()
{
struct ipc t;
int tmp;
int ret = shmget(1234, sizeof(t), IPC_CREAT | 0644); // 1、创建共享内存
if(ret == -1)
sys_err("shmget error");
printf("shared memory mid=%d\n", ret);
struct ipc* p = (struct ipc*)shmat(ret, NULL, 0); // 2、将共享内存绑定到进程内存
空间
if( p == (void *)-1)
sys_err("shmat error");
int i = 0;
while(i < 5) // 向共享内存中写消息
{
p->var = i+1;
sprintf(p->str, "hello shmat, this is %d", i+1);
i++;
sleep(1);
}
tmp = shmdt(p); // 3、断开共享内存连接
if(tmp == -1)
sys_err("shmdt error");
printf("已断开连接\n");
sleep(2);
tmp = shmctl(ret, IPC_RMID, NULL); // 4、销毁共享内存
if(tmp == -1)
sys_err("shmtl error");
printf("共享内存已销\n");
return 0;
}
client.c
#include
#include
#include
#include
#include
#include
#include
#include
struct ipc{
int var;
char str[256];
};
void sys_err(const char* str)
{
perror(str);
exit(1);
}
int main()
{
struct ipc tmp;
int ret = shmget(1234, sizeof(tmp), IPC_CREAT | 0644); // 打开共享内存
if(ret == -1)
sys_err("shmget error");
struct ipc* p = (struct ipc*)shmat(ret, NULL, 0); // 建立共享内存连接
if( p == (void *)-1)
sys_err("shmat error");
int i=0;
while(1) // 从共享内存中读取消息
{
printf("%d, %s\n", p->var, p->str);
sleep(1);
i++;
}
ret = shmdt(p); // 断开共享内存连接
if(ret == -1)
sys_err("shmdt error");
printf("已断开连接\n");
return 0;
}
分析当server运行结束时,对共享内存进行了删除,但是由于client内是死循环,所以仍然和共享内存具有连接,此时该共享内存并不会被立即从系统中删除,而是被设置为IPC_PRIVATE状态,并被标记为"已被删除"。此时查看ipc仍然存在。将client终止,再查看ipc: