- 1噪珊、Binder驅(qū)動(dòng)簡(jiǎn)述
- 2油湖、Binder驅(qū)動(dòng)的核心函數(shù)
- 3纠永、Binder驅(qū)動(dòng)的結(jié)構(gòu)體
- 4、Binder驅(qū)動(dòng)通信協(xié)議
- 5核蘸、Binder驅(qū)動(dòng)內(nèi)存
- 6巍糯、附錄:關(guān)于misc
驅(qū)動(dòng)層的原路徑(這部分代碼不在AOSP中,而是位于Linux內(nèi)核代碼中)
- /kernel/drivers/android/binder.c
- /kernel/include/uapi/linux/android/binder.h
或者 - /kernel/drivers/staging/android/binder.c
- /kernel/drivers/staging/android/uapi/binder.h
1 Binder驅(qū)動(dòng)簡(jiǎn)述
1.1 簡(jiǎn)述
- Binder驅(qū)動(dòng)是Android專用的客扎,但底層的驅(qū)動(dòng)架構(gòu)與Linux驅(qū)動(dòng)一樣祟峦。
- Binder驅(qū)動(dòng)在misc設(shè)備上進(jìn)行注冊(cè),作為虛擬字符設(shè)備徙鱼,沒(méi)有直接操作硬件宅楞,只對(duì)設(shè)備內(nèi)存做處理。
- 主要工作是:
- 驅(qū)動(dòng)設(shè)備的初始化(binder_init)
- 打開(kāi)(binder_open)
- 映射(binder_mmap)
- 數(shù)據(jù)操作(binder_ioctl)袱吆。
1.2 系統(tǒng)調(diào)用
- 用戶態(tài)的程序調(diào)用Kernel層驅(qū)動(dòng)是需要陷入內(nèi)核態(tài)厌衙,進(jìn)行系統(tǒng)調(diào)用(system call,后面簡(jiǎn)寫syscall)绞绒,比如打開(kāi)Binder驅(qū)動(dòng)方法的調(diào)用鏈為:open——> _open()——> binder_open() 婶希。
-
open() 為用戶態(tài)的函數(shù),_open()便是系統(tǒng)調(diào)用(syscall)中的響應(yīng)的處理函數(shù)蓬衡,通過(guò)查找喻杈,調(diào)用內(nèi)核態(tài)中對(duì)應(yīng)的驅(qū)動(dòng)binder_open()函數(shù),至于其他的從用戶態(tài)陷入內(nèi)核態(tài)的流程也基本一致狰晚。
當(dāng)用戶空間調(diào)用open()函數(shù)筒饰,最終會(huì)調(diào)用binder驅(qū)動(dòng)的binder_open()函數(shù);mmap()/ioctl()函數(shù)也是同理家肯,Binder的系統(tǒng)中的用戶態(tài)進(jìn)入內(nèi)核態(tài)都依賴系統(tǒng)調(diào)用過(guò)程龄砰。
1.3 Binder驅(qū)動(dòng)的四個(gè)核心方法
1.3.1 binder_init()函數(shù)
代碼在/kernel/drivers/android/binder.c
代碼如下:
//kernel/drivers/android/binder.c 4216行
static int __init binder_init(void)
{
int ret;
//創(chuàng)建名為binder的工作隊(duì)列
binder_deferred_workqueue = create_singlethread_workqueue("binder");
// **** 省略部分代碼 ****
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
if (binder_debugfs_dir_entry_root)
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
binder_debugfs_dir_entry_root);
if (binder_debugfs_dir_entry_root) {
//在debugfs文件系統(tǒng)中創(chuàng)建一系列的問(wèn)題件
// **** 省略部分代碼 ****
}
// **** 省略部分代碼 ****
while ((device_name = strsep(&device_names, ","))) {
//binder設(shè)備初始化
ret = init_binder_device(device_name);
if (ret)
//binder設(shè)備初始化失敗
goto err_init_binder_device_failed;
}
return ret;
}
- debugfs_create_dir是指在debugfs文件系統(tǒng)中創(chuàng)建一個(gè)目錄,返回的是指向dentry的指針。
- 當(dāng)kernel中禁用debugfs的話换棚,返回值是 -%ENODEV式镐。默認(rèn)是禁用的。如果需要打開(kāi)固蚤,在目錄/kernel/arch/arm64/configs/下找到目標(biāo)defconfig文件中添加一行CONFIG_DEBUG_FS=y娘汞,再重新編譯版本,即可打開(kāi)debug_fs夕玩。
1.3.2 init_binder_device()函數(shù)解析
//kernel/drivers/android/binder.c 4189行
static int __init init_binder_device(const char *name)
{
int ret;
struct binder_device *binder_device;
binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
if (!binder_device)
return -ENOMEM;
binder_device->miscdev.fops = &binder_fops;
binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
binder_device->miscdev.name = name;
binder_device->context.binder_context_mgr_uid = INVALID_UID;
binder_device->context.name = name;
//注冊(cè) misc設(shè)備
ret = misc_register(&binder_device->miscdev);
if (ret < 0) {
kfree(binder_device);
return ret;
}
hlist_add_head(&binder_device->hlist, &binder_devices);
return ret;
}
通過(guò)調(diào)用misc_register()函數(shù)來(lái)注冊(cè)misc設(shè)備你弦,miscdevice結(jié)構(gòu)體,便是前面注冊(cè)misc設(shè)備時(shí)傳遞進(jìn)去的參數(shù)
1.3.2.1binder_device的結(jié)構(gòu)體
binder_device的結(jié)構(gòu)體
//kernel/drivers/android/binder.c 234行
struct binder_device {
struct hlist_node hlist;
struct miscdevice miscdev;
struct binder_context context;
};
在binder_device里面有一個(gè)miscdevice
//設(shè)備文件操作結(jié)構(gòu)燎孟,這是file_operation結(jié)構(gòu)
binder_device->miscdev.fops = &binder_fops;
// 次設(shè)備號(hào) 動(dòng)態(tài)分配
binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
// 設(shè)備名
binder_device->miscdev.name = name;
// uid
binder_device->context.binder_context_mgr_uid = INVALID_UID;
//上下文的名字
binder_device->context.name = name;
1.3.2.2 file_operations的結(jié)構(gòu)體
file_operations 結(jié)構(gòu)體禽作,指定相應(yīng)文件操作的方法
/kernel/drivers/android/binder.c 4173行
static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
1.3.3 binder_open()函數(shù)解析
開(kāi)打binder驅(qū)動(dòng)設(shè)備
/kernel/drivers/android/binder.c 3456行
static int binder_open(struct inode *nodp, struct file *filp)
{
// binder進(jìn)程
struct binder_proc *proc;
struct binder_device *binder_dev;
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
//為binder_proc結(jié)構(gòu)體在再分配kernel內(nèi)存空間
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
get_task_struct(current);
//當(dāng)前線程的task保存在binder進(jìn)程的tsk
proc->tsk = current;
proc->vma_vm_mm = current->mm;
//初始化todo列表
INIT_LIST_HEAD(&proc->todo);
//初始化wait隊(duì)列
init_waitqueue_head(&proc->wait);
// 當(dāng)前進(jìn)程的nice值轉(zhuǎn)化為進(jìn)程優(yōu)先級(jí)
proc->default_priority = task_nice(current);
binder_dev = container_of(filp->private_data, struct binder_device,miscdev);
proc->context = &binder_dev->context;
//同步鎖,因?yàn)閎inder支持多線程訪問(wèn)
binder_lock(__func__);
//BINDER_PROC對(duì)象創(chuàng)建+1
binder_stats_created(BINDER_STAT_PROC);
//將proc_node節(jié)點(diǎn)添加到binder_procs為表頭的隊(duì)列
hlist_add_head(&proc->proc_node, &binder_procs);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
//file文件指針的private_data變量指向binder_proc數(shù)據(jù)
filp->private_data = proc;
//釋放同步鎖
binder_unlock(__func__);
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
/*
* proc debug entries are shared between contexts, so
* this will fail if the process tries to open the driver
* again with a different context. The priting code will
* anyway print all contexts that a given PID has, so this
* is not a problem.
*/
proc->debugfs_entry =debugfs_create_file(strbuf,S_IRUGO,binder_debugfs_dir_entry_proc,(void *)(unsigned long)proc->pid,&binder_proc_fops);
}
return 0;
}
- 創(chuàng)建binder_proc對(duì)象,并把當(dāng)前進(jìn)程等信息保存到binder_proc對(duì)象揩页,該對(duì)象管理IPC所需的各種新并有用其他結(jié)構(gòu)體的跟結(jié)構(gòu)體旷偿;
- 再把binder_proc對(duì)象保存到文件指針filp,以及binder_proc加入到全局鏈表 binder_proc爆侣。
- Binder驅(qū)動(dòng)通過(guò)static HIST_HEAD(binder_procs)萍程;,創(chuàng)建了全局的哈希鏈表binder_procs兔仰,用于保存所有的binder_procs隊(duì)列茫负,每次創(chuàng)建的binder_proc對(duì)象都會(huì)加入binder_procs鏈表中。
1.3.4 binder_mmap()函數(shù)解析
- binder_mmap(文件描述符乎赴,用戶虛擬內(nèi)存空間)
- 主要功能:
- 首先在內(nèi)核虛擬地址空間忍法,申請(qǐng)一塊與用戶虛擬內(nèi)存相同的大小的內(nèi)存;
- 然后申請(qǐng)1個(gè)page大小的物理內(nèi)存无虚,再將同一塊物理內(nèi)存分別映射到內(nèi)核虛擬內(nèi)存空間和用戶虛擬內(nèi)存空間缔赠,從而實(shí)現(xiàn)了用戶空間的buffer與內(nèi)核空間的buffer同步操作的功能。
/kernel/drivers/android/binder.c 3357行
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
//內(nèi)核虛擬空間
struct vm_struct *area;
struct binder_proc *proc = filp->private_data;
const char *failure_string;
struct binder_buffer *buffer;
if (proc->tsk != current)
return -EINVAL;
//保證映射內(nèi)存大小不超過(guò)4M
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
}
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
mutex_lock(&binder_mmap_lock);
if (proc->buffer) {
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
//分配一個(gè)連續(xù)的內(nèi)核虛擬空間友题,與進(jìn)程虛擬空間大小一致
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
//指向內(nèi)核虛擬空間的地址
proc->buffer = area->addr;
//地址便宜量=用戶空間地址-內(nèi)核空間地址
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
// 釋放鎖
mutex_unlock(&binder_mmap_lock);
#ifdef CONFIG_CPU_CACHE_VIPT
if (cache_is_vipt_aliasing()) {
while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
pr_info("binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
vma->vm_start += PAGE_SIZE;
}
}
#endif
//分配物理頁(yè)的指針數(shù)組,大小等于用戶虛擬內(nèi)存/4K
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
if (proc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
proc->buffer_size = vma->vm_end - vma->vm_start;
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
// 分配物理頁(yè)面戴质,同時(shí)映射到內(nèi)核空間和進(jìn)程空間度宦,目前只分配1個(gè)page的物理頁(yè)
if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
ret = -ENOMEM;
failure_string = "alloc small buf";
goto err_alloc_small_buf_failed;
}
// binder_buffer對(duì)象,指向proc的buffer地址
buffer = proc->buffer;
//創(chuàng)建進(jìn)程的buffers鏈表頭
INIT_LIST_HEAD(&proc->buffers);
//將binder_buffer地址 加入到所屬進(jìn)程的buffer隊(duì)列
list_add(&buffer->entry, &proc->buffers);
buffer->free = 1;
//將空閑的buffer放入proc->free_buffer中
binder_insert_free_buffer(proc, buffer);
// 異步可用空間大小為buffer總體大小的一半
proc->free_async_space = proc->buffer_size / 2;
barrier();
proc->files = get_files_struct(current);
proc->vma = vma;
proc->vma_vm_mm = vma->vm_mm;
/*pr_info("binder_mmap: %d %lx-%lx maps %p\n",
proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
return 0;
//錯(cuò)誤跳轉(zhuǎn)
err_alloc_small_buf_failed:
kfree(proc->pages);
proc->pages = NULL;
err_alloc_pages_failed:
mutex_lock(&binder_mmap_lock);
vfree(proc->buffer);
proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
mutex_unlock(&binder_mmap_lock);
err_bad_arg:
pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
binder_mmap通過(guò)加鎖告匠,保證一次只有一個(gè)進(jìn)程分享內(nèi)存戈抄,保證多進(jìn)程間的并發(fā)訪問(wèn)。其中user_buffer_offset是虛擬進(jìn)程地址與虛擬內(nèi)核地址的差值后专,也就是說(shuō)同一物理地址划鸽,當(dāng)內(nèi)核地址為kernel_addr,則進(jìn)程地址為proc_addr=kernel_addr+user_buffer_offset。
1.3.4.1 binder_update_page_range()函數(shù)解析
/kernel/drivers/android/binder.c 567行
static int binder_update_page_range(struct binder_proc *proc, int allocate,
void *start, void *end,
struct vm_area_struct *vma)
{
void *page_addr;
unsigned long user_page_addr;
struct page **page;
struct mm_struct *mm;
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: %s pages %p-%p\n", proc->pid,
allocate ? "allocate" : "free", start, end);
if (end <= start)
return 0;
trace_binder_update_page_range(proc, allocate, start, end);
if (vma)
mm = NULL;
else
mm = get_task_mm(proc->tsk);
if (mm) {
down_write(&mm->mmap_sem);
vma = proc->vma;
if (vma && mm != proc->vma_vm_mm) {
pr_err("%d: vma mm and task mm mismatch\n",
proc->pid);
vma = NULL;
}
}
if (allocate == 0)
goto free_range;
if (vma == NULL) {
pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n",
proc->pid);
goto err_no_vma;
}
// **************** 這里是重點(diǎn)********************
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
int ret;
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
BUG_ON(*page);
// 分配物理內(nèi)存
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
if (*page == NULL) {
pr_err("%d: binder_alloc_buf failed for page at %p\n",
proc->pid, page_addr);
goto err_alloc_page_failed;
}
// 物理空間映射到內(nèi)核空間
ret = map_kernel_range_noflush((unsigned long)page_addr,
PAGE_SIZE, PAGE_KERNEL, page);
flush_cache_vmap((unsigned long)page_addr,
(unsigned long)page_addr + PAGE_SIZE);
if (ret != 1) {
pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",
proc->pid, page_addr);
goto err_map_kernel_failed;
}
user_page_addr =
(uintptr_t)page_addr + proc->user_buffer_offset;
// 物理空間映射到虛擬進(jìn)程空間
ret = vm_insert_page(vma, user_page_addr, page[0]);
if (ret) {
pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
proc->pid, user_page_addr);
goto err_vm_insert_page_failed;
}
/* vm_insert_page does not seem to increment the refcount */
}
if (mm) {
up_write(&mm->mmap_sem);
mmput(mm);
}
return 0;
free_range:
for (page_addr = end - PAGE_SIZE; page_addr >= start;
page_addr -= PAGE_SIZE) {
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
if (vma)
zap_page_range(vma, (uintptr_t)page_addr +
proc->user_buffer_offset, PAGE_SIZE);
err_vm_insert_page_failed:
unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
err_map_kernel_failed:
__free_page(*page);
*page = NULL;
err_alloc_page_failed:
;
}
err_no_vma:
if (mm) {
up_write(&mm->mmap_sem);
mmput(mm);
}
return -ENOMEM;
}
主要工作可用下面的圖來(lái)表達(dá):
- binder_update_page_range 主要完成工作:分配物理內(nèi)存空間裸诽,將物理空間映射到內(nèi)核空間嫂用,將物理空間映射到進(jìn)程空間。當(dāng)然
- binder_update_page_range 既可以分配物理頁(yè)面丈冬,也可以釋放物理頁(yè)面
1.3.5 binder_ioctl()函數(shù)解析
在分析binder_ioctl()函數(shù)之前嘱函,建議大家看下我的上篇文章Android跨進(jìn)程通信IPC之10——Binder相關(guān)結(jié)構(gòu)體簡(jiǎn)介
了解相關(guān)的結(jié)構(gòu)體,為了便于查找埂蕊,這些結(jié)構(gòu)體之間都留有字段的存儲(chǔ)關(guān)聯(lián)的結(jié)構(gòu)往弓,下面的這幅圖描述了這里說(shuō)到的內(nèi)容
- binder_ioctl()函數(shù)負(fù)責(zé)在兩個(gè)進(jìn)程間收發(fā)IPC數(shù)據(jù)和IPC reply數(shù)據(jù)
- ioctl(文件描述符,ioctl命令蓄氧,數(shù)據(jù)類型)
- ioctl文件描述符函似,是通過(guò)open()方法打開(kāi)Binder Driver后返回值。
- ioctl命令和數(shù)據(jù)類型是一體喉童,不同的命令對(duì)應(yīng)不同的數(shù)據(jù)類型
下面這些命令中BINDER_WRITE_READ使用最為頻繁撇寞,也是ioctl的最為核心的命令。
//kernel/drivers/android/binder.c 3239行
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
//binder線程
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
/*pr_info("binder_ioctl: %d:%d %x %lx\n",
proc->pid, current->pid, cmd, arg);*/
if (unlikely(current->mm != proc->vma_vm_mm)) {
pr_err("current mm mismatch proc mm\n");
return -EINVAL;
}
trace_binder_ioctl(cmd, arg);
//進(jìn)入休眠狀態(tài)泄朴,直到中斷喚醒
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
binder_lock(__func__);
//獲取binder_thread
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
switch (cmd) {
//進(jìn)行binder的讀寫操作
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
//設(shè)置binder的最大支持線程數(shù)
case BINDER_SET_MAX_THREADS:
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
//設(shè)置binder的context管理者重抖,也就是servicemanager稱為守護(hù)進(jìn)程
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
//當(dāng)binder線程退出,釋放binder線程
case BINDER_THREAD_EXIT:
binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
proc->pid, thread->pid);
binder_free_thread(proc, thread);
thread = NULL;
break;
//獲取binder的版本號(hào)
case BINDER_VERSION: {
struct binder_version __user *ver = ubuf;
if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
default:
ret = -EINVAL;
goto err;
}
ret = 0;
err:
if (thread)
thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
binder_unlock(__func__);
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret && ret != -ERESTARTSYS)
pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}
1.3.5.1 binder_get_thread()函數(shù)解析
從binder_proc中查找binder_thread祖灰,如果當(dāng)前線程已經(jīng)加入到proc的線程隊(duì)列則直接返回钟沛,如果不存在則創(chuàng)建binder_thread,并將當(dāng)前線程添加到當(dāng)前的proc
//kernel/drivers/android/binder.c 3026行
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
struct rb_node **p = &proc->threads.rb_node;
//根據(jù)當(dāng)前進(jìn)程的pid局扶,從binder_proc中查找相應(yīng)的binder_thread
while (*p) {
parent = *p;
thread = rb_entry(parent, struct binder_thread, rb_node);
if (current->pid < thread->pid)
p = &(*p)->rb_left;
else if (current->pid > thread->pid)
p = &(*p)->rb_right;
else
break;
}
if (*p == NULL) {
// 新建binder_thread結(jié)構(gòu)體
thread = kzalloc(sizeof(*thread), GFP_KERNEL);
if (thread == NULL)
return NULL;
binder_stats_created(BINDER_STAT_THREAD);
thread->proc = proc;
//保持當(dāng)前進(jìn)程(線程)的pid
thread->pid = current->pid;
init_waitqueue_head(&thread->wait);
INIT_LIST_HEAD(&thread->todo);
rb_link_node(&thread->rb_node, parent, p);
rb_insert_color(&thread->rb_node, &proc->threads);
thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
thread->return_error = BR_OK;
thread->return_error2 = BR_OK;
}
return thread;
}
1.3.5.2 binder_ioctl_write_read()函數(shù)解析
對(duì)于ioctl()方法中恨统,傳遞進(jìn)來(lái)的命令是cmd = BINDER_WRITE_READ時(shí)執(zhí)行該方法,arg是一個(gè)binder_write_read結(jié)構(gòu)體
/kernel/drivers/android/binder.c 3134行
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
if (size != sizeof(struct binder_write_read)) {
ret = -EINVAL;
goto out;
}
//把用戶控件數(shù)據(jù)ubuf拷貝到bwr
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d write %lld at %016llx, read %lld at %016llx\n",
proc->pid, thread->pid,
(u64)bwr.write_size, (u64)bwr.write_buffer,
(u64)bwr.read_size, (u64)bwr.read_buffer);
//如果寫緩存中有數(shù)據(jù)
if (bwr.write_size > 0) {
//執(zhí)行binder寫操作
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
//如果寫失敗三妈,再將bwr數(shù)據(jù)寫回用戶空間畜埋,并返回
if (ret < 0) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
//當(dāng)讀緩存有數(shù)據(jù)
if (bwr.read_size > 0) {
//執(zhí)行binder讀操作
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait);
//如果讀失敗
if (ret < 0) {
//當(dāng)讀失敗,再將bwr數(shù)據(jù)寫回用戶空間畴蒲,并返回
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d wrote %lld of %lld, read return %lld of %lld\n",
proc->pid, thread->pid,
(u64)bwr.write_consumed, (u64)bwr.write_size,
(u64)bwr.read_consumed, (u64)bwr.read_size);
//將內(nèi)核數(shù)據(jù)bwr拷貝到用戶空間ubuf
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
out:
return ret;
}
- 1 首先把用戶空間的數(shù)據(jù)拷貝到內(nèi)核空間bwr
- 2 其次當(dāng)bwr寫緩存中有數(shù)據(jù)悠鞍,則執(zhí)行binder寫操作。如果寫失敗模燥,則再將bwr數(shù)據(jù)寫回用戶控件咖祭,并退出。
- 3 再次當(dāng)bwr讀緩存中有數(shù)據(jù)蔫骂,則執(zhí)行binder讀緩存么翰;當(dāng)讀失敗,再將bwr數(shù)據(jù)寫會(huì)用戶空間辽旋,并退出浩嫌。
- 4 最后把內(nèi)核數(shù)據(jù)拷貝到用戶空間檐迟。
1.3.6 總結(jié)
本章主要講解了binder驅(qū)動(dòng)的的四大功能點(diǎn)
- 1 binder_init :初始化字符設(shè)備
- 2 binder_open:打開(kāi)驅(qū)動(dòng)設(shè)備,過(guò)程需要持有binder_main_lock同步鎖
- 3 binder_mmap: 申請(qǐng)內(nèi)存空間码耐,該過(guò)程需要持有binder_mmap_lock同步鎖追迟;
- 4 binder_ioctl:執(zhí)行相應(yīng)的io操作,該過(guò)程需要持有binder_main_lock同步鎖伐坏;當(dāng)處于binder_thread_read過(guò)程怔匣,卻讀緩存無(wú)數(shù)據(jù)則會(huì)先釋放該同步鎖,并處于wait_event_freezable過(guò)程桦沉,等有數(shù)據(jù)到來(lái)則喚醒并嘗試持有同步鎖每瞒。
2 Binder驅(qū)動(dòng)通信
2.1 Binder驅(qū)動(dòng)通信簡(jiǎn)述
- Client進(jìn)程通過(guò)RPC(Remote Procedure Call Protocol) 與Server通信,可以簡(jiǎn)單地劃分為三層: 1纯露、驅(qū)動(dòng)層 2剿骨、IPC層 3、業(yè)務(wù)層埠褪。下圖的doAction()便是Client與Server共同協(xié)商好的統(tǒng)一方法浓利;其中handle、RPC數(shù)據(jù)钞速、代碼贷掖、協(xié)議、這4項(xiàng)組成了IPC層的數(shù)據(jù)渴语,通過(guò)IPC層進(jìn)行數(shù)據(jù)傳輸苹威;而真正在Client和Server兩端建立通信的基礎(chǔ)是Binder Driver
模型如下圖:
2.2 Binder驅(qū)動(dòng)通信協(xié)議
Binder協(xié)議包含在IPC數(shù)據(jù)中,分為兩類:
- BINDER_COMMAND_PROTOCOL:binder請(qǐng)求碼驾凶,以"BC_" 開(kāi)頭牙甫,簡(jiǎn)稱"BC碼",從IPC層傳遞到 Binder Driver層调违;
- BINDER_RETURN_PROTOCOL: binder響應(yīng)碼窟哺,以"BR_"開(kāi)頭,簡(jiǎn)稱"BR碼"技肩,用于從BinderDirver層傳遞到IPC層
Binder IPC 通信至少是兩個(gè)進(jìn)程的交互:
- client進(jìn)程執(zhí)行binder_thread_write且轨,根據(jù)BC_XXX 命令,生成相應(yīng)的binder_work虚婿;
- server進(jìn)程執(zhí)行binder_thread_read殖告,根據(jù)binder_work.type類型,生成BR_XXX雳锋,發(fā)送用戶處理。
如下圖:
其中binder_work.type共有6種類型
//kernel/drivers/android/binder.c 240行
struct binder_work {
struct list_head entry;
enum {
BINDER_WORK_TRANSACTION = 1
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
} type;
};
這里用到了上面提到兩個(gè)函數(shù)一個(gè)是binder_thread_write()函數(shù)羡洁,另一個(gè)是binder_thread_read函數(shù)罵我們就來(lái)詳細(xì)研究下:
2.3 通信函數(shù)
2.3.1 binder_thread_write() 函數(shù)詳解
- 請(qǐng)求處理過(guò)程是通過(guò)binder_thread_write()函數(shù)玷过,該方法用于處理Binder協(xié)議的請(qǐng)求碼。當(dāng)binder_buffer存在數(shù)據(jù),binder線程的寫操作循環(huán)執(zhí)行
代碼在 kernel/drivers/android/binder.c 2248行辛蚊。 - 代碼太多了就不全部粘貼了粤蝎,只粘貼重點(diǎn)部分,代碼如下:
static int binder_thread_write(){
while (ptr < end && thread->return_error == BR_OK) {
//獲取IPC數(shù)據(jù)中的Binder協(xié)議的BC碼
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
switch (cmd) {
case BC_INCREFS: ...
case BC_ACQUIRE: ...
case BC_RELEASE: ...
case BC_DECREFS: ...
case BC_INCREFS_DONE: ...
case BC_ACQUIRE_DONE: ...
case BC_FREE_BUFFER: ...
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
//拷貝用戶控件tr到內(nèi)核
copy_from_user(&tr, ptr, sizeof(tr))袋马;
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
break;
case BC_REGISTER_LOOPER: ...
case BC_ENTER_LOOPER: ...
case BC_EXIT_LOOPER: ...
case BC_REQUEST_DEATH_NOTIFICATION: ...
case BC_CLEAR_DEATH_NOTIFICATION: ...
case BC_DEAD_BINDER_DONE: ...
}
}
}
}
對(duì)于 請(qǐng)求碼為BC_TRANSCATION或BC_REPLY時(shí)初澎,會(huì)執(zhí)行binder_transaction()方法,這是最頻繁的操作虑凛。那我們一起來(lái)看下binder_transaction()函數(shù)碑宴。
2.3.1.1 binder_transaction() 函數(shù)詳解
這塊的代碼依舊很多,我就只粘貼重點(diǎn)了桑谍,全部代碼在/kernel/drivers/android/binder.c 1827行延柠。
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply){
//根絕各種判斷,獲取如下信息:
//目標(biāo)進(jìn)程
struct binder_proc *target_proc锣披;
// 目標(biāo)線程
struct binder_thread *target_thread贞间;
// 目標(biāo)binder節(jié)點(diǎn)
struct binder_node *target_node;
//目標(biāo)TODO隊(duì)列
struct list_head *target_list雹仿;
// 目標(biāo)等待隊(duì)列
wait_queue_head_t *target_wait增热;
//*** 省略部分代碼 ***
//分配兩個(gè)結(jié)構(gòu)體內(nèi)存
t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_t_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION);
*tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
//*** 省略部分代碼 ***
//從target_proc分配一塊buffer
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
//*** 省略部分代碼 ***
for (; offp < off_end; offp++) {
//*** 省略部分代碼 ***
switch (fp->type) {
case BINDER_TYPE_BINDER: ...
case BINDER_TYPE_WEAK_BINDER: ...
case BINDER_TYPE_HANDLE: ...
case BINDER_TYPE_WEAK_HANDLE: ...
case BINDER_TYPE_FD: ...
}
}
//分別給target_list和當(dāng)前線程TODO隊(duì)列插入事務(wù)
t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list);
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
list_add_tail(&tcomplete->entry, &thread->todo);
if (target_wait)
wake_up_interruptible(target_wait);
return;
}
replay的過(guò)程會(huì)找到 target_thread,非reply則一般找到target_proc胧辽,對(duì)于特殊的嵌套binder call會(huì)根據(jù)transaction_stack來(lái)決定是否插入事物到目標(biāo)進(jìn)程峻仇。
2.3.1.2 BC_PROTOCOL 請(qǐng)求碼
Binder請(qǐng)求碼,在binder.h里面的366行票顾。是用binder_driver_command_protocol 來(lái)定義的础浮,是用于應(yīng)用程序向Binder驅(qū)動(dòng)設(shè)備發(fā)送請(qǐng)求消息,應(yīng)用程序包含Client端和Server端奠骄,以BC_開(kāi)頭豆同,供給17條 ( - 表示目前不支持請(qǐng)求碼)
重點(diǎn)說(shuō)幾個(gè):
- BC_FREE_BUFFER:通過(guò)mmap()映射內(nèi)存,其中ServiceMananger映射的空間大小為128K含鳞,其他Binder應(yīng)用的進(jìn)程映射的內(nèi)存大小為8K-1M影锈,Binder驅(qū)動(dòng)基于這塊映射的內(nèi)存采用最佳匹配算法來(lái)動(dòng)態(tài)分配和釋放,通過(guò)binder_buffer結(jié)構(gòu)體中free字段來(lái)表示相應(yīng)的buffer是空閑還是已分配狀態(tài)蝉绷。對(duì)于已分配的buffer加入binder_proc中的allocated_buffers紅黑樹(shù)鸭廷;對(duì)于空閑的buffers加入到binder_proch中的free_buffer紅黑樹(shù)。當(dāng)應(yīng)用程序需要內(nèi)存時(shí)熔吗,根據(jù)所需內(nèi)存大小從free_buffers中找到最合適的內(nèi)存辆床,并放入allocated_buffers樹(shù);當(dāng)應(yīng)用程序處理完后桅狠,必須盡快用BC_FREE_BUFFER命令來(lái)釋放該buffer讼载,從而添加回free_buffers樹(shù)中轿秧。
- BC_INCREFS、BC_ACQUIRE咨堤、BC_RELEASE菇篡、BC_DECREFS等請(qǐng)求碼的作用是對(duì)binder的 強(qiáng)弱引用的技術(shù)操作,用于實(shí)現(xiàn)強(qiáng)/弱 指針的功能一喘。
- 對(duì)于參數(shù)類型 binder_ptr_cookie是由binder指針和cookie組成
- 對(duì)于參數(shù)類型 binder_ptr_cookie是由binder指針和cookie組成
- BC_ENTER_LOOPER: binder主線程(由應(yīng)用層發(fā)起)的創(chuàng)建會(huì)向驅(qū)動(dòng)發(fā)送該消息驱还;joinThreadPool()過(guò)程創(chuàng)建binder主線程;
- BC_REGISTER_LOOPER: Binder用于驅(qū)動(dòng)層決策而創(chuàng)建新的binder線程凸克;joinThreadPool()過(guò)程议蟆,創(chuàng)建非binder主線程;
- BC_EXIT_LOOPER:退出Binder線程触徐,對(duì)于binder主線程是不能退出的咪鲜;joinThreadPool的過(guò)程出現(xiàn)timeout,并且非binder主線程撞鹉,則會(huì)退出該binder線程疟丙。
2.3.1.3 binder_thread_read()函數(shù)詳解
響應(yīng)處理過(guò)程是通過(guò)binder_thread_read()函數(shù),該方法根據(jù)不同的binder_work->type以及不同的狀態(tài)生成不同的響應(yīng)碼
代碼太多了鸟雏,我只截取了部分代碼享郊,具體代碼在/kernel/drivers/android/binder.c 2650行
static int binder_thread_read(){
//*** 省略部分代碼 ***
// 根據(jù) wait_for_proc_work 來(lái)決定wait在當(dāng)前線程還是進(jìn)程的等待隊(duì)列
if (wait_for_proc_work) {
//*** 省略部分代碼 ***
ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
//*** 省略部分代碼 ***
} else {
//*** 省略部分代碼 ***
ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
//*** 省略部分代碼 ***
}
//*** 省略部分代碼 ***
while (1) {
//*** 省略部分代碼 ***
//當(dāng)&thread->todo和&proc->todo都為空時(shí),goto到retry標(biāo)志處孝鹊,否則往下執(zhí)行:
if (!list_empty(&thread->todo)) {
w = list_first_entry(&thread->todo, struct binder_work,entry);
} else if (!list_empty(&proc->todo) && wait_for_proc_work) {
w = list_first_entry(&proc->todo, struct binder_work,entry);
} else {
/* no data added */
if (ptr - buffer == 4 &&
!(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
goto retry;
break;
}
struct binder_transaction_data tr;
struct binder_transaction *t = NULL;
switch (w->type) {
case BINDER_WORK_TRANSACTION: ...
case BINDER_WORK_TRANSACTION_COMPLETE: ...
case BINDER_WORK_NODE: ...
case BINDER_WORK_DEAD_BINDER: ...
case BINDER_WORK_DEAD_BINDER_AND_CLEAR: ...
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: ...
}
...
}
done:
*consumed = ptr - buffer;
//當(dāng)滿足請(qǐng)求線程 加上 已準(zhǔn)備線程數(shù) 等于0 并且 其已啟動(dòng)線程小于最大線程數(shù)15炊琉,并且looper狀態(tài)為已注冊(cè)或已進(jìn)入時(shí)創(chuàng)建新的線程。
if (proc->requested_threads + proc->ready_threads == 0 &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
/*spawn a new thread if we leave this out */) {
proc->requested_threads++;
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BR_SPAWN_LOOPER\n",
proc->pid, thread->pid);
//生成BR_SPAWN_LOOPER命令又活,用于創(chuàng)建新的線程
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
}
return 0;
}
當(dāng)transaction堆棧為空苔咪,且線程todo鏈表為空,且non_block=false時(shí)柳骄,意味著沒(méi)有任務(wù)事物需要處理团赏,會(huì)進(jìn)入等待客戶端請(qǐng)求的狀態(tài)。當(dāng)有事物需要處理時(shí)便會(huì)進(jìn)入循環(huán)處理過(guò)程耐薯,并生成相應(yīng)的響應(yīng)碼舔清。
在Binder驅(qū)動(dòng)層面,只有進(jìn)入binder_thread_read()方法時(shí)曲初,同時(shí)滿足以下條件体谒,才會(huì)生成BR_SPAWN_LOOPER命令,當(dāng)用戶態(tài)進(jìn)程收到該命令則會(huì)創(chuàng)新新線程:
- binder_proc的requested_threads線程數(shù)為0
- binder_proc的ready_threads線程數(shù)為0
- binder_proc的requested_threads_started個(gè)數(shù)小于15(即最大線程個(gè)數(shù))
- binder_thread的looper狀態(tài)為BINDER_LOOPER_STATE_REGISTERED或者BINDER_LOOPER_STATE_ENTERED
- 那么問(wèn)題來(lái)了臼婆,什么時(shí)候處理的響應(yīng)碼抒痒?通過(guò)上面的Binder通信協(xié)議圖,可以知道處理響應(yīng)碼的過(guò)程是在用戶態(tài)處理颁褂,下一篇文章會(huì)講解到用戶控件IPCThreadState類中IPCThreadState::waitForResponse()和IPCThreadState::executeCommand()兩個(gè)方法共同處理Binder協(xié)議的18個(gè)響應(yīng)碼
2.3.1.4 BR_PROTOCOL 響應(yīng)碼
- binder響應(yīng)碼评汰,在binder.h里面的278行是用enum binder_driver_return_protocol來(lái)定義的纷捞,是binder設(shè)備向應(yīng)用程序回復(fù)的消息,應(yīng)用程序包含Client端和Serve端被去,以BR_開(kāi)頭,合計(jì)18條奖唯。
幾個(gè)難點(diǎn):
- BR_SPAWN_LOOPER:binder驅(qū)動(dòng)已經(jīng)檢測(cè)到進(jìn)程中沒(méi)有線程等待即將到來(lái)的事物惨缆。那么當(dāng)一個(gè)進(jìn)程接收到這條命令時(shí),該進(jìn)程必須創(chuàng)建一條新的服務(wù)線程并注冊(cè)該線程丰捷,在接下來(lái)的響應(yīng)過(guò)程會(huì)看到合何時(shí)生成該響應(yīng)嘛
- BR_TRANSACTION_COMPLETE:當(dāng)Client端向Binder驅(qū)動(dòng)發(fā)送BC_TRANSACTION命令后坯墨,Client會(huì)受到BR_TRANSACTION_COMPLETE命令,告知Client端請(qǐng)求命令發(fā)送成功能病往;對(duì)于Server向Binder驅(qū)動(dòng)發(fā)送BC_REPLY命令后捣染,Server端會(huì)受到BR_TRANSACTION_COMPLETE命令,告知Server端請(qǐng)求回應(yīng)命令發(fā)送成功停巷。
- BR_READ_REPLY: 當(dāng)應(yīng)用層向Binder驅(qū)動(dòng)發(fā)送Binder調(diào)用時(shí)耍攘,若Binder應(yīng)用層的另一個(gè)端已經(jīng)死亡,則驅(qū)動(dòng)回應(yīng)BR_DEAD_BINDER命令畔勤。
- BR_FAILED_REPLY:當(dāng)應(yīng)用層向Binder驅(qū)動(dòng)發(fā)送Binder調(diào)用是蕾各,若transcation出錯(cuò),比如調(diào)用的函數(shù)號(hào)不存在庆揪,則驅(qū)動(dòng)回應(yīng)BR_FAILED_REPLY式曲。
3 Binder內(nèi)存
Binder內(nèi)存我主要分3個(gè)內(nèi)容來(lái)依次講解,分別為:
- mmap機(jī)制
- 內(nèi)存分配
- 內(nèi)存釋放
3.1 mmap機(jī)制
上面從代碼的角度闡釋了binder_mmap缸榛,也是Binder進(jìn)程通信效率高的核心機(jī)制所在吝羞,如下圖:
- 虛擬進(jìn)程地址空間(vm_area_struct)和虛擬內(nèi)核地址空間(vm_struct)都映射到同一塊物理內(nèi)存空間。
- 當(dāng)Client端與Server端發(fā)送數(shù)據(jù)時(shí)内颗,Client(作為數(shù)據(jù)發(fā)送端)先從自己的進(jìn)程空間把IPC通信數(shù)據(jù)copy_from_user拷貝到內(nèi)核空間钧排,而Server端(作為數(shù)據(jù)接收端)與內(nèi)核共享數(shù)據(jù),不再需要拷貝數(shù)據(jù)起暮,而是通過(guò)內(nèi)存地址空間的偏移量卖氨,即可獲悉內(nèi)存地址,整個(gè)過(guò)程只要發(fā)生一次內(nèi)存拷貝负懦。
- 對(duì)于進(jìn)程和內(nèi)核虛擬地址映射到同一個(gè)物理內(nèi)存的操作是發(fā)生在數(shù)據(jù)接收端筒捺,而數(shù)據(jù)發(fā)送端還是需要將用戶態(tài)的數(shù)據(jù)復(fù)制到內(nèi)核態(tài)。
3.2 內(nèi)存分配
- Binder內(nèi)存分配方法通過(guò)binder_alloc_buf()方法纸厉,內(nèi)存管理單元為binder_buffer結(jié)構(gòu)體系吭,只有在binder_transaction過(guò)程中才需要分配buffer。
- 具體代碼在/kernel/drivers/android/binder.c 678行颗品,我選取了重點(diǎn)部分
static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc,
size_t data_size, size_t offsets_size, int is_async)
{
//*** 省略部分代碼 ***
//如果不存在虛擬地址空間為肯尺,則直接返回
if (proc->vma == NULL) {
pr_err("%d: binder_alloc_buf, no vma\n",
proc->pid);
return NULL;
}
data_offsets_size = ALIGN(data_size, sizeof(void *)) +
ALIGN(offsets_size, sizeof(void *));
//非法的size排截,直接返回
if (data_offsets_size < data_size || data_offsets_size < offsets_size) {
binder_user_error("%d: got transaction with invalid size %zd-%zd\n",
proc->pid, data_size, offsets_size);
return NULL;
}
size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
//非法的size脑蠕,直接返回
if (size < data_offsets_size || size < extra_buffers_size) {
binder_user_error("%d: got transaction with invalid extra_buffers_size %zd\n",
proc->pid, extra_buffers_size);
return NULL;
}
//如果 剩余的異步空間太少,以至于滿足需求,也直接返回
if (is_async &&
proc->free_async_space < size + sizeof(struct binder_buffer)) {
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: binder_alloc_buf size %zd failed, no async space left\n",
proc->pid, size);
return NULL;
}
//從binder_buffer的紅黑樹(shù)叢中宦搬,查找大小相等的buffer塊
while (n) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
BUG_ON(!buffer->free);
buffer_size = binder_buffer_size(proc, buffer);
if (size < buffer_size) {
best_fit = n;
n = n->rb_left;
} else if (size > buffer_size)
n = n->rb_right;
else {
best_fit = n;
break;
}
}
//如果內(nèi)存分配失敗,地址為空
if (best_fit == NULL) {
pr_err("%d: binder_alloc_buf size %zd failed, no address space\n",
proc->pid, size);
return NULL;
}
if (n == NULL) {
buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
buffer_size = binder_buffer_size(proc, buffer);
}
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: binder_alloc_buf size %zd got buffer %p size %zd\n",
proc->pid, size, buffer, buffer_size);
has_page_addr =
(void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK);
if (n == NULL) {
if (size + sizeof(struct binder_buffer) + 4 >= buffer_size)
buffer_size = size; /* no room for other buffers */
else
buffer_size = size + sizeof(struct binder_buffer);
}
end_page_addr =
(void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size);
if (end_page_addr > has_page_addr)
end_page_addr = has_page_addr;
if (binder_update_page_range(proc, 1,
(void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL))
return NULL;
rb_erase(best_fit, &proc->free_buffers);
buffer->free = 0;
binder_insert_allocated_buffer(proc, buffer);
if (buffer_size != size) {
struct binder_buffer *new_buffer = (void *)buffer->data + size;
list_add(&new_buffer->entry, &buffer->entry);
new_buffer->free = 1;
binder_insert_free_buffer(proc, new_buffer);
}
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: binder_alloc_buf size %zd got %p\n",
proc->pid, size, buffer);
buffer->data_size = data_size;
buffer->offsets_size = offsets_size;
buffer->extra_buffers_size = extra_buffers_size;
buffer->async_transaction = is_async;
if (is_async) {
proc->free_async_space -= size + sizeof(struct binder_buffer);
binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
"%d: binder_alloc_buf size %zd async free %zd\n",
proc->pid, size, proc->free_async_space);
}
return buffer;
}
3.3 內(nèi)存釋放
內(nèi)存釋放相關(guān)函數(shù):
- binder_free_buf() : 在/kernel/drivers/android/binder.c 847行
- binder_delete_free_buffer():在/kernel/drivers/android/binder.c 802行
- binder_transaction_buffer_release():在/kernel/drivers/android/binder.c 1430行
4 附錄:關(guān)于misc
4.1 linux子系統(tǒng)-miscdevice
雜項(xiàng)設(shè)備(miscdevice) 是嵌入式系統(tǒng)中用的比較多的一種設(shè)備類型朗恳。
在Linux驅(qū)動(dòng)中把無(wú)法歸類的五花八門的設(shè)備定義為混雜設(shè)備(用miscdevice結(jié)構(gòu)體表述)
Linux內(nèi)核所提供的miscdevice有很強(qiáng)的包容性鳍烁,各種無(wú)法歸結(jié)為標(biāo)準(zhǔn)字符設(shè)備的類型都可以定義為miscdevice,譬如NVRAM,看門狗敬扛,實(shí)時(shí)時(shí)鐘晰洒,字符LCD等,就像一組大雜燴啥箭。
在Linux內(nèi)核里把所有misc設(shè)備組織在一起谍珊,構(gòu)成一個(gè)子系統(tǒng)(subsys),統(tǒng)一進(jìn)行管理急侥。在這個(gè)子系統(tǒng)里的所有miscdevice類型的設(shè)備共享一個(gè)主設(shè)備號(hào)MISC_MAJOR(即10)砌滞,這次設(shè)備號(hào)不同。
在內(nèi)核中用struct miscdevice表示miscdevice設(shè)備缆巧,具體的定義在內(nèi)核頭文件"include/linux/miscdevice.h"中
//https://github.com/torvalds/linux/blob/master/include/linux/miscdevice.h 63行
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
- miscdevice的API的實(shí)現(xiàn)在drivers/char/misc.c中布持,misc子系統(tǒng)的初始化,以及misc設(shè)備的注冊(cè)陕悬,注銷等接口都實(shí)現(xiàn)在這個(gè)文件中题暖。
- 通過(guò)閱讀這個(gè)文件,miscdevice類型的設(shè)備實(shí)際上就是對(duì)字符設(shè)備的簡(jiǎn)單封裝捉超,最直接的證據(jù)可以看misdevice子系統(tǒng)的初始化函數(shù)misc_init()胧卤,在這個(gè)函數(shù)里,撇開(kāi)其他代碼不看拼岳,其中有如下兩行關(guān)鍵代碼:
//drivers/char/misc.c 279行
...
misc_class = class_create(THIS_MODULE, "misc");
...
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
代碼解析:
- 第一行枝誊,創(chuàng)建了一個(gè)名字叫misc的類,具體表現(xiàn)是在/sys/class目錄下創(chuàng)建一個(gè)名為misc的目錄惜纸。以后美注冊(cè)一個(gè)自己的miscdevice都會(huì)在該目錄下新建一項(xiàng)叶撒。
- 第二行,調(diào)用register_chrdev為給定的主設(shè)備號(hào)MISC_MAJOR(10)注冊(cè)0~255供256個(gè)次設(shè)備號(hào)耐版,并為每個(gè)設(shè)備建立一個(gè)對(duì)應(yīng)的默認(rèn)的cdev結(jié)構(gòu)祠够,該函數(shù)是2.6內(nèi)核之前的老函數(shù),現(xiàn)在已經(jīng)不建議使用了粪牲。由此可見(jiàn)misc設(shè)備其實(shí)也就是主設(shè)備號(hào)是MISC_MAJOR(10)的字符設(shè)備古瓤。從面相對(duì)象的角度來(lái)看,字符設(shè)備類是misc設(shè)備類的父類。同時(shí)我們也主要注意到采用這個(gè)函數(shù)注冊(cè)后實(shí)際上系統(tǒng)最多支持有255個(gè)驅(qū)動(dòng)自定義的雜項(xiàng)設(shè)備落君,因?yàn)殡s項(xiàng)設(shè)備子系統(tǒng)模塊自己占用了一個(gè)次設(shè)備號(hào)0穿香。查看源文件可知,目前內(nèi)核里已經(jīng)被預(yù)先使用的子設(shè)備號(hào)定義在include/linux/miscdevice.h的開(kāi)頭
- 在這個(gè)子系統(tǒng)中所有的miscdevice設(shè)備形成了一個(gè)鏈表绎速,他們共享一個(gè)主設(shè)備號(hào)10皮获,但它們有不同的次設(shè)備號(hào)。對(duì)設(shè)備訪問(wèn)時(shí)內(nèi)核根據(jù)次設(shè)備號(hào)查找對(duì)應(yīng)的miscdevice設(shè)備朝氓。這一點(diǎn)我們可以通過(guò)閱讀misc子系統(tǒng)提供的注冊(cè)接口函數(shù)misc_register()和注銷接口函數(shù)misc_deregister()來(lái)理解魔市。
- 在這個(gè)子系統(tǒng)中所有的miscdevice設(shè)備不僅共享了主設(shè)備號(hào),還共享了一個(gè)misc_open()的文件操作方法赵哲。
//drivers/char/misc.c 161行
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
};
- 該方法結(jié)構(gòu)在注冊(cè)設(shè)備時(shí)通過(guò)register_chrdev(MISC_MAJOR,"misc",&misc_fops)傳遞給內(nèi)核。但不要以為所有的miscdevice都使用相同的文件open方法君丁,仔細(xì)閱讀misc_open()我們發(fā)現(xiàn)該函數(shù)內(nèi)部會(huì)檢查驅(qū)動(dòng)模塊自已定義的miscdevice結(jié)構(gòu)體對(duì)象的fops成員枫夺,如果不為空將其替換掉缺省的,參考函數(shù)中的new_fops = fops_get(c->fops);以及file->f_op = new_fops;語(yǔ)句绘闷。如此這般橡庞,以后內(nèi)核再調(diào)用設(shè)備文件的操作時(shí)就會(huì)調(diào)用該misc設(shè)備自己定義的文件操作函數(shù)了。這種實(shí)現(xiàn)方式有點(diǎn)類似java里面函數(shù)重載的概念印蔗。
4.2 采用miscdevice開(kāi)發(fā)設(shè)備驅(qū)動(dòng)的方法
- 第一步扒最,定義自己misc設(shè)備的文件操作函數(shù)以及file_operations結(jié)構(gòu)體對(duì)象。如下:
static const struct file_operations my_misc_drv_fops = {
.owner = THIS_MODULE,
.open = my_misc_drv_open,
.release = my_misc_drv_release,
//根據(jù)實(shí)際情況擴(kuò)充 ...
};
- 第二步华嘹,定義自己的misc設(shè)備對(duì)象吧趣,如下:
static struct miscdevice my_misc_drv_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = KBUILD_MODNAME,
.fops = &my_misc_drv_fops,
};
- .minor 如果填充MISC_DYNAMIC_MINOR,則由內(nèi)核動(dòng)態(tài)分配次設(shè)備號(hào)耙厚,否則根據(jù)你自己定義的指定强挫。
- .name 給定設(shè)備的名字,也可以直接利用內(nèi)核編譯系統(tǒng)的環(huán)境變量KBUILD_MODNAME薛躬。
- .fops設(shè)置為第一步定義的文件操作結(jié)構(gòu)體的地址
- 第三步俯渤,注銷和銷毀
驅(qū)動(dòng)模塊一般在模塊初始化函數(shù)中調(diào)用misc_register()注冊(cè)自己的misc設(shè)備。實(shí)例代碼如下:
ret = misc_register(&my_misc_drv_dev);
if (ret < 0) {
//失敗處理
}
注意在misc_register()函數(shù)中有如下語(yǔ)句
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name);
這句話配合前面在misc_init()函數(shù)中的misc_class = class_create(THIS_MODULE, "misc");
這樣型宝,class_create()創(chuàng)建了一個(gè)類八匠,而device_create()就創(chuàng)建了該類的一個(gè)設(shè)備,這些都涉及l(fā)inux內(nèi)核的設(shè)備模型和sys文件系統(tǒng)額概念趴酣,暫不展開(kāi)梨树,我們只需要知道,如此這般价卤,當(dāng)該驅(qū)動(dòng)模塊被加載(insmod)時(shí)劝萤,和內(nèi)核態(tài)的設(shè)備模型配套運(yùn)行的用戶態(tài)有個(gè)udev的后臺(tái)服務(wù)會(huì)自動(dòng)在/dev下創(chuàng)建一個(gè)驅(qū)動(dòng)模塊中注冊(cè)的misc設(shè)備對(duì)應(yīng)的設(shè)備文件節(jié)點(diǎn),其名字就是misc->name慎璧。這樣就省去了自己創(chuàng)建設(shè)備文件的麻煩床嫌。這樣也有助于動(dòng)態(tài)設(shè)備的管理跨释。
驅(qū)動(dòng)模塊可以在模塊卸載函數(shù)中調(diào)用misc_deregister()注銷自己的misc設(shè)備。實(shí)例代碼如下:
misc_deregister(&my_misc_drv_dev);
在這個(gè)函數(shù)中會(huì)自動(dòng)刪除`/dev下的同名設(shè)備文件厌处。
4.3 總結(jié)
雜項(xiàng)設(shè)備作為字符設(shè)備的封裝鳖谈,為字符設(shè)備提供簡(jiǎn)單的編程接口,如果編寫新的字符驅(qū)動(dòng)阔涉,可以考慮使用雜項(xiàng)設(shè)備接口缆娃,簡(jiǎn)單粗暴,只需要初始化一個(gè)miscdevice的結(jié)構(gòu)體設(shè)備對(duì)象瑰排,然后調(diào)用misc_register注冊(cè)就可以了贯要,不用的時(shí)候,調(diào)用misc_deregister進(jìn)行卸載椭住。