共享内存介绍(一)

1、 概述
共享内存是可用IPC形式中最快的。一旦这样的内存区映射到共享它的进程地址空间,这些进程间的数据传递就不在涉及内核。不涉及内核的含义是:进程不再通过执行任何进入内核的系统调用来彼此传递数据。
从服务器到客户的文件数据流
上图是一些其他IPC形式在进行进程间的数据传递,通常都需要内核和应用进程之间进行数据的复制工作。在这个过程中,需要四次的复制过程,系统的开销比较大。
使用共享内存区将文件数据从服务器复制到客户
而对比于共享内存的这种形式,服务端和客户只需要各自复制一次,就可以完成数据的传递。对于访问共享内存的进程,都需要某种形式的同步,互斥锁、条件变量、读写锁、记录锁、信号量。
2、 共享内存映射到进程的地址空间
在《深入理解linux内核》这本书中关于进程地址空间有这样的描述:进程的地址空间由允许进程使用的全部线性地址组成,内核可以通过增加和删除某些线性地址区间来动态的修改进程地址空间。进程可能创建一个IPC共享线性区来与其他合作进程共享数据,在这种情况下,内核给这个进程分配一个新的线性区以实现这个方案。
因此,通过共享内存的方法,将文件或者共享内存区映射到调用进程的地址空间,会使进程在线性区增加一段线性地址,进程访问此地址就是可以直接访问文件或者共享的内存区了。
3、 mmap、munmap、msync函数

三个目的:(1)使用普通文件以提供内存映射I/O;(2)使用特殊文件以提供匿名内存映射;(3)使用shm_open以提供无亲缘关系进程间的POsix共享内存。
① void *mmap(void *start, size_t length, int prot, int flags,int fd, off_t offset);
addr,某个特定的地址作为起始地址,当被设置为NULL,系统会在地址空间选择一块合适的内存区域。
length说的是内存段的长度。
prot是用来设定内存段的访问权限。
prot参数 说明
PROT_READ 内存段可读
PROT_WRITE 内存段可写
PROT_EXEC 内存段可执行
PROT_NONE 内存段不能被访问
flags参数控制内存段内容被修改以后程序的行为。
flags参数 说明
MAP_SHARED 进程间共享内存,对该内存段修改反映到映射文件中。提供了POSIX共享内存
MAP_PRIVATE 内存段为调用进程所私有。对该内存段的修改不会反映到映射文件
MAP_ANNOYMOUS 这段内存不是从文件映射而来的。内容被初始化为全0
MAP_FIXED 内存段必须位于start参数指定的地址处,start必须是页大小的整数倍(4K整数倍)
MAP_HUGETLB 按照大内存页面来分配内存空间
fd参数是用来被映射文件对应的文件描述符。通过open系统调用得到。offset设定从何处进行映射
② int munmap(void *start, size_t length);为从某个进程地址空间删除一个映射关系,调用munmap。
start是由mmap返回的地址,length是映射区的大小。再次访问这些地址将导致向调用进程产生一个SIGSEGV信号。
③ int msync(void *addr,size_t len,int flag);为了保持硬盘上的文件内容与映射区中的内容一致,调用msync来执行此种同步。
Flag的取值
常值 说明
MS_ASYNC 执行异步写
MS_SYNC 执行同步写
MS_INVALIDATE 使用高速缓存的数据失效
4、 mmap使用示例(来自unix网络编程卷二)

#include "unpipc.h"
#define SEM_NAME  "mysem"

int main(int argc,char **argv)
{
    int fd,i,nloop,zero = 0;
    int *ptr;
    sem_t *mutex;

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

    fd = open(argv[1],O_RDWR|O_CREAT,FILE_MODE);//打开文件获取文件描述符
    write(fd,&zero,sizeof(int));
    //调用mmap把刚才打开的文件映射到本进程的内存空间。第一个参数空指针,由系统来选择起始地址。长度是
    //一个int的大小,保护模式参数指定读写访问。MAP_SHARE,父进程所做的任何变动子进程都能看到,反过来也一样。
    //函数的返回值是待共享内存区(父子进程共享)的起始地址,我们把它保存在ptr中。
    ptr = mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARE,fd,0);
    close(fd);
    //把标准输出设置成非缓冲模式后调用fork。父子进程都要对由ptr指向的整数计数器执行加1操作
    mutex = sem_open(Px_ipc_name(SEM_NAME));
    sem_unlink(Px_ipc_name(SEM_NAME));
    setbuf(stdout,NULL);

    if(fork() == 0)
    {
        for(i=0;i<nloop;i++)
        {
            sem_wait(mutex);
            printf("child:%d\n",(*ptr)++);
            sem_post(mutex);
        }
        exit(0);
    }
    for(i=0;i<nloop;i++)
    {
        sem_wait(mutex);
        printf("parent:%d\n",(*ptr)++);
        sem_post(mutex);
    }
    exit(0);    
}

猜你喜欢

转载自blog.csdn.net/caogenwangbaoqiang/article/details/80782549