Android PMem 和Ashmem 介绍 1、Ashmem(匿名共享内存驱动:Anonymous Shared Memory) 它基于mmap系统调用,不同进程可以将同一段物理内存映射到各自的虚拟地址控制,从而实现共享 A、(mmap:是一种共享内存的系统。假如:A进程的内存空间范围0X0000~0XFFFF,B进程的内存空间范围0X0000~0XFFFF,他们两个进程想共同共享一个文件或一段空间时,可以使用mmap(比如都想读取硬盘上的c.txt,txt内容为"123"),首先另外开辟第三个内存空间(3个字节),将硬盘上的c.txt映射到这个内存空间中,使此内存空间有了这个c.txt,再将A、B进程分别映射至这个内存空间,则现在A进程的内核空间范围为0X0000~0XFFFF+4,B进程的内核空间范围为0X0000~0XFFFF+4。那么此时A、B进程都拥有了共同的内存空间,即可以互相共享共同内存空间里的内容了;当然,如果创建mmap时也可以指定是可读还是可写,如果A或B改变了共同内存空间的值,将c.txt内容改为了"234"的话,硬盘上的c.txt内容仍然为123,若想改变,则得调用msync实现硬盘和共享内存区的同步),而Ashmem与mmap稍有不同的是,Ashmem与cache shrinker关联起来,可以在适当时机去回收这些共享内存,这点比较智能,而mmap是做不到的 B、Ashmem实现 Ashmem类位于/android2.1/kernel/mm/ashmem.c,通过注册cache shrinker来实现回收内存,通过注册misc提供mmap接口等。Ashmem用两个结构体ashmem_area和ashmem_range来维护,ashmem_area代表共享内存的区域,ashmem_range则将这段区域以页为单位分为多个range。ashmem_area有个unpinned_list成员,挂在这个list上的range可以被回收。ashmem_range有一个LRU链表,在cache shrink回收一个ashmem_area的某段内存时候,是根据LRU的原则来选择哪些页面优先被回收的 C、Ashmem流程 ashmem_init(创建struct ashmem_area和struct ashmem_range、注册ashmem driver(misc_register)、注册cache shrinker)----->在注册misc构造方法时,引进了ashmem_fops----->在注册fops时,创建了ashmem_open、ashmem_release、ashmem_mmap、 ashmem_shrink、ashmem_ioctl---->创建ashmem_open时,调用了kmem_cache_zalloc去分配了一个ashmem_area并初始化了成员变量、创建ashmem_release,调用了了kmem_cache_free,此静态方法与zalloc相反,是去释放ashmem_area、创建ashmem_mmap调用shmem_file_setup来从tmpfs系统(基于内存的文件系统)中创建一个文件(内存)给ashmem_area用,这个内存就是共享内存、创建ashmem_shrink来实现内存回收,这个函数从LRU链表上回收指定数目的unpinned ashmem_range、创建ashmem_ioctl,设置一些ashmem_area的size啊,然后查看被pin的range有多少啊,然后pin or unpin range(pin range代表此range从unpinned_list中取下来,而unpin range代表此range挂在unpinned_list上,以便被回收,由此可见只有被unpin的range才会被回收) D、用户接口 进程A可通过open打开该文件,用ioctl命令ASHMEM_SET_NAME和ASHMEM_SET_SIZE设置共享内存块的名字和大小,并将得到的handle传给mmap,来获得共享的内存区域,进程B通过将相同的handle传给mmap,获得同一块内存,handle在进程间的传递可通过Binder来实现。
2、Android PMEM
pmem与ashmem都通过mmap实现共享,区别是Pmem的共享区域是一段连续的物理内存,而Ashmem的共享区域在虚拟空间是连续的,物理内存却不一定连续
A、PMEM的实现
Pmem的源代码在drivers/misc/pmem.c中,Pmem驱动依赖于linux的misc device和platform driver框架,一个系统可以有多个Pmem,默认的是最多10个。Pmem暴露4组操作,分别是platform driver的probe和remove操作; misc device的fops接口和vm_ops操作。模块初始化时会注册一个platform driver,在之后probe时,创建misc设备文件,分配内存,完成初始化工作。
Pmem通过pmem_info,pmem_data,pmem_region三个结构体维护分配的共享内存,其中pmem_info代表一个Pmem设备分配的内存块,pmem_data代表该内存块的一个子块,pmem_region则把每个子块分成多个区域。 pmem_data是分配的基本单位,即每次应用层要分配一块Pmem内存,就会有一个pmem_data来表示这个被分配的内存块,实际上在open的时候,并不是open一个pmem_info表示的整个Pmem内存块,而是创建一个pmem_data以备使用。一个应用可以通过ioctl来分配 pmem_data中的一个区域,并可以把它map到进程空间;并不一定每次都要分配和map整个pmem_data内存块
B、用户接口
一个进程首先打开Pmem设备,通过ioctl(PMEM_ALLOCATE)分配内存,它mmap这段内存到自己的进程空间后,该进程成为 master进程。其他进程可以重新打开这个pmem设 3. 在应用程序中的使用:
1) sp<MemoryHeapBase> master_workspace = new MemoryHeapBase(pmem_adsp, Coda_WorkSpace_Size); //new 一个base memeoryHeapBase ,构造函数中做了2件事情, 一个是open “/dev/pmem_adsp” 设备, 第二是调用mapfd (内部即mmap)来得到共享内存区域 if (master_workspace->heapID() < 0) { LOGD("Error creating workspace heap"); Status = UNKNOWN_ERROR; } master_workspace->setDevice(pmem); // 如果pmem_adsp device出错, 就是用pmem device mHeapPmem_workspace = new MemoryHeapPmem(master_workspace, 0); // new 一个Pmem , MemoryHeapPmem , (构造函数中调用init ,设定了Base,device ,size 等) mHeapPmem_workspace->slap(); master_workspace.clear(); if (ioctl(mHeapPmem_workspace->heapID(), PMEM_GET_PHYS, ®ion) >= 0)//得到物理地址 { pys_address = (unsigned int)region.offset; WORK_SPACE_PMEM_PHY_ADDRESS = pys_address; WORK_SPACE_PMEM_VIR_ADDRESS = mHeapPmem_workspace->base(); //得到虚拟地址 } else { pys_address = 0xFFFFFFFF; LOGE("Error: WORKSPACE PMEM_GET_PHYS FAILED"); return UNKNOWN_ERROR; } 2) Ashmem 例子: sp<MemoryHeapBase> heap = new MemoryHeapBase(frameSize * kBufferCount); if (heap->heapID() < 0) { LOGE("Error creating frame buffer heap"); return false; } //没有指定device , 就是用的ashmem . 构造函数中做了2件事情, 一个是ashmem_create_region, 第二是调用mapfd (内部即mmap)来得到共享内存区域 第一步通过调用ashmem_create_region函数,这个函数完成这几件事: 1)fd = open("/dev/ashmem", O_RDWR); 2)ioctl(fd, ASHMEM_SET_NAME, region_name); // 这一步可选 3)ioctl(fd, ASHMEM_SET_SIZE, region_size); 第二步,应用程序一般会调用mmap来把ashmem分配的空间映射到进程空间: mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); 4. 如何将PMEM编入内核
|
|