Posix共享内存区基础知识

来源:互联网 发布:帝国cms验证权限 编辑:程序博客网 时间:2024/05/17 14:28

1. 基本信息

共享内存区是可用IPC中最快的,一旦其映射到共享它的进程的地址空间,进程间的数据场地就不再涉及内核(无需系统调用传递数据)。

普通的客户-服务程序涉及的步骤:

服务端读文件—》服务端写入IPC—》客户端读IPC—》客户端写入文件

而使用共享内存:

服务端将文件读入共享内存对象—》客户端从共享对象写入文件。


2. mmap、munmap和msync函数

#include <sys/mman.h>void *mmap(void *addr, size_t len, int prot,int flags, int fd, off_t offset); //成功返回映射区起始地址,失败返回MAP_FAILED

addr    指定起始地址,一般置为NULL,内核自己选择起始地址;

len       指定映射到调用进程空间的字节数,其从距离文件头offset个字节数开始,一般设为0.

prot     内存映射区的保护由此参数指定,该参数的常见值是代表读写访问的PROT_READ| PROT_WRITE

            PROT_READ      数据可读

            PROT_WRITE    数据可写

            PROT_EXEC      数据可执行

            PROT_NONE     数据不可访问

flags   常值指定,MAP_SHARED 或 MAP_PRIVATE这两个必须指定一个

            MAP_SHARED   变动是共享的

            MAP_PRIVATE   变动是私有的

            MAP_FIXED        准确的解释addr参数,可移植程序不应指定

该函数把一个文件或posix共享内存区对象映射到调用进程的地址空间。主要有三个目的:

A. 使用普通文件以提供内存映射I/O

B. 是用特殊文件以提供匿名内存映射

C. 是用shm_open以提供无亲缘关系进程间的Posix共享内存区。

父子进程之间共享内存区的方式是在fork之前指定MAP_SHARED调用mmap。一旦mmap成功返回,fd参数可以被关闭,该操作对于mmap建立的映射关系没有影响。


#include <sys/mman.h>int munmap(void *addr, size_t len);
addr    由mmap返回的地址

len      映射区的大小

调用该函数后后再使用这些地址会返回SIGSEGV信号,如果映射区是MAP_PRIVATE标志映射的,若指定MAP_SHARED则调用进程对它所做的变动都会被丢弃。一般内核的虚拟内存算法(硬盘上)与内存映射区(内存中)的同步(有一定同步延迟),若要保证同步需要调用msync函数。


#include <sys/mman.h>int msync(void *addr, size_t len, int flags)   //成功0,出错返回-1
addr & len 指代整个映射区,不过也可以指定该内存的一个子集

flags 常用以下值,必须指定前两个中的一个,但不可同时指定

          MS_ASYNC    执行异步写,等待写操作完成

          MS_SYNC      执行同步写,操作由内核排入队列立即返回

          MS_INVALIDATE   使用高速缓存的数据失效,与副本不一直的内存数据都将被丢弃

不是所有的文件都能进行内存映射,比如终端或套接字描述符会返回一个错误。

