• 细说linux IPC(三):mmap系统调用共享内存


            【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet 或 .../gentleliu,文章仅供学习交流,请勿用于商业用途】
            前面讲到socket的进程间通信方式。这样的方式在进程间传递数据时首先须要从进程1地址空间中把数据复制到内核,内核再将数据复制到进程2的地址空间中,也就是数据传递须要经过内核传递。这样在处理较多数据时效率不是非常高。而让多个进程共享一片内存区则攻克了之前socket进程通信的问题。共享内存是最快的进程间通信 。将一片内存映射到多个进程地址空间中,那么进程间的数据传递将不在涉及内核。
            共享内存并非从某一进程拥有的内存中划分出来的。进程的内存总是私有的。共享内存是从系统的空暇内存池中分配的,希望訪问它的每一个进程连接它。这个连接过程称为映射。它给共享内存段分配每一个进程的地址空间中的本地地址。
        mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后。进程能够向訪问普通内存一样对文件进行訪问。不必再调用read(),write()等操作。

    函数原型为:

         #include <sys/mman.h>
         void *mmap(void *addr, size_t length, int prot, int flags,  int fd, off_t offset); 
            当中參数addr为描写叙述符fd应该被映射到进程空间的起始地址,当指定为NULL时内核将自己去选择起始地址,不管addr是为NULL,函数返回值都是fd所映射到内存的起始地址;
            len是映射到调用进程地址空间的字节数,它 从被映射文件开头offset个字节開始算起,offset通常设置为0;
            prot 參数指定共享内存的訪问权限。可取例如以下几个值的或:PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可运行), PROT_NONE(不可訪问)。该值常设置为PROT_READ | PROT_WRITE 。
            flags由下面几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,当中,MAP_SHARED(变动是共享的,对共享内存的改动全部进程可见) , MAP_PRIVATE(变动是私有的。对共享内存改动仅仅对该进程可见)   必选其一,而MAP_FIXED则不推荐使用 。

        munmp() 删除地址映射关系。函数原型例如以下:
         #include <sys/mman.h>
           int munmap(void *addr, size_t length);
            參数addr是由mmap返回的地址。len是映射区大小。

            进程在映射空间的对共享内容的改变并不直接写回到磁盘文件里。往往在调用munmap()后才运行该操作。能够通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。 msync()函数原型为:
           #include <sys/mman.h>
           int msync(void *addr, size_t length, int flags);
    
            參数addr和len代表内存区,flags有下面值指定,MS_ASYNC(运行异步写), MS_SYNC(运行同步写),MS_INVALIDATE(使快速缓存失效)。当中MS_ASYNC和MS_SYNC两个值必须且仅仅能指定一个,一旦写操作排入内核。MS_ASYNC马上返回,MS_SYNC要等到写操作完毕后才返回。假设还指定了MS_INVALIDATE,那么与其终于拷贝不一致的文件数据的全部内存中拷贝都失效。

            在使用open函数打开一个文件之后调用mmap把文件内容映射到调用进程的地址空间,这样我们操作文件内容仅仅须要对映射的地址空间进行操作。而无需再使用open。write等函数。

            使用共享内存的步骤基本是:
    open()创建内存段;
    用 ftruncate()设置它的大小。
    用mmap() 把它映射到进程内存,运行其它參与者须要的操作;
    当使用完时,原来的进程调用 munmap()然后退出。
    以下来看一个实现:
    server程序创建内存并向共享内存写入数据:
    int sln_shm_get(char *shm_file, void **shm, int mem_len)
    {
        int fd;
        fd = open(shm_file, O_RDWR | O_CREAT, 0644);//1. 创建内存段
        if (fd < 0) {
            printf("open <%s> failed: %s
    ", shm_file, strerror(errno));
            return -1;
        }
        ftruncate(fd, mem_len);//2.设置共享内存大小
        *shm = mmap(NULL, mem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //mmap映射系统内存池到进程内存
        if (MAP_FAILED == *shm) {
            printf("mmap: %s
    ", strerror(errno));
            return -1;
        }
        close(fd);
        return 0;
    }
    int main(int argc, const char *argv[])
    {
        char *shm_buf = NULL;
        sln_shm_get(SHM_IPC_FILENAME, (void **)&shm_buf, SHM_IPC_MAX_LEN);
        snprintf(shm_buf, SHM_IPC_MAX_LEN, "hello share memory ipc! i'm server.");
        return 0;
    }
    client程序映射共享内存并读取当中数据:
    int main(int argc, const char *argv[])
    {
        char *shm_buf = NULL;
        sln_shm_get(SHM_IPC_FILENAME, (void **)&shm_buf, SHM_IPC_MAX_LEN);
        printf("ipc client get: %s
    ", shm_buf);
        munmap(shm_buf, SHM_IPC_MAX_LEN);
        return 0;
    }
    
    先执行server程序向共享内存写入数据,再执行客户程序。执行结果例如以下:
    # ./server
    # ./client
    ipc client get: hello share memory ipc! i'm server.
    #
    共享内存不像socket那样本身具有同步机制,它须要通过添加其它同步操作来实现同步,比方信号量等。同步相关操作在后面会有相关专栏具体叙述。


    本节源代码下载:

    http://download.csdn.net/detail/gentleliu/8140487

    最后今天祝各位单身程序员节日快乐!。!!!


  • 相关阅读:
    转载ORACLE批量绑定FORALL与BULK COLLECT
    Oracle Locking Survival Guide
    转载:TOAD中查看执行计划
    Oracle 9i/10g编程艺术笔记第七章 并发与多版本
    C#调用Oracle存储过程返回多结果集
    转载oracle 字符集查看与修改
    转载:Oracle的优化器(Optimizer)
    Oracle 随笔
    转载:Oracle中SQL语句执行效率的查找与解决
    当查询和设置需要输入Pn时界面的处理方法
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/6809825.html
Copyright © 2020-2023  润新知