android pmem 和ashmem 介绍及实例分析

1Ashmem(匿名共享内存驱动:AnonymousShared Memory)


它基于mmap系统调用,不同进程可以将同一段物理内存映射到各自的虚拟地址控制,从而实现共享


A(mmap:是一种共享内存的系统。假如:A进程的内存空间范围0X00000XFFFFB进程的内存空间范围0X0000~0XFFFF,他们两个进程想共同共享一个文件或一段空间时,可以使用mmap(比如都想读取硬盘上的c.txttxt内容为"123"),首先另外开辟第三个内存空间(3个字节),将硬盘上的c.txt映射到这个内存空间中,使此内存空间有了这个c.txt,再将AB进程分别映射至这个内存空间,则现在A进程的内核空间范围为0X00000XFFFF+4B进程的内核空间范围为0X00000XFFFF+4。那么此时AB进程都拥有了共同的内存空间,即可以互相共享共同内存空间里的内容了;当然,如果创建mmap时也可以指定是可读还是可写,如果AB改变了共同内存空间的值,将c.txt内容改为了"234"的话,硬盘上的c.txt内容仍然为123,若想改变,则得调用msync实现硬盘和共享内存区的同步),而Ashmemmmap稍有不同的是,Ashmemcacheshrinker关联起来,可以在适当时机去回收这些共享内存,这点比较智能,而mmap是做不到的


BAshmem实现


Ashmem类位于/android2.1/kernel/mm/ashmem.c,通过注册cacheshrinker来实现回收内存,通过注册misc提供mmap接口等。Ashmem用两个结构体ashmem_areaashmem_range来维护,ashmem_area代表共享内存的区域,ashmem_range则将这段区域以页为单位分为多个rangeashmem_area有个unpinned_list成员,挂在这个list上的range可以被回收。ashmem_range有一个LRU链表,在cacheshrink回收一个ashmem_area的某段内存时候,是根据LRU的原则来选择哪些页面优先被回收的


CAshmem流程


ashmem_init(创建structashmem_areastructashmem_range、注册ashmemdriver(misc_register)、注册cacheshrinker)----->在注册misc构造方法时,引进了ashmem_fops----->在注册fops时,创建了ashmem_openashmem_releaseashmem_mmap


ashmem_shrinkashmem_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链表上回收指定数目的unpinnedashmem_range、创建ashmem_ioctl,设置一些ashmem_areasize啊,然后查看被pinrange有多少啊,然后pinor unpin range(pin range代表此rangeunpinned_list中取下来,而unpinrange代表此range挂在unpinned_list上,以便被回收,由此可见只有被unpinrange才会被回收)


D、用户接口


进程A可通过open打开该文件,用ioctl命令ASHMEM_SET_NAMEASHMEM_SET_SIZE设置共享内存块的名字和大小,并将得到的handle传给mmap,来获得共享的内存区域,进程B通过将相同的handle传给mmap,获得同一块内存,handle在进程间的传递可通过Binder来实现。



2AndroidPMEM


pmemashmem都通过mmap实现共享,区别是Pmem的共享区域是一段连续的物理内存,而Ashmem的共享区域在虚拟空间是连续的,物理内存却不一定连续


APMEM的实现


Pmem的源代码在drivers/misc/pmem.c中,Pmem驱动依赖于linuxmiscdeviceplatformdriver框架,一个系统可以有多个Pmem,默认的是最多10个。Pmem暴露4组操作,分别是platformdriverproberemove操作;miscdevicefops接口和vm_ops操作。模块初始化时会注册一个platformdriver,在之后probe时,创建misc设备文件,分配内存,完成初始化工作。


Pmem通过pmem_infopmem_datapmem_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

备,通过调用ioctl(PMEM_CONNECT)将自己的pmem_datamaster进程的pmem_data建立连接关系,这个进程就成为client进程。Client进程可以通过mmapmasterPmem中的一段或全部重新映射到自己的进程空间,这样就实现了共享Pmem内存。如果是GPUDSP则可以通过ioctl(PMEM_GET_PHYS)获取物理地址进行操作。



3.在应用程序中的使用:


1)

Pmem例子:



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("Errorcreating workspace heap");

Status= UNKNOWN_ERROR;

}


master_workspace->setDevice(pmem);//如果pmem_adspdevice出错,就是用pmemdevice

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, &region)>= 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");

returnUNKNOWN_ERROR;

}


2)

Ashmem例子:

sp<MemoryHeapBase>heap = new MemoryHeapBase(frameSize * kBufferCount);

if(heap->heapID() < 0) {

LOGE("Errorcreating frame buffer heap");

returnfalse;

}

//没有指定device,就是用的ashmem.构造函数中做了2件事情,一个是ashmem_create_region,第二是调用mapfd(内部即mmap)来得到共享内存区域


第一步通过调用ashmem_create_region函数,这个函数完成这几件事:


1fd= open("/dev/ashmem", O_RDWR);

2ioctl(fd,ASHMEM_SET_NAME, region_name); // 这一步可选

3ioctl(fd,ASHMEM_SET_SIZE, region_size);


第二步,应用程序一般会调用mmap来把ashmem分配的空间映射到进程空间:


mapAddr= mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE, MAP_PRIVATE,fd, 0);



4.如何将PMEM编入内核


AndroidPMEM驱动研究(1)——如何将PMEM编入内核


PMEM并不像Ashmembinder那样,选中就可以被Android系统使用,他是一个platform设备,需要注册才可以使用。


下面以S3C6410为例,描述使用流程:


1)选中内核选项

DeviceDrivers --->

Miscdevices --->

Androidpmem allocator


2)修改你的dev.c注册文件,添加如下内容:

#ifdefCONFIG_ANDROID_PMEM

staticstruct android_pmem_platform_data android_pmem_pdata = {

.name= "pmem",

.start= PMEM_BASE,

.size= PMEM_BASE_SIZE,

.no_allocator = 1,

.cached= 1,

};


staticstruct android_pmem_platform_data android_pmem_adsp_pdata = {

.name= "pmem_adsp",

.start= PMEM_ADSP_BASE,

.size= PMEM_ADSP_BASE_SIZE,

.no_allocator = 0,

.cached= 0,

};


structplatform_device android_pmem_device = {

.name= "android_pmem",

.id= 0,

.dev= { .platform_data = &android_pmem_pdata },

};


structplatform_device android_pmem_adsp_device = {

.name= "android_pmem",

.id= 1,

.dev= { .platform_data = &android_pmem_adsp_pdata },

};

#endif


3)在驱动注册列表中添加如下内容:

staticstruct platform_device *smdk6410_devices[] __initdata = {

#ifdefCONFIG_ANDROID_PMEM

&android_pmem_device,

&android_pmem_adsp_device,

#endif

};


4)分配物理地址我用了128MB的最后8MB

#definePMEM_BASE 0x57900000

#definePMEM_BASE_SIZE SZ_1M*4

#definePMEM_ADSP_BASE 0x57c00000

#definePMEM_ADSP_BASE_SIZE SZ_1M*4


5)重新编译内核


6)修改bootargs减少Linux可管理的MEM

MEM=120MB


7)重新启动系统

启动信息:

pmem:1 init

pmem_adsp:0 init


8)查看dev目录,多了pmempmem_adsp


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值