赞
踩
共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,它是在多个进程之间对内存段进行映射的方式实现内存共享的。这是IPC最快捷的方式,因为共享内存方式的通信没有中间过程,而管道、消息队列等方式则是需要将数据通过中间机制进行转换;与此相反,共享内存方式直接将某段内存段进行映射,多个进程间的共享内存是同一块的物理空间,仅仅是地址不同而已,因此不需要进行复制,可以直接使用此段空间。
进程间数据交换的实现方式包括三类
1、通过文件,比如命名管道文件,相互通信的进程通过访问同一磁盘文件实现数据交换。存在read、write等系统调用,效率低下,速度慢、
2、通过内核,比如消息队列,需要进行用户、内核的内存拷贝,开销大,效率低、速度慢
3、通过共享内存,读写速度快,无需数据拷贝、系统调用等,效率高
1、共享内存为相互的通信的进程提供了同一块物理内存空间,每个进程都可以直接访问,不需要用户内核之间的数据拷贝或者系统调用,大大提高了通信的速度,是最快的进程间通信方式
2、共享内存并未提供同步机制,多进程竞争同个共享资源会造成数据混乱
3、共享内存生命周期是随内核的,不是随进程的,程序员必须释放。
每个进程都有独立的虚拟空间地址,通过MMU地址转换将虚拟地址与物理地址进行映射,每个进程虚拟地址空间都会映射到不同的物理地址,每个进程在物理内存空间都是相互独立和隔离的。共享内存通过分配一块共享的物理空间,将其挂接到相互通信息的进程虚拟地址空间中,实现虚拟地址到共享物理内存的映射。
结构shmid_ds结构定义如下:
struct shmid ds{
struct ipc_perm shm_perm; /*所有者和权限*/
size_t shm_segsz; /*段大小,以字节为单位*/
time t shm_atime; /*最后挂接时间*/
time_t shm_dtime; /*最后取出时间*/
time_t shm_ctime; /*最后修改时间*/
pid_t shm_cpid; /*建立者的PID */
pid_t shm_lpid; /*最后调用函数 shmat ()/shmdt ()*/
shmatt_t shm_nattch; /*现在挂接的数量*/
}
system V提供的IPC机制包括消息队列,信号量和共享内存3种。使用IPC前必须先创建,每种IPC都有特定的生产者、所有者和访问权限。
pcs命令可以查看当前系统正在使用的IPC工具。一个IPC工具至少包含key值、ID值、拥有者、权限和使用的大小等关键信息。如果需要手工删除某个IPC机制,可以使用ipcrm命令。
//获取IPC键值 用于区分不同的ipc
//key_t key 用来进行进程间通信的,让不同进程能看到同一份资源
key_t ftok(const char *pathname, int proj_id);
参数 pathname:为文件路径,或目录,一般是当前目录。
参数 id:为一个整形变量,是子序号,参与构成ftok()函数的返回值。虽然是int类型,但是只使用8bits(1-255)。
在ftok()函数创建key值过程中使用了该文件属性的st_dev 和st_ino。
key值构成:
key值的第31-24(共8位)为ftok()第二个参数的低8位。//第二个参数的用处在这里。
key值的第23-16(共8位)为该文件的st_dev属性的第8位。
key值的第15-0为该文件的st_ino属性的低16位。
函数shmget()用于创建一个新的共享内存段,或者访问一个现有的共享内存段,它与消息队列以及信号量集合对应的函数十分相似。函数shmget()的原型如下:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
/*执行成功返回值为shmid 共享内存的标识符ID
key:只是用来在系统层面进行标识唯一性的,不能用来管理shm。
shmid:是OS给用户返回的ID,用来在用户层进行shm的管理。
*/
shmget()的第一个参数是关键字的值。然后,这个值将与内核中现有的其他共享内存段的关键字值相比较。在比较之后,打开和访问操作都将依赖于shmflg参数的内容。size参数为共享内存大小,一般为存物理页的整数倍。
IPC_CREAT:如果在内核中不存在该内存段,则创建它。
IPC_EXCL:当与IPC_CREAT一起使用时,如果该内存段早已存在,则此次调用将失败。
如果只使用IPC_CREAT, shmget()或者将返回新创建的内存段的段标识符,或者返回早已存在于内核中的具有相同关键字值的内存段的标识符。如果同时使用IPC_CREAT和IPC_EXCL,则可能会有两种结果:如果该内存段不存在,则将创建一个新的内存段;如果内存段早已存在,则此次调用失败,并将返回-1。IPC_EXCL本身是没有什么用处的,但在与IPC_CREAT组合使用时,它可用于防止一个现有的内存段为了访问而打开着。旦进程获得了给定内存段的合法IPC标识符,它的下一步操作就是连接该内存段,或者把该内存段映射到自己的寻址空间中。
函数shmat()用来获取共享内存的地址,获取共享内存成功后,可以像使用通用内存一样对其进行读写操作。函数的原型如下:
#include <sys/types.h>
#include <sys/shm.h>
void *shmat (int shmid, const void *shmaddr, int shmflg);
/*返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1*/
如果shmaddr参数值等于0,则内核将试着查找一个未映射的区域。用户可以指定一个地址,但通常该地址只用于访问所拥有的硬件,或者解决与其他应用程序的冲突。
SHM_RND标志可以与标志参数进行OR操作,结果再置为标志参数,这样可以让传送的地址页对齐(舍入到最相近的页面大小)。此外,如果把SHM_RDONLY标志与标志参数进行OR操作,结果再置为标志参数,这样映射的共享内存段只能标记为只读方式。当申请成功时,对内存的操作与一般内存一样,可以直接进行写入和读出,以及偏移的操作。
IPC_REMAP:替换位于shmaddr处的任意既有映射:共享内存段或内存映射
函数shmdt()用于删除一段共享内存。函数的原型如下:
#include <sys/types.h>
#include <sys/shm.h>
int shmdt (const void *shmaddr);
当某进程不再需要一个共享内存段时,它必须调用这个函数来断开与该内存段的连接。这与从内核删除内存段是两回事!在成功完成了断开连接操作以后,相关的shmid_ds结构的shm_nattch成员的值将减去1。如果这个值减到0,则内核将真正删除该内存段。
共享内存的控制函数shmctl()的使用类似ioctl)的方式对共享内存进行操作:向共享内存的句柄发送命令,来完成某种功能。函数shmctl()的原型如下,其中shmid是共享内存的句柄,cmd是向共享内存发送的命令,最后一个参数buf则是向共享内存发送命令的参数。
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl (int shmid, int cmd, struct shmid_ds *buf);
此函数与消息队列的msgctlO调用是完全类似的,它的合法命令值是:
IPC_STT:获取内存段的shmid_ds结构,并把它存储在buf参数所指定的地址中。
IPC_SET 设置内存段shmid_ds结构的ipc_perm成员的值,此命令是从buf参数中获得该值的。
IPC_RMID:标记某内存段,以备删除。该命令并不真正地把内存段从内存中删除。相反,它只是标记上该内存段,以备将来删除。只有当前连接到该内存段的最后一个进程正确地断开了与它的连接,实际的删除操作才会发生。当然,如果当前没有进程与该内存段相连接,则删除将立刻发生。为了正确地断开与其共享内存段的连接,进程需要调用 shmdt()函数。
1、创建ipc系统唯一标识key -> ftok
2、创建共享内存 -> shmget
3、映射共享内存 -> shmat
4、共享内存读写
5、解除共享内存映射 -> shmdt
6、删除共享内存 -> shmctl
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <string.h> int main (void){ key_t key; /*系统唯一性标识ipc*/ int shmid; /*共享内存标识*/ char *shms; /* 共享内存挂接后的地址, */ struct shmid_ds shmbuf; pid_t p; /*进程号*/ key = ftok(".", 'a'); /*生成系统唯一标识ipc*/ if(key<0) { perror("ftok failed"); return 1; } shmid = shmget(key, 1024, IPC_CREAT|0600); /*获得共享内存,大小为1024个字节*/ if(shmid<0) { perror("shmget failed"); return 2; } shms = (char *)shmat(shmid, NULL, 0); /*挂接共享内存*/ printf("shmat success!\n"); while(strlen(shms)==0){ sleep(1); printf("%s\n",shms); } shmdt(shms); /*摘除共享内存*/ printf("shmdt success!\n"); shmctl(shmid,IPC_RMID,&shmbuf); /*删除共享内存*/ return 0; }
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <string.h> static char msg[]="你好,共享内存!\n"; int main (void){ key_t key; /*系统唯一性标识ipc*/ int shmid; /*共享内存标识*/ char *shmc; /* 共享内存挂接后的地址, */ struct shmid_ds shmbuf; pid_t p; /*进程号*/ key = ftok(".", 'a'); /*生成系统唯一标识ipc*/ if(key<0) { perror("ftok failed"); return 1; } shmid = shmget(key, 1024, IPC_CREAT|0600); /*获得共享内存,大小为1024个字节*/ if(shmid<0) { perror("shmget failed\n"); return 2; } shmc = (char *)shmat(shmid, NULL, 0); /*挂接共享内存*/ printf("shmat success!"); memcpy(shmc, msg, strlen(msg) +1); /*复制内容到共享内存*/ shmdt(shmc); /*摘除共享内存*/ printf("shmdt success!\n"); shmctl(shmid,IPC_RMID,&shmbuf); /*删除共享内存*/ return 0; }
all:client server
client: client.o
gcc -o client client.o
server: server.o
gcc -o server server.o
clean:
rm -rf server client *.o
先运行server
之后检查是否创建共享内存
运行client
检查共享内存是否被删除
传送门
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。