【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet 或 .../gentleliu,文章仅供学习交流,请勿用于商业用途】
前面讲到socket的进程间通信方式。这样的方式在进程间传递数据时首先须要从进程1地址空间中把数据复制到内核,内核再将数据复制到进程2的地址空间中,也就是数据传递须要经过内核传递。这样在处理较多数据时效率不是非常高。而让多个进程共享一片内存区则攻克了之前socket进程通信的问题。共享内存是最快的进程间通信 。将一片内存映射到多个进程地址空间中,那么进程间的数据传递将不在涉及内核。
共享内存并非从某一进程拥有的内存中划分出来的。进程的内存总是私有的。共享内存是从系统的空暇内存池中分配的,希望訪问它的每一个进程连接它。这个连接过程称为映射。它给共享内存段分配每一个进程的地址空间中的本地地址。
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后。进程能够向訪问普通内存一样对文件进行訪问。不必再调用read(),write()等操作。
len是映射到调用进程地址空间的字节数,它 从被映射文件开头offset个字节開始算起,offset通常设置为0;
prot 參数指定共享内存的訪问权限。可取例如以下几个值的或:PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可运行), PROT_NONE(不可訪问)。该值常设置为PROT_READ | PROT_WRITE 。
flags由下面几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,当中,MAP_SHARED(变动是共享的,对共享内存的改动全部进程可见) , MAP_PRIVATE(变动是私有的。对共享内存改动仅仅对该进程可见) 必选其一,而MAP_FIXED则不推荐使用 。
munmp() 删除地址映射关系。函数原型例如以下:
进程在映射空间的对共享内容的改变并不直接写回到磁盘文件里。往往在调用munmap()后才运行该操作。能够通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。 msync()函数原型为:
在使用open函数打开一个文件之后调用mmap把文件内容映射到调用进程的地址空间,这样我们操作文件内容仅仅须要对映射的地址空间进行操作。而无需再使用open。write等函数。
使用共享内存的步骤基本是:
open()创建内存段;
用 ftruncate()设置它的大小。
用mmap() 把它映射到进程内存,运行其它參与者须要的操作;
当使用完时,原来的进程调用 munmap()然后退出。
以下来看一个实现:
server程序创建内存并向共享内存写入数据:
前面讲到socket的进程间通信方式。这样的方式在进程间传递数据时首先须要从进程1地址空间中把数据复制到内核,内核再将数据复制到进程2的地址空间中,也就是数据传递须要经过内核传递。这样在处理较多数据时效率不是非常高。而让多个进程共享一片内存区则攻克了之前socket进程通信的问题。共享内存是最快的进程间通信 。将一片内存映射到多个进程地址空间中,那么进程间的数据传递将不在涉及内核。
共享内存并非从某一进程拥有的内存中划分出来的。进程的内存总是私有的。共享内存是从系统的空暇内存池中分配的,希望訪问它的每一个进程连接它。这个连接过程称为映射。它给共享内存段分配每一个进程的地址空间中的本地地址。
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后。进程能够向訪问普通内存一样对文件进行訪问。不必再调用read(),write()等操作。
函数原型为:
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);当中參数addr为描写叙述符fd应该被映射到进程空间的起始地址,当指定为NULL时内核将自己去选择起始地址,不管addr是为NULL,函数返回值都是fd所映射到内存的起始地址;
len是映射到调用进程地址空间的字节数,它 从被映射文件开头offset个字节開始算起,offset通常设置为0;
prot 參数指定共享内存的訪问权限。可取例如以下几个值的或:PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可运行), PROT_NONE(不可訪问)。该值常设置为PROT_READ | PROT_WRITE 。
flags由下面几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,当中,MAP_SHARED(变动是共享的,对共享内存的改动全部进程可见) , MAP_PRIVATE(变动是私有的。对共享内存改动仅仅对该进程可见) 必选其一,而MAP_FIXED则不推荐使用 。
munmp() 删除地址映射关系。函数原型例如以下:
#include <sys/mman.h> int munmap(void *addr, size_t length);參数addr是由mmap返回的地址。len是映射区大小。
进程在映射空间的对共享内容的改变并不直接写回到磁盘文件里。往往在调用munmap()后才运行该操作。能够通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。 msync()函数原型为:
#include <sys/mman.h> int msync(void *addr, size_t length, int flags);參数addr和len代表内存区,flags有下面值指定,MS_ASYNC(运行异步写), MS_SYNC(运行同步写),MS_INVALIDATE(使快速缓存失效)。当中MS_ASYNC和MS_SYNC两个值必须且仅仅能指定一个,一旦写操作排入内核。MS_ASYNC马上返回,MS_SYNC要等到写操作完毕后才返回。假设还指定了MS_INVALIDATE,那么与其终于拷贝不一致的文件数据的全部内存中拷贝都失效。
在使用open函数打开一个文件之后调用mmap把文件内容映射到调用进程的地址空间,这样我们操作文件内容仅仅须要对映射的地址空间进行操作。而无需再使用open。write等函数。
使用共享内存的步骤基本是:
open()创建内存段;
用 ftruncate()设置它的大小。
用mmap() 把它映射到进程内存,运行其它參与者须要的操作;
当使用完时,原来的进程调用 munmap()然后退出。
以下来看一个实现:
server程序创建内存并向共享内存写入数据:
int sln_shm_get(char *shm_file, void **shm, int mem_len) { int fd; fd = open(shm_file, O_RDWR | O_CREAT, 0644);//1. 创建内存段 if (fd < 0) { printf("open <%s> failed: %s ", shm_file, strerror(errno)); return -1; } ftruncate(fd, mem_len);//2.设置共享内存大小 *shm = mmap(NULL, mem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //mmap映射系统内存池到进程内存 if (MAP_FAILED == *shm) { printf("mmap: %s ", strerror(errno)); return -1; } close(fd); return 0; } int main(int argc, const char *argv[]) { char *shm_buf = NULL; sln_shm_get(SHM_IPC_FILENAME, (void **)&shm_buf, SHM_IPC_MAX_LEN); snprintf(shm_buf, SHM_IPC_MAX_LEN, "hello share memory ipc! i'm server."); return 0; }client程序映射共享内存并读取当中数据:
int main(int argc, const char *argv[]) { char *shm_buf = NULL; sln_shm_get(SHM_IPC_FILENAME, (void **)&shm_buf, SHM_IPC_MAX_LEN); printf("ipc client get: %s ", shm_buf); munmap(shm_buf, SHM_IPC_MAX_LEN); return 0; }先执行server程序向共享内存写入数据,再执行客户程序。执行结果例如以下:
# ./server # ./client ipc client get: hello share memory ipc! i'm server. #共享内存不像socket那样本身具有同步机制,它须要通过添加其它同步操作来实现同步,比方信号量等。同步相关操作在后面会有相关专栏具体叙述。
本节源代码下载:
http://download.csdn.net/detail/gentleliu/8140487
最后今天祝各位单身程序员节日快乐!。!!!