POSIX本质上就是 mmap 对文件的共享方式映射,只不过映射的是 tmpfs 文件系统上的文件。
POSIX Api简介
POSIX本质上就是 mmap 对文件的共享方式映射,只不过映射的是 tmpfs 文件系统上的文件。tmpfs是Linux提供的一种“临时”的文件系统,它可以将内存的一部分空间拿来当做文件系统使用,使内存空间可以当做目录文件来用。Linux提供的POSIX共享内存,实际上就是在/dev/shm下创建一个文件,并将其mmap之后映射其内存地址即可。
mmap系列函数简介
mmap函数主要的功能就是将文件或设备映射到调用进程的地址空间中,当使用mmap映射文件到进程后,就可以直接操作这段虚拟地址进行文件的读写等操作,不必再调用read,write等系统调用。在很大程度上提高了系统的效率和代码的简洁性。
mmap函数主要的作用
- 对普通文件提供内存映射I/O,可以提供无亲缘进程间的通信;
- 提供匿名内存映射,以供亲缘进程间进行通信。
- 对shm_open创建的POSIX共享内存区对象进程内存映射,以供无亲缘进程间进行通信。
mmap函数主要的API
mmap 映射内存
mmap成功后,返回值即为fd映射到内存区的起始地址,之后可以关闭fd,一般也是这么做的,这对该内存映射没有任何影响。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
void *mmap(void *start, size_t len, int prot, int flags, int fd, off_t offset);
|
munmap删除映射
1 2 3 4 5
|
int munmap(void *start, size_t len);
|
msync实时同步
对于一个MAP_SHARED的内存映射区,内核的虚拟内存算法会保持内存映射文件和内存映射区的同步,也就是说,对于内存映射文件所对应内存映射区的修改,内核会在稍后的某个时刻更新该内存映射文件。如果我们希望硬盘上的文件内容和内存映射区中的内容实时一致,那么我们就可以调用msync开执行这种同步:
1 2 3 4 5 6 7 8 9
|
int msync(void *start, size_t len, int flags);
|
mmap实现线程中通信
通过匿名内存映射提供亲缘进程间的通信
我们可以通过在父进程fork之前指定MAP_SHARED调用mmap,通过映射一个文件来实现父子进程间的通信,POSIX保证了父进程的内存映射关系保留到子进程中,父子进程对内存映射区的修改双方都可以看到。
在Linux 2.4以后,mmap提供匿名内存映射机制,即将mmap的flags参数指定为:MAP_SHARED | MAP_ANON。这样就彻底避免了内存映射文件的创建和打开,简化了对文件的操作。匿名内存映射机制的目的就是为了提供一个穿越父子进程间的内存映射区,很方便的提供了亲缘进程间的通信。
简化测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int main(int argc, char **argv) { int *memPtr; memPtr = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, 0, 0); if (memPtr == MAP_FAILED) return -1; *memPtr = 0; if (fork() == 0) { *memPtr = 1; cout<<"child:set memory "<<*memPtr<<endl; exit(0); } sleep(1); cout<<"parent:memory value "<<*memPtr<<endl; return 0; }
|
通过内存映射文件提供无亲缘进程间的通信
通过在不同进程间对同一内存映射文件进行映射,来进行无亲缘进程间的通信。
简化测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| int main() { int *memPtr; int fd; fd = open(PATH_NAME, O_RDWR | O_CREAT, 0666); if (fd < 0) { return -1; } ftruncate(fd, sizeof(int)); memPtr = (int *)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (memPtr == MAP_FAILED) { cout<<"mmap failed..."<<strerror(errno)<<endl; return -1; } *memPtr = 111; cout<<"process:"<<getpid()<<" send:"<<*memPtr<<endl; return 0; }
int main() { int *memPtr; int fd; fd = open(PATH_NAME, O_RDWR | O_CREAT, 0666); if (fd < 0) { return -1; } memPtr = (int *)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (memPtr == MAP_FAILED) { cout<<"mmap failed..."<<strerror(errno)<<endl; return -1; } cout<<"process:"<<getpid()<<" receive:"<<*memPtr<<endl; return 0; }
|
基于mmap的POSIX共享内存
具体步骤
- 通过shm_open创建或打开一个POSIX共享内存对象
- 然后调用mmap将它映射到当前进程的地址空间
POSIX共享内存底层支撑对象
内存映射文件(memory-mapped file)
由open函数打开,由mmap函数把所得到的描述符映射到当前进程空间地址中的一个文件。共享的数据载体是物理文件。
主流:共享内存区对象(shared-memory object)
由shm_open函数打开一个Posix.1 IPC名字,所返回的描述符由mmap函数映射到当前进程的地址空间。共享的数据载体是物理内存。
共享内存区对象API
shm_open打开共享内存区
shm_open用于创建一个新的共享内存区对象或打开一个已经存在的共享内存区对象。
1 2 3 4 5 6 7 8
|
int shm_open(const char *name, int oflag, mode_t mode);
|
shm_unlink删除共享内存对象
shm_unlink用于删除一个共享内存区对象,跟其他文件的unlink以及其他POSIX IPC的删除操作一样,对象的析构会到对该对象的所有引用全部关闭才会发生。
1
| int shm_unlink(const char *name);
|
代码简单测试实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| #define SHM_NAME "/memmap" #define SHM_NAME_SEM "/memmap_sem" char sharedMem[10]; int main() { int fd; sem_t *sem; fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0666); sem = sem_open(SHM_NAME_SEM, O_CREAT, 0666, 0); if (fd < 0 || sem == SEM_FAILED) { cout<<"shm_open or sem_open failed..."; cout<<strerror(errno)<<endl; return -1; } ftruncate(fd, sizeof(sharedMem)); char *memPtr; memPtr = (char *)mmap(NULL, sizeof(sharedMem), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); char msg[] = "yuki..."; memmove(memPtr, msg, sizeof(msg)); cout<<"process:"<<getpid()<<" send:"<<memPtr<<endl; sem_post(sem); sem_close(sem); return 0; }
int main() { int fd; sem_t *sem; fd = shm_open(SHM_NAME, O_RDWR, 0); sem = sem_open(SHM_NAME_SEM, 0); if (fd < 0 || sem == SEM_FAILED) { cout<<"shm_open or sem_open failed..."; cout<<strerror(errno)<<endl; return -1; } struct stat fileStat; fstat(fd, &fileStat); char *memPtr; memPtr = (char *)mmap(NULL, fileStat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); sem_wait(sem); cout<<"process:"<<getpid()<<" recv:"<<memPtr<<endl; sem_close(sem); return 0; }
|