Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15496883
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类:

2009-09-28 11:01:16

Why Use Memory Map ?

1. 内存映射以页面为单位,将文件内容映射到内存中。
2. 使用内存映射可以创建内存映射文件。内存映射文件的优点是我们不需要调用 read 、write 之类的I/O函数,只需用从内存映射区取、存数据,实际的 I/O 操作是在背后由内核执行的。很多时候,这样做可以简化我们的代码。当然并不是所有的文件都可以被映射,比如终端和套接字文件就不能被映射。

3. 使用内存映射可以实现进程间共享内存。(以 MAP_SHARED 调用 mmap


Functions

#include 

void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off );
                                      Returns: starting address of mapped region if OK, MAP_FAILED on error

说明:
1. 参数 addr 指明 描述字 filedes 指定的文件在进程地址空间内的映射区的开始地址,必须是页面对齐的地址,通常设为 NULL ,让内核去选择开始地址。任何情况下,mmap 的返回值为内存映射区的开始地址。
2. 参数 len 指明 文件需要被映射的字节长度。off 指明文件的偏移量。通常 off 设为 0 。
    ※ 如果 len 不是页面的倍数,它将被扩大为页面的倍数。扩充的部分通常被系统置为 0 ,而且对其修改并不影响到文件。
    ※ off 同样必须是页面的倍数。通过 sysconf(_SC_PAGE_SIZE)可以获得页面的。大小。
3. 参数 prot 指明映射区的保护权限。通常有以下 4 种。通常是 PROT_READ | PROT_WRITE 。
    PROT_READ        可读
    PROT_WRITE       可写
    PROT_EXEC        可执行
    PROT_NONE       不能被访问

4. 参数 flag 指明映射区的属性。取值有以下几种。MAP_PRIVATE 与 MAP_SHARED 必选其一,MAP_FIXED 为可选项。
1)MAP_PRIVATE 指明对映射区数据的修改不会影响 真正的文件。
2)MAP_SHARED 指明对映射区数据的修改,多个共享该映射区的进程都可以看见,而且会反映到实际的文件。
3)MAP_FIXED 要求 mmap 的返回值必须等于 addr 。如果不指定 MAP_FIXED 并且 addr 不为 NULL ,则对 addr 的处理取决于具体实现。考虑到可移植性,addr 通常设为 NULL ,不指定 MAP_FIXED。

5. 当 mmap 成功返回时,filedes 就可以关闭,这并不影响创建的映射区。


#include 

int munmap(void *addr, size_t len);
                                                    Returns: 0 if OK, -1 on error

说明:
1. 进程退出的时候,映射区会自动删除。不过当不再需要映射区时,可以调用 munmap 显式删除。当映射区删除后,后续对映射区的引用会生成 SIGSEGV 信号。


#include 

int msync(void *addr, size_t len, int flags);

                                                           Returns: 0 if OK, -1 on error

说明:
1. 当映射区数据被修改时,内核会稍后将其更新到文件。但有时候为了确保修改能被反映到文件,可以调用 msync 函数来进行同步操作。
2. 参数 addrlen 通常引用整个映射区,当然也可以指定映射区的一部分。
3. 参数 flags 控制回写到文件的具体方式。MS_ASYNC 、MS_SYNC 必须指定其一。
1)MS_ASYNC ,只是将写操作排队,并不等待写操作完成就返回。
2)MS_SYNC ,等待写操作完成后才返回。
3)MS_INVALIDATE ,作废与实际文件内容不一致缓存页,有的实现则是作废整个映射区的缓存页。后续的引用将从文件获取数据。



我们知道,共享内存可以用在非相关(unrelated)进程间进行通信。而对于相关(related)进程间的通信,一些实现提供了基于内存映射的不同的技术。下面两种就是。

(SVR 4 ) /dev/zero Memory Mapping

