zoukankan      html  css  js  c++  java
  • 系统程序员成长计划内存管理(一)

    Wednesday, April 22nd, 2009 | Author: admin | » Edit «

    转载时请注明出处和作者联系方式
    文章出处:http://www.limodev.cn/blog
    作者联系方式:李先静 <xianjimli at hotmail dot com>

    共享内存

    大家都知道,进程的地址空间是独立的,它们之间互不影响。比如同样地址为0xabcd1234的内存,在不同的进程中,它们的数据是完全不同的。这 样做的好处有:首先是每个进程的地址空间变大了,让编写程序更为容易。其次是一个进程崩溃了,不会影响其它进程,提高了系统的稳定性。

    要做到进程的地址空间独立,光靠软件是难以实现的,通常还依赖于硬件的帮助。这种硬件称为MMU(Memory Manage Unit),即所谓的内存管理单元。在这种体系结构下,内存分为物理内存和虚拟内存两种。物理内存就是实际的内存,机器上装了多大内存条就有多大的物理内 存。而应用程序使用的是虚拟内存,访问内存数据时,由MMU根据页表把虚拟内存地址转换对应的物理内存地址。

    MMU把各个进程的虚拟内存映射到不同的物理内存上,这样就能保证进程的虚拟内存地址是独立的。由于物理内存远远少于各个进程的虚拟内存的总和,操 作系统会把暂时不用的内存数据写到磁盘上去,把腾出来的物理内存分配给有需要的进程使用。一般会创建一个专门的分区存放换出的内存数据,这个分区称为交换 分区。

    从虚拟内存到物理内存的映射并不是一个字节一个字节映射的,而是以一个称为页(page)最小单位的进行的。页的大小视具体硬件平台而定,通常是 4K。当应用程序访问的虚拟内存的页面不在物理内存里时,MMU产生一个缺页中断,并挂起当前进程,缺页中断处理函数负责把相应的数据从磁盘读入内存,然 后唤醒挂起的进程。

    进程地址空间独立有它的好处,但我们需要在进程之间共享数据,比如两个进程共享同一段可执行代码或者共享其它数据,这就需要共享内存。实现共享内存非常容易,只要把两个进程的虚拟内存页面映射同一个物理内存页面就行了。

    在程序中使用共享内存非常简单,操作系统或者函数库提供了一些API。如Linux提供了:

    void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);
    int munmap(void *start, size_t length);

    mmap是Linux下创建共享内存最正统的方法,其它共享内存接口都是以它为基础进行包装的。 mmap可以通过参数 start指定映射的地址,但是这个不能保证成功,实际的地址应以返回值为准。如果需要在不同进程之间传递共享内存的地址,最好是在所有进程中,把共享内 存映射到同样的地址,为了保证映射成功,需要精心选择一些不常用的内存地址,并在程序初始化时就映射好。

    示例:

    下面我们用下循环队列(FIFO Ring),在两个进程之间传递数据,由于循环队列在一个读一个写的情况下不需要加锁,暂时我们可以避开进程间同步的问题。

    o 创建共享内存

    static int g_shmem_fd = -1;
    void* shmem_alloc(size_t size)
    {
    g_shmem_fd = open("/tmp/shmem_demo", O_RDWR);

    if(g_shmem_fd < 0)
    {
    char* buffer = calloc(size, 1);
    g_shmem_fd = open("/tmp/shmem_demo", O_RDWR | O_CREAT, 0640);
    write(g_shmem_fd, buffer, size);
    free(buffer);
    }

    void* addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, g_shmem_fd, 0);

    return addr;
    }

    o 释放共享内存

    void shmem_free(void* addr, size_t size)
    {
    munmap(addr, size);
    close(g_shmem_fd);

    return;
    }

    这里用了一个全局变量 g_shmem_fd来保存文件描述符,这里不好的,在后面的内存管理器中,我们会把它封装到内存管理器。

    o 创建循环队列

    FifoRing* fifo_ring_create(size_t length)
    {
    FifoRing* thiz = NULL;

    return_val_if_fail(length > 1, NULL);

    thiz = (FifoRing*)shmem_alloc(sizeof(FifoRing) + length * sizeof(void*));

    if(thiz != NULL)
    {
    if(thiz->inited == 0)
    {
    thiz->r_cursor = 0;
    thiz->w_cursor = 0;
    thiz->length = length;
    thiz->inited = 1;
    }
    }

    return thiz;
    }

    这里的 shmem_alloc代替了以前的malloc。

    o 销毁循环队列

    void fifo_ring_destroy(FifoRing* thiz)
    {
    if(thiz != NULL)
    {
    shmem_free(thiz, sizeof(FifoRing) + thiz->length * sizeof(void*));
    }

    return;
    }

    这里的shmem_free代替了以前的free。

    循环队列的其它函数实现不变,有兴趣的读者可以参考完整的示例代码。

    本节示例代码请到这里下载。

  • 相关阅读:
    android ListView布局之一(继承listActivity、使用arrayAdapter)
    android your project contains error
    wojilu系统的ORM代码解析[源代码结构分析,ObjectBase基类分析]
    ORM中启用数据库事务
    我记录网站综合系统 技术原理解析[11:ActionProcessor流程wojilu核心]
    互联网,让我们更安全了,还是更危险了【纯讨论】
    不用服务器也能跑的框架wojilu续篇
    使用wojilu 无代码实现 输入框提示 及其背后的原理
    wojilu日志系统可以单独使用
    “我有什么” 和 “你要什么” 框架制作的一些思考
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167540.html
Copyright © 2011-2022 走看看