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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章