1. 可以将伪设备 "/dev/zero" 作为参数传递给 mmap 而创建一个映射区。/dev/zero 的特殊在于,对于该设备文件所有的读操作都返回值为 0 的指定长度的字节流 ,任何写入的内容都被丢弃。我们的兴趣在于用它来创建映射区,用 /dev/zero 创建的映射区,其内容被初始为 0 。
2. 使用 /dev/zero 的优点在于,mmap创建映射区时,不需要一个时间存在的文件,伪文件 /dev/zero 就足够了。缺点是只能用在相关进程间。相对于相关进程间的通信,使用线程间通信效率要更高一些。不管使用那种技术,对共享数据的访问都需要进行同步。
3. 关于 /dev/zero ,请参考 /dev/null 与 /dev/zero文件 (zz)

(4.4 BSD) Anonymous Memory Mapping

1. 匿名内存映射 与 使用 /dev/zero 类型,都不需要真实的文件。要使用匿名映射之需要向 mmap 传入 MAP_ANON 标志,并且 fd 参数 置为 -1 。
2. 所谓匿名,指的是映射区并没有通过 fd 与 文件路径名相关联。匿名内存映射用在有血缘关系的进程间。
3. 匿名内存映射的创建如下所示:
if ((area = mmap(0, SIZE, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_SHARED, -1, 0)) == MAP_FAILED)
A Example From UNPv2

#include    "unpipc.h"

struct shared {
sem_t    mutex;        /* the mutex: a Posix memory-based semaphore */
int    count;        /* and the counter */
} shared;

int
main(int argc, char **argv)
{
    int        fd, i, nloop;
    struct shared     *ptr;

    if (argc != 3)
        err_quit("usage: incr3 <#loops>");
    nloop = atoi(argv[2]);

        /* 4open file, initialize to 0, map into memory */
    fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE);
    Write(fd, &shared, sizeof(struct shared));
    ptr = Mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE,
               MAP_SHARED, fd, 0);
    Close(fd);

        /* 4initialize semaphore that is shared between processes */
    Sem_init(&ptr->mutex, 1, 1);

    setbuf(stdout, NULL);    /* stdout is unbuffered */ (※)
    if (Fork() == 0) {        /* child */
        for (i = 0; i < nloop; i++) {
            Sem_wait(&ptr->mutex);
            printf("child: %d\n", ptr->count++);
            Sem_post(&ptr->mutex);
        }
        exit(0);
    }

        /* 4parent */
    for (i = 0; i < nloop; i++) {
        Sem_wait(&ptr->mutex);
        printf("parent: %d\n", ptr->count++);
        Sem_post(&ptr->mutex);
    }
    exit(0);
}

※ 代码中setbuf(stdout, NULL) 被解释为 是为了防止父子进程的输出相互影响,交错出现。
不过,为什么输出缓冲区设为 0 , 父子进程间的输出就不影响了?不是很理解。


Referencing Memory-Mapping Objects

1. 对于映射区各区段的引用,其结果如下所示:
       
|-------------------------------------------| (file size)

                                                     remainder of page
|-------------------------------------------|-------------------------------|-------------------------------------| (mmap size)
\<-                               reference OK                            ->\<-           SIGBUS                ->\<- SIGSEGV

由于内存映射是以 页 为单位进行的,因而我们可以访问比实际文件大小还有大的区域,如上图所示那样。

2. 内核会跟踪所映射的文件的大小。我们总是能够引用当前文件大小之内,并且也在映射区之内的数据。也就是说我们可以利用 ftruncate 函数动态的更新的文件的大小,而由该文件映射的共享内存同时也会自动更新。






---------------------------------------------------------------------------------------------------------------------
小技巧:生成固定大小的文件

fd = Open(PATHNAME, O_RDWR | O_CREAT | O_TRUNC, FILE_MODE);
    Lseek(fd, filesize-1, SEEK_SET);
    Write(fd, "", 1);
阅读(11349) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

2011-05-22 15:38:59

学习了,多谢楼主分享哦!也欢迎广大linux爱好者来我的论坛一起讨论arm哦!www.lt-net.cn