struct shared{sem_t mutex;int   count;}shared;int main(int argc, char **argv){int fd , i, nloop;struct shared *ptr;nloop = atoi(argv[2]);fd = open(argv[1],O_RDWR);write(fd, &shared,sizeof(struct shared));ptr = mmap(NULL,sizeof(struct shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);close(fd);sem_init(&ptr->mutex, 1, 1);setbuf(stdout, NULL);if(fork() == 0){for(i == 0; i < nloop; i++){sem_wait(&ptr->mutex);printf("child: %d\n, ptr->count++);sem_post(&ptr->mutex);}exit(0);}for( i == 0; i < nloop; i++){sem_wait(&ptr->mutex);printf("parent: %d\n",ptr->count++);sem_post(&ptr->mutex);}exit(0);}


3. 匿名映射

上面的例子中我们需要先open文件(不存在就在文件系统中创建它,然后写一些0以初始化文件。但是,如果是父子进程则可以简化策略。

(1)对于BSD设备,提供"匿名内存映射“,其办法是把mmap的flags参数指定成MAP_SHARED|MAP_ANON,把fd指定为-1,而flag参数被忽略,这样的内存被初始化为0.

int main(int argc, char* argv[]){     int  i,nloop;     int  *ptr;     sem_t  *mutex;     nloop = atoi(argv[1]);     ptr = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);}

(2)SVR4提供/dev/zero设备文件,我们open它之后就可以在mmap中使用得到的描述符,从该设备读时返回的字节全为0,写往该设备的任何直接则被丢弃。

int main(int argc, char* argv[]){     int  i,nloop;     int  *ptr;     sem_t  *mutex;     nloop = atoi(argv[1]);     fd = open("/dev/zero", O_RDWR);     ptr = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);close(fd);}


但我们使用文件的方式映射一段内存时,内核的内存保护实际上是以页为单位,假设页大小为4096.若文件大小为5000,而映射区为5000,则我们可以读写0到8191字节的数据,但超出该范围就会有segment fault。但是如果我们映射区为15000,则读写8192到14999的部分会引发SIGBUS错误,超出部分SIGSEGV部分。

5. Posix共享内存区

Posix提供了2种在无亲缘关系的进程间共享的内存区的办法:

A. 内存映射文件:open打开,mmap将描述符fd映射到当前进程地址空间(上面提到的第一种方式)

B. 共享内存区对象:由shm_open打开一个IPC名字,将返回的描述符映射到当前进程的地址空间


Posix共享内存区涉及到两个步骤:

首先,使用shm_open打开一个已有或创建一个新的共享内存区对象;然后,使用mmap将其映射到进程的地址空间。传递给shm_open的名字参数可以由希望共享该内存区的任何进程使用。

#include <sys/mman.h>int shm_open(const char *name, int oflag, mode_t mode);//成功返回描述符,失败返回-1int shm_unlink(const char* name);//成功返回描述符,失败返回-1

name  必须以/ 开头

oflag    必须含有O_RDONLY 和 O_RDWR,还可以指定O_CREAT、O_EXCL(若对象已存在,返回EEXIST错误)、O_TRUNC

mode  S_IWUSR\S_IRUSR\S_IWGRP\S_IRGRP\S_IWOTH\S_IROTH

这里说明一下shm_unlink,该函数仅仅是删除一个共享内存对象的名字,防止后续的open操作,但不会影响对于其底层支撑对象的现有引用,知道该对象的引用全部关闭为止,这是大部分unlink参数的作用。


shm_open默认打开的对象大小为0,我们可以通过ftruncate函数调整普通文件和共享内存区对象的大小:

#include<unistd.h>int ftruncate(int fd, off_t length);
对于普通文件,若其长度大于length,则丢弃大于length部分,若长度小于length,则会将文件扩大至length大小,但一般来说扩展文件采用另一种策略:lseek到指定位置,在length-1处写一个字节;

对于一个共享内存区对象:ftruncate将对象设置为length指定大小,在调整大小时并不能保证被初始化为0,尽管标准是这样定义的。

我们还可以同过fstat查看共享内存区对象的信息:

#include <sys/type.h>#include <sys/stat.h>int fstat(int fd, struct stat *buf);
简单示例:
struct shmstruct{int count;};sem_t *sem;int main(int argc, char* argv[]){int fd;struct shmstruct *ptr;shm_unlink(argv[1]);fd = shm_open(argv[1], O_RDWR | O_CREAT | O_EXCL ,FILE_MODE);ftruncate(fd,sizeof(struct shmstruct));ptr = mmap(NULL,sizeof(struct shmstruct), PROT_READ|PROT_WRITE, MAP_SHARED, fd ,0);close(fd);sem_unlink(argv[2]);mutex = sem_open(argv[2]);sem_close(mutex);return 0;}




0 0
原创粉丝点击