共享内存区是可用IPC形式中最快的。一旦内存区映射到共享它的进程的地址空间,进程间数据的传递就不再涉及内核。然而往该共享内存区存放信息或从中取走信息的进程间通常需要某种形式的同步。不再涉及内核是指:进程不再通过执行任何进入内核的系统调用来彼此传递数据。内核必须建立允许各个进程共享该内存区的内存映射关系,然后一直管理内存区。
默认情况下通过fork派生的子进程并不与其父进程共享内存区。
mmap函数把一个文件或一个Posix共享内存区对象映射到调用进程的地址空间。使用该函数的目的有:
1、使用普通文件以提供内存映射I/O。
2、使用特殊文件以提供匿名内存映射。
3、使用shm_open以提供无亲缘关系进程间的Posix共享内存区。
#include <sys/mman.h> void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
addr指定描述符fd应被映射到的进程内空间的起始地址。若为空指针,则由内核自己去选择起始地址。无论哪种情况,该函数的返回值都是描述符fd所映射到内存区的起始地址。
len是映射到调用进程地址空间中的字节数,它从被映射文件开头起第offset个字节处开始算。offset通常设置为0。
prot参数指定内存区映射区的保护。有以下四种:PROT_READ(数据可读)、PROT_WRITE(数据可写)、PROT_EXEC(数据可执行)、PROT_NONE(数据不可访问)。
flags有以下三种:MAP_SHARED(变动是共享的)、MAP_PRIVATE(变动是私有的)、MAP_FIXED(准确地解释addr参数)。
父子进程之间共享内存区的方法之一是,父进程在调用fork前先指定MAP_SHARED调用mmap。
mmap成功返回后,fd参数可以关闭。该操作对由mmap建立的映射关系没有影响。
为某个进程的地址空间删除一个映射关系,我们调用munmap。(int munmap(void *addr, size_t len))
调用msync来执行同步。(int msync(void *addr, size_t len, int flags))
注意:不是所有文件都能进程内存映射,例如:试图把一个访问终端或套接字的描述符映射到内存将导致mmap返回一个错误。这些类型的描述符必须使用read和write来访问。mmap的另一个用途是在无亲缘关系的进程间提供共享的内存区。
程序实例:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/mman.h> #define SEM_NAME "mysem" #define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) int main(int argc, char **argv) { int fd, i, nloop, zero = 0; int *ptr; sem_t *mutex; if(argc != 3){ printf("usage:incrl <pathname> <#nloops>\n"); return -1; } nloop = atoi(argv[2]); /*open file , initialize to 0, map into memory*/ fd = open(argv[1], O_RDWR | O_CREAT, FILE_MODE); write(fd, &zero, sizeof(int)); ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); /*create, initialize, and unlink semaphore*/ mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1); if(mutex < 0){ printf("sem_open error.\n"); return -1; } sem_unlink(SEM_NAME); setbuf(stdout, NULL); /*stdout is unbuffered*/ if(fork() == 0){ for(i = 0; i < nloop; i++){ sem_wait(mutex); printf("child:%d\n", (*ptr)++); sem_post(mutex); } exit(0); } /*parent*/ for(i = 0; i < nloop; i++){ sem_wait(mutex); printf("parent:%d\n", (*ptr)++); sem_post(mutex); } exit(0); }注意: 内存映射一个普通文件时,内存中映射区的大小通常等于该文件的大小。然而文件大小和内存映射区大小可以不同。