下面我们先来通过一段程序来 初步了解一下如何使用共享内存实现进程间通信
首先编写公共的头文件comm.h
#ifndef _COMM_
#define _COMM_
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#define PATHNAME "."
#define PROJ_ID 0x666
int create_shm(int size);
int get_shm();
int destroy_shm(int shmid);
#endif
接下来是接口实现的源文件
#include"comm.h"
static int comm_shm(int size, int flags)
{
key_t key = ftok(PATHNAME, PROJ_ID);
if(key < 0)
{
perror("ftok");
return -1;
}
int shmid = shmget(key,size,flags);//共享内存创建函数
if(shmid < 0)
{
perror("shmget");
return -2;
}
return shmid;
}
int create_shm(int size)
{
return comm_shm(size,IPC_CREAT | IPC_EXCL);
}
int get_shm()
{
return comm_shm(0,IPC_CREAT);
}
int destroy_shm(int shmid)
{
if(shmctl(shmid,IPC_RMID,NULL) < 0)//共享内存销毁函数
{
perror("shmctl");
return -1;
}
return 0;
}
接下来是交流的双方进程首先是开辟共享内存并且写入的server.c
#include"comm.h"
int main()
{
int shmid = create_shm(4096);
char *buf = shmat(shmid,NULL,0);//共享内存区对象映射到调用进程的地址空间
int count = 0;
while(count < 4096)
{
buf[count] = 'A'+count%26;
count++;
buf[count] = 0;
sleep(1);
}
shmdt(buf);//解除共享内存区对象到调用进程的地址空间的映射
destroy_shm(shmid);
return 0;
}
接下来接受的文件client.c
#include"comm.h"
int main()
{
int shmid = get_shm();
char *buf = shmat(shmid,NULL,0);//共享内存区对象映射到调用进程的地址空间
while(1)
{
printf("%s\n",buf);
sleep(1);
}
shmdt(buf);//解除共享内存区对象到调用进程的地址空间的映射
return 0;
}
这里我们要注意的是共享内存的生命周期是随内核的,如果程序的销毁函数无法正常执行时我们需要手动的删除 ipcs -m:查看系统中的共享内存信息,我们可以找到我们程序所创建的共享内存采用ipcrm -m shmid ,就是通过他的shmid销毁它。
好了一起还看看程序的结果吧
接下来我们了解一下具体函数的信息
int shmget(key_t key, size_t size, int shmflg);
第一个参数key还是和之前一样通过ftok()获取。
第二个参数是我们需要获取的内存的大小:所有的内存分配操作都是以页为单位的。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE=4096字节)这样,新创建的共享内存的大小实际上是从size这个参数调整而来的页面大小。即如果size为1至4096,则实际申请到的共享内存大小为4K(一页);4097到8192,则实际申请到的共享内存大小为8K(两页),依此类推。
shmflg参数:这个参数可以选择的值IPC_CREAT和IPC_EXCL,单独使用 IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则打开操作。 IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。 如果单独使用IPC_CREAT,shmget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。如果将IPC_CREAT和IPC_EXCL标志一起使用,shmget()将返回一个新建的共享内存的标识符;如果该共享内存已存在,或者返回-1。IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。
int shmctl( int shmid , int cmd , struct shmid_ds *buf );
shmid 共享内存标识ID;
cmd IPC_STAT 得到共享内存的状态
IPC_SET 改变共享内存的状态
IPC_RMID 删除共享内存
buf 是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定;
void *shmat( int shmid , void *shmaddr , int shmflag );
shmat()是用来允许本进程访问一块共享内存的函数。
int shmid是那块共享内存的ID。
char *shmaddr是共享内存的起始地址一般有操作系统自己决定所以设置为NULL,如果 shmaddr 是NULL,系统将自动选择一个合适的地址。
int shmflag是本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。其它的是读写模式成功时,这个函数返回共享内存的起始地址。失败时返回-1最近用到内存共享。