畢設(shè)做網(wǎng)站具體步驟seo關(guān)鍵詞快速提升軟件官網(wǎng)
源碼基于:Andoird U?+ Kernel-5.10
0. 簡介
ashmem 稱為匿名共享內(nèi)存(Anonymous Shared Memory),它以驅(qū)動程序的形式實現(xiàn)在內(nèi)核空間中。它有兩個特點:
-
能否輔助內(nèi)存管理系統(tǒng)來有效地管理不再使用的內(nèi)存塊(pin / unpin);
-
通過Binder進程間通信機制來實現(xiàn)內(nèi)存共享;
雖然 Binder機制已經(jīng)可以實現(xiàn)了跨進程的高效通信,但是Binder 通信所允許的數(shù)據(jù)是有限制的(如下代碼),如果需要大量數(shù)據(jù)交互就有限制了。
frameworks/native/libs/binder/ProcessState.cpp//限制了大小為
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
ashmem 系統(tǒng)大概分三層:
-
Java 層使用 MemoryFile.java 或 SharedMemory.java 來創(chuàng)建ashmem 共享內(nèi)存;
-
Native 層分兩部分:
-
一部分是從Java 層調(diào)下來的 JNI 接口,另外是給Native 層使用的MemoryHeapBase.cpp 文件;
-
另一部分是ashmem 的lib,實現(xiàn)的函數(shù)定義在 ashmem-dev.cpp 文件中;
-
-
Kernel 層就是 ashmem 的驅(qū)動,ashmem 的核心就是通過驅(qū)動來管理共享內(nèi)存;
1. ashmem 驅(qū)動原理分析
源碼:drivers/staging/android/ashmem.c
-
用戶層都是通過fd 進行mmap 進行映射;
-
雖然 fd 在不同的進程可能不相同,但其對應(yīng)的 file 結(jié)構(gòu)是相同的;
-
file 結(jié)構(gòu)中的成員 private_data 指向 ashmem_area,這就是匿名共享內(nèi)存的核心數(shù)據(jù)結(jié)構(gòu);
-
ashmem_area 中的file 是映射的實際文件,通過shmem_file_setup() 函數(shù)創(chuàng)建,并指定其fops 為shmem_file_operations,該 fops 也被存在 ashmem 中的靜態(tài)局部變量 vmfile_fops 中;
-
vmfile_fops 只初始化一次,后面再次調(diào)用 ashmem_mmap() 函數(shù)時將直接使用;
1.1 ashmem 的重要數(shù)據(jù)結(jié)構(gòu)和變量
1.1.1 變量 ashmem_misc
static struct miscdevice ashmem_misc = {.minor = MISC_DYNAMIC_MINOR,.name = "ashmem",.fops = &ashmem_fops,
};
在 ashmem 驅(qū)動初始化的時候會調(diào)用 misc_register() 進行注冊,詳細可以查看 ashmem_init() 函數(shù)。
該設(shè)備中指定的 file_operations 是 ashmem_fops,如下。
1.1.2 變量 ashmem_fops
static const struct file_operations ashmem_fops = {.owner = THIS_MODULE,.open = ashmem_open, //節(jié)點文件open函數(shù).release = ashmem_release, //節(jié)點文件結(jié)構(gòu)被釋放時會調(diào)用release函數(shù).read_iter = ashmem_read_iter, .llseek = ashmem_llseek,.mmap = ashmem_mmap, //節(jié)點文件的mmap函數(shù).unlocked_ioctl = ashmem_ioctl, //節(jié)點文件的ioctl函數(shù)
#ifdef CONFIG_COMPAT.compat_ioctl = compat_ashmem_ioctl, //32位用戶系統(tǒng)調(diào)用64位驅(qū)動的ioctl函數(shù)時使用
#endif
#ifdef CONFIG_PROC_FS.show_fdinfo = ashmem_show_fdinfo, //查詢/proc/pid/fdinfos/[fd]時打印
#endif
};
compat_ioctl 對于64bit 的驅(qū)動必須要實現(xiàn)的ioctl,當有32 bit 的用戶層調(diào)用 ioctl() 時,會callback 到這里,否則會返回 Not a typewriter 的錯誤。
64bit 用戶層調(diào)用 64bit 驅(qū)動,或者 32bit 用戶層調(diào)用 32bit 驅(qū)動時,都是callback unlocked_ioctl 函數(shù)。
show_fdinfo 函數(shù)用于查詢 /proc/<pid>/fdinfos/<fd> 時打印,例如:
phone_shift:/ # cat /proc/967/fdinfo/34
pos: 0
flags: 0400002
mnt_id: 45
ino: 1210
inode: 6146
name: gralloc_shared_memory
size: 2404
1.1.3 struct ashmem_area
struct ashmem_area {char name[ASHMEM_FULL_NAME_LEN]; //共享內(nèi)存區(qū)域的名稱,在/proc/<pid>/maps中攜帶struct list_head unpinned_list; //用以串聯(lián)所有的 ashmem_areastruct file *file; //該共享內(nèi)存的實際filesize_t size; //該共享內(nèi)存的大小unsigned long prot_mask; //該共享內(nèi)存文件的屬性,包括r、w、x
};
驅(qū)動中通過該數(shù)據(jù)結(jié)構(gòu)管理所有的共享內(nèi)存區(qū)域,每個共享內(nèi)存區(qū)域都有一個名字,前綴是ASHMEM_NAME_PREFIX (dev/ashmem/),這個名字通過 /proc/<pid>/maps 查看到,例如:
130|phone_shift:/ # cat /proc/967/maps | grep ashmem
76b525e000-76b526e000 rw-s 00000000 00:01 13 /dev/ashmem/MessageQueue (deleted)
76b5309000-76b5319000 rw-s 00000000 00:01 2057 /dev/ashmem/MessageQueue (deleted)
76b55c2000-76b55c3000 rw-s 00000000 00:01 3095 /dev/ashmem/gralloc_shared_memory (deleted)
76b55c4000-76b55c5000 rw-s 00000000 00:01 4099 /dev/ashmem/gralloc_shared_memory (deleted)
這是 hwc 進程中 ashmem 對應(yīng)的 vma。在申請ashmem 的時候,都會指定其 name。
每個ashmem 都會在臨時文件系統(tǒng) tmpfs 中對應(yīng)一個文件,也就是成員變量 file,并且通過 prot_mask 指定文件訪問的權(quán)限,該 ashmem 在初始化的時候 prot_mask 被指定為 PROT_MASK,后期根據(jù)實際情況可以通過接口進行修改。
#define PROT_MASK (PROT_EXEC | PROT_READ | PROT_WRITE)
ashmem 機制中,需要使用內(nèi)存塊(ashmem_range) 時需要調(diào)用ashmem_pin() 函數(shù)進行鎖定,不被使用的內(nèi)存會解除鎖定。
如果內(nèi)存塊(ashmem_range) 解除鎖定,會將這些內(nèi)存塊(ashmem_range) 添加到 asma->unpinned_list 鏈表中,且添加到 ashmem_lru_list 中便于內(nèi)存緊張時進行回收。但是兩個鏈表的插入方式不同:
-
asma->unpinned_list 是將 ashmem_range 的地址從大到小串聯(lián)起來;
-
ashmem_lru_list 是按照LRU 方式將最新的 ashmem_range 插入到鏈表尾;
1.1.4 struct ashmem_range
struct ashmem_range {struct list_head lru; //串聯(lián)鏈表使用struct list_head unpinned; //用以標記是否加入unpinned liststruct ashmem_area *asma; //該塊內(nèi)存歸屬于ashmem_areasize_t pgstart; //內(nèi)存的起始pagesize_t pgend; //內(nèi)存的結(jié)尾page,這塊內(nèi)存區(qū)間是[pgstart, pgend]unsigned int purged; //標記這塊區(qū)間是否被回收
};
ashmem_range 可以理解為 ashmem_area 中的內(nèi)存塊。
每個 ashmem_area 會被分成很多的小塊(ashmem_range),這些小塊會有兩種狀態(tài):pin 和 unpin。如果該內(nèi)存塊為 unpin,則該內(nèi)存塊會被添加到 ashmem_area 中的 unpinned_list 鏈表中,通過成員變量 unpinned。
每個小塊的內(nèi)存區(qū)間是 [pgstart, pgend]。
1.1.5 struct ashmem_pin
struct ashmem_pin {__u32 offset; /* offset into region, in bytes, page-aligned */__u32 len; /* length forward from offset, in bytes, page-aligned */
};
提供給用戶的結(jié)構(gòu)體,標記 pin/unpin 時的內(nèi)存區(qū)域,但是有一定要求:
-
len 可以為0,這樣內(nèi)存空間 ashmem_area 從 offset 之后的所有空間;
-
offset和 len 需要頁對齊;
-
offset + len 不能超過 32位無符號數(shù);
-
offset + len 不能超過 ashmem_area 空間;
1.1.6 ashmem_lru_list
/* LRU list of unpinned pages, protected by ashmem_mutex */
static LIST_HEAD(ashmem_lru_list);static unsigned long lru_count;
在 ashmem_pin() 或這 ashmem_unpin() 中會分配新的 ashmem_range 對象,該結(jié)構(gòu)體中成員 lru 用以串聯(lián)鏈表并存放在 ashmem_lru_list 這個全局鏈表中,便于在內(nèi)存使用緊張的時候進行回收。
與 ashmem_lru_list 對應(yīng)的還有 lru_count 用于統(tǒng)計該 list 中頁面數(shù)量。
1.2 ashmem 初始化
static int __init ashmem_init(void)
{int ret = -ENOMEM;ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",sizeof(struct ashmem_area),0, 0, NULL);if (!ashmem_area_cachep) {pr_err("failed to create slab cache\n");goto out;}ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",sizeof(struct ashmem_range),0, 0, NULL);if (!ashmem_range_cachep) {pr_err("failed to create slab cache\n");goto out_free1;}ret = misc_register(&ashmem_misc);if (ret) {pr_err("failed to register misc device!\n");goto out_free2;}ret = register_shrinker(&ashmem_shrinker);if (ret) {pr_err("failed to register shrinker!\n");goto out_demisc;}pr_info("initialized\n");return 0;out_demisc:misc_deregister(&ashmem_misc);
out_free2:kmem_cache_destroy(ashmem_range_cachep);
out_free1:kmem_cache_destroy(ashmem_area_cachep);
out:return ret;
}
device_initcall(ashmem_init);
代碼比較清晰,主要做了幾件事:
-
創(chuàng)建兩個 slab cache:ashmem_area_cache 和 ashmem_range_cache,后面struct ashmem_area 和 struct ashmem_range 都是從slab 中分配內(nèi)存;
-
misc_register() 函數(shù)注冊 ashmem 設(shè)備;
-
register_shrinker() 注冊回收函數(shù),當內(nèi)存不足時系統(tǒng)會通過 shrink_slab() 函數(shù)輪訓(xùn)系統(tǒng)中所有調(diào)用 register_shrinker() 注冊的回收函數(shù);
1.3?ashmem_open()
static int ashmem_open(struct inode *inode, struct file *file)
{struct ashmem_area *asma;int ret;ret = generic_file_open(inode, file);if (ret)return ret;asma = kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL);if (!asma)return -ENOMEM;INIT_LIST_HEAD(&asma->unpinned_list);memcpy(asma->name, ASHMEM_NAME_PREFIX, ASHMEM_NAME_PREFIX_LEN);asma->prot_mask = PROT_MASK;file->private_data = asma;return 0;
}
用戶通過 open("/dev/ashmem") 函數(shù)進而觸發(fā)該函數(shù),主要目的是創(chuàng)建一個 ashmem_area 并初始化。
注意三點:
-
ashmem_area 名稱初始化就有了,默認是 “/dev/ashmem/”;
-
默認ashmem_area 對應(yīng)的共享文件是 PROT_MASK 權(quán)限;
-
ashmem_area 會被記錄到 file->private_data 中;
1.4?ashmem_release()
static int ashmem_release(struct inode *ignored, struct file *file)
{struct ashmem_area *asma = file->private_data;struct ashmem_range *range, *next;mutex_lock(&ashmem_mutex);list_for_each_entry_safe(range, next, &asma->unpinned_list, unpinned)range_del(range);mutex_unlock(&ashmem_mutex);if (asma->file)fput(asma->file);kmem_cache_free(ashmem_area_cachep, asma);return 0;
}
當最后一個打開設(shè)備的用戶執(zhí)行 close() 系統(tǒng)調(diào)用時,內(nèi)核會調(diào)用驅(qū)動設(shè)定的 fops->release() 函數(shù)。對應(yīng) ashmem 驅(qū)動來說就是調(diào)用 ashmem_release() 函數(shù)。
主要做了三件事情:
-
遍歷所有的 unpinned_list,將其中的 range 內(nèi)存都釋放掉;
-
fput() 將文件的計數(shù)減 1,如果發(fā)現(xiàn) f_count為0了,那么將其對應(yīng)的struct file結(jié)構(gòu)刪除。與其對應(yīng)的是 fget() 函數(shù),用以獲取 struct file 結(jié)構(gòu)并將 f_count 計數(shù)加 1;
-
釋放ashmem_area 內(nèi)存;
1.5 ashmem_mmap()
static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
{static struct file_operations vmfile_fops;struct ashmem_area *asma = file->private_data;int ret = 0;mutex_lock(&ashmem_mutex);/* user needs to SET_SIZE before mapping *///用戶在調(diào)用mmap進行映射之前需要先調(diào)用 set_size()函數(shù)配置大小,否則無法mmapif (!asma->size) {ret = -EINVAL;goto out;}/* requested mapping size larger than object size *///要求ashmem_area的size大于請求mapping的大小if (vma->vm_end - vma->vm_start > PAGE_ALIGN(asma->size)) {ret = -EINVAL;goto out;}/* requested protection bits must match our allowed protection mask *///檢測需要映射的vma的保護權(quán)限是否超過了ashmem_area的權(quán)限if ((vma->vm_flags & ~calc_vm_prot_bits(asma->prot_mask, 0)) &calc_vm_prot_bits(PROT_MASK, 0)) {ret = -EPERM;goto out;}vma->vm_flags &= ~calc_vm_may_flags(~asma->prot_mask);//這是ashmem_mmap()函數(shù)的核心處理,創(chuàng)建一個臨時文件if (!asma->file) {char *name = ASHMEM_NAME_DEF;struct file *vmfile;struct inode *inode;//臨時文件名默認/dev/ashmem,但如果asma->name已經(jīng)配置好,則使用asma->nameif (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0')name = asma->name;/* ... and allocate the backing shmem file *///利用linux原生的shmem,在tmpfs中創(chuàng)建一個臨時文件,該文件只對內(nèi)核態(tài)可見,用戶態(tài)不可見vmfile = shmem_file_setup(name, asma->size, vma->vm_flags);if (IS_ERR(vmfile)) {ret = PTR_ERR(vmfile);goto out;}//臨時文件初始化vmfile->f_mode |= FMODE_LSEEK;inode = file_inode(vmfile);lockdep_set_class(&inode->i_rwsem, &backing_shmem_inode_class);//更新asma->file,此后的共享內(nèi)存對應(yīng)臨時文件asma->file = vmfile;//更新臨時文件的fops,覆蓋掉mmap函數(shù),臨時文件mmap不再做任何事情//并且,要覆蓋掉get_unmapped_area接口,獲取進程沒有映射的內(nèi)存空間//vmfile_fops為staic,只需要更新一次if (!vmfile_fops.mmap) {vmfile_fops = *vmfile->f_op; //拿到vmfile的 fops,準備更新vmfile_fops.mmap = ashmem_vmfile_mmap;vmfile_fops.get_unmapped_area =ashmem_vmfile_get_unmapped_area;}//每次創(chuàng)建的臨時文件的fops都使用vmfile_fopsvmfile->f_op = &vmfile_fops;}get_file(asma->file);//如果該vma已經(jīng)處于共享狀態(tài),調(diào)用shmem_zero_setup()配置vma->vm_ops為shmem_vm_ops//vma->vm_file在后面會更新掉if (vma->vm_flags & VM_SHARED) {ret = shmem_zero_setup(vma);if (ret) {fput(asma->file);goto out;}} else {vma_set_anonymous(vma);}//vma->vm_file將指定到asma->file,即指定到創(chuàng)建好的臨時vmfileif (vma->vm_file)fput(vma->vm_file);vma->vm_file = asma->file;out:mutex_unlock(&ashmem_mutex);return ret;
}
當?shù)谝淮蝝map 時,會通過 shmem_file_setup() 在tmpfs 文件系統(tǒng)中創(chuàng)建一個臨時文件(也許只是內(nèi)核中的一個inode 節(jié)點)。該臨時文件 vmfile 創(chuàng)建好之后會覆蓋 asma->file,至此ashmem 與該vmfile 對應(yīng),ashmem 機制真正使用的map 對象就是該臨時文件vmfile。
1.6 ashmem_ioctl()
涉及的命令有:
drivers/staging/android/uapi/ashmem.h#define __ASHMEMIOC 0x77#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)
#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)
#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long)
#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6)
#define ASHMEM_PIN _IOW(__ASHMEMIOC, 7, struct ashmem_pin)
#define ASHMEM_UNPIN _IOW(__ASHMEMIOC, 8, struct ashmem_pin)
#define ASHMEM_GET_PIN_STATUS _IO(__ASHMEMIOC, 9)
#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)
具體實現(xiàn)可以查看源碼,主要來看下 pin 和 unpin:
static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{struct ashmem_area *asma = file->private_data;long ret = -ENOTTY;switch (cmd) {...case ASHMEM_PIN:case ASHMEM_UNPIN:case ASHMEM_GET_PIN_STATUS:ret = ashmem_pin_unpin(asma, cmd, (void __user *)arg);break;}return ret;
}
1.7 ashmem_pin_unpin()
static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd,void __user *p)
{struct ashmem_pin pin;size_t pgstart, pgend;int ret = -EINVAL;struct ashmem_range *range = NULL;//用戶等調(diào)用pin/unpin時,會傳遞ashmem_pin類型的參數(shù)if (copy_from_user(&pin, p, sizeof(pin)))return -EFAULT;//當調(diào)用pin/unpin接口時,創(chuàng)建一個ashmem_range對象if (cmd == ASHMEM_PIN || cmd == ASHMEM_UNPIN) {range = kmem_cache_zalloc(ashmem_range_cachep, GFP_KERNEL);if (!range)return -ENOMEM;}mutex_lock(&ashmem_mutex);//等待回收的完成wait_event(ashmem_shrink_wait, !atomic_read(&ashmem_shrink_inflight));//如果臨時文件vmfile還沒有創(chuàng)建好,無法進行pin/unpin操作if (!asma->file)goto out_unlock;//pin.len的值可以設(shè)為0,即從pin.offset之后的全部內(nèi)存if (!pin.len)pin.len = PAGE_ALIGN(asma->size) - pin.offset;//偏移地址和大小要求頁對齊if ((pin.offset | pin.len) & ~PAGE_MASK)goto out_unlock;//需要操作內(nèi)存末尾地址不能超出if (((__u32)-1) - pin.offset < pin.len)goto out_unlock;//需要操作內(nèi)存末尾地址不能超過ashmem_area指定的sizeif (PAGE_ALIGN(asma->size) < pin.offset + pin.len)goto out_unlock;//操作區(qū)間需要頁對齊pgstart = pin.offset / PAGE_SIZE;pgend = pgstart + (pin.len / PAGE_SIZE) - 1;switch (cmd) {case ASHMEM_PIN:ret = ashmem_pin(asma, pgstart, pgend, &range);break;case ASHMEM_UNPIN:ret = ashmem_unpin(asma, pgstart, pgend, &range);break;case ASHMEM_GET_PIN_STATUS:ret = ashmem_get_pin_status(asma, pgstart, pgend);break;}out_unlock:mutex_unlock(&ashmem_mutex);if (range)kmem_cache_free(ashmem_range_cachep, range);return ret;
}
pin/unpin 對于 offset 和 len 有一定的要求:
-
len 可以為0,這樣內(nèi)存空間 ashmem_area 從 offset 之后的所有空間;
-
offset和len 需要頁對齊;
-
offset + len 不能超過 32位無符號數(shù);
-
offset + len 不能超過 ashmem_area 空間;
ashmem 機制中,正在使用的 ashmem_range 需要 pin,不被使用的 ashmem_range 需要 unpin。unpin 的ashmem_range 會添加到 asma->unpinned_list 鏈表中,且該 ashmem_range 會被添加到ashmem_lru_list 中。
pin 和 unpin 只是改變相關(guān)狀態(tài)標記,并不會改變已經(jīng) mapping 的地址空間,因此,用戶可以在unpin 后重新pin 住內(nèi)存塊。
注意:
函數(shù)的返回值為實際處理函數(shù)的范圍值,有兩種情況:ASHMEM_NOT_PURGED 和ASHMEM_WAS_PURGED。ASHMEM_NOT_PURGED 表示該內(nèi)存塊物理內(nèi)存沒有被回收。
1.7.1 ashmem_unpin()
static int ashmem_unpin(struct ashmem_area *asma, size_t pgstart, size_t pgend,struct ashmem_range **new_range)
{struct ashmem_range *range, *next;unsigned int purged = ASHMEM_NOT_PURGED;restart:list_for_each_entry_safe(range, next, &asma->unpinned_list, unpinned) {/* short circuit: this is our insertion point */if (range_before_page(range, pgstart))break;/** The user can ask us to unpin pages that are already entirely* or partially pinned. We handle those two cases here.*/if (page_range_subsumed_by_range(range, pgstart, pgend))return 0;if (page_range_in_range(range, pgstart, pgend)) {pgstart = min(range->pgstart, pgstart);pgend = max(range->pgend, pgend);purged |= range->purged;range_del(range);goto restart;}}range_alloc(asma, range, purged, pgstart, pgend, new_range);return 0;
}
如 struct ashmem_area 中所述,一塊匿名共享內(nèi)存中的所有解鎖內(nèi)存塊,都是按照地址從大到小的順序保存在 unpinned_list 中。
該函數(shù)的目的是將該內(nèi)存塊(ashmem_range) 添加到 asma->unpinned_list 和 ashmem_lru_list 鏈表中。
當內(nèi)存塊插入到 asma->unpinned_list 時會考慮內(nèi)存合并,有如下幾種處理方式(綠色是old,紅色是new):
-
A:新添加的內(nèi)存塊地址大于最大range,將直接添加到asma->unpinned_list 頭部;
-
B:新添加的內(nèi)存塊完全處于某range 中,不做任何處理,原先的range 已經(jīng)unpinned 了;
-
C、D、E:三種情況是合并的情況,都會將原先range 與新的range 進行合并,并將原先range刪掉,保留新的range;
1.7.2 ashmem_pin()
static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend,struct ashmem_range **new_range)
{struct ashmem_range *range, *next;int ret = ASHMEM_NOT_PURGED;list_for_each_entry_safe(range, next, &asma->unpinned_list, unpinned) {/* moved past last applicable page; we can short circuit */if (range_before_page(range, pgstart))break;if (page_range_in_range(range, pgstart, pgend)) {ret |= range->purged;/* Case #1: Easy. Just nuke the whole thing. */if (page_range_subsumes_range(range, pgstart, pgend)) {range_del(range);continue;}/* Case #2: We overlap from the start, so adjust it */if (range->pgstart >= pgstart) {range_shrink(range, pgend + 1, range->pgend);continue;}/* Case #3: We overlap from the rear, so adjust it */if (range->pgend <= pgend) {range_shrink(range, range->pgstart,pgstart - 1);continue;}/** Case #4: We eat a chunk out of the middle. A bit* more complicated, we allocate a new range for the* second half and adjust the first chunk's endpoint.*/range_alloc(asma, range, range->purged,pgend + 1, range->pgend, new_range);range_shrink(range, range->pgstart, pgstart - 1);break;}}return ret;
}
ashmem機制中的一個內(nèi)存塊,最開始一定處于鎖定狀態(tài),被解鎖之后會被放入到 asma->unpinned_list 中。該函數(shù)的目的是遍歷該 unpinned_list,尋找和指定的pgstart和pgend 相交或包含的內(nèi)存塊,對其進行重新鎖定。
同ashmem_unpinned(),仍然將處理方式分為如下幾種(綠色是old,紅色是new):
A:需要pinned 的內(nèi)存塊不在 asma->unpinned_list 中,那不用過多考慮;
B:需要pinned 的內(nèi)存塊完全包含range中,繼續(xù)輪詢確認是否與其他range有交叉;
C、D:并不會刪除range 后再重新分配,而是直接修改 range->pgstart 和 range->pgend,并相應(yīng)的減少 LRU list中的頁面數(shù)量;
E:這種情況比較特殊,會將range 切成兩塊,[range->pgstart, pgstart) 算在原來的range 中,(pgend, range->pgend] 算到new range中;
2. ashmem 在進程間共享的原理
假設(shè)進程 A 調(diào)用 open() 函數(shù)打開 /dev/ashmem,這樣會得到一個匿名共享內(nèi)存的一個struct file 和文件描述符,假如是 file1 和 fd1. 然后進程 B 通過 Binder 進程間通信機制請求進程 A 將fd1 返回給它,但 fd1 只在進程 A 中有效,因此 Binder 驅(qū)動程序在進程B 中創(chuàng)建一個新的描述符 fd2,使得fd2 也指向 file1,最后再將 fd2 返回給進程 B。這樣描述符 fd1 和 fd2 就指向同一個文件結(jié)構(gòu)體 file1,即指向同一個匿名共享內(nèi)存。
3. ashmem 注意點
-
ashmem 機制相當于Linux 共享內(nèi)存的擴展,擴展后使用更加便捷;
-
Android 中通過 binder 機制將 ashmem 的 fd 進行傳遞,增加安全性,同時避免了 buffer 拷貝,效率提升;
-
ashmem 不會占用Dalvik heap 和 Native heap,所以不會導(dǎo)致 OOM;
-
ashmem 占用空間的計算,是計算到第一個創(chuàng)建它的進程中,其他進程不會將 ashmem 計算在內(nèi);
參考:
https://www.kancloud.cn/alex_wsc/androids/477718
https://blog.csdn.net/vviccc/article/details/123237169
https://blog.csdn.net/run068/article/details/121695036
https://www.cjcbill.com/2019/04/15/android-ashmem/
https://blog.51cto.com/u_9420214/6331901