Binder Driver 初探从驱动层角度来看

news2025/1/20 12:06:00

1:驱动概述

 1.1基本简介

Binder 驱动是 Android 专用的,但底层的驱动架构与Linux 驱动一样。binder 驱动在以 misc 设备进行注册,作为虚拟字符设备,没有直接操作硬件,只是对设备内存的处理。主要是驱动设备的初始化(binder_init),打开(binder_open),映射(binder_mmap),数据操作(binder_ioctl)

1.2 系统调用

用户态的程序调用 Kernel 层驱动是需要进入内核态,进行系统调用(syscall),比如打开 Binder 驱动方法的调用链为: open-> __open() -> binder_open()。open()为用户空间的方法,__open()便是系统调用中相应的处理方法,通过查找,对应调用到内核 binder 驱动的 binder_open()方法,至于其他的从用户态进入内核态的流程也基本一致。

简单说,当用户空间调用 open()方法,最终会调用 binder 驱动的binder_open() 方法;mmap()/ioctl()方法也是同理,在 BInder 系列的后续文章从用户态进入内核态,都依赖于系统调用过程。

 2、 Binder 核心方法

     2.1 binder_init

主要工作是为了注册 misc 设备

     /drivers/android/binder.c

//基本讲解

static int __init binder_init(void) {

int ret;

//创建名为 binder 的工作队列

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);

// 注册 misc 设备【见小节 2.1.1】

ret = misc_register(&binder_miscdev); if (binder_debugfs_dir_entry_root) { ... //在 debugfs 文件系统中创建一系列的文件 }

return ret;

}

//Android11 有改变为

static int __init binder_init(void)
6081  {
6082  	int ret;
6083  	char *device_name, *device_tmp;
6084  	struct binder_device *device;
6085  	struct hlist_node *tmp;
6086  	char *device_names = NULL;
6087  
6088  	ret = binder_alloc_shrinker_init();
6089  	if (ret)
6090  		return ret;
6091  
6092  	atomic_set(&binder_transaction_log.cur, ~0U);
6093  	atomic_set(&binder_transaction_log_failed.cur, ~0U);
6094  
6095  	binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
6096  	if (binder_debugfs_dir_entry_root)
6097  		binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
6098  						 binder_debugfs_dir_entry_root);
6099  
6100  	if (binder_debugfs_dir_entry_root) {
6101  		debugfs_create_file("state",
6102  				    0444,
6103  				    binder_debugfs_dir_entry_root,
6104  				    NULL,
6105  				    &binder_state_fops);
6106  		debugfs_create_file("stats",
6107  				    0444,
6108  				    binder_debugfs_dir_entry_root,
6109  				    NULL,
6110  				    &binder_stats_fops);
6111  		debugfs_create_file("transactions",
6112  				    0444,
6113  				    binder_debugfs_dir_entry_root,
6114  				    NULL,
6115  				    &binder_transactions_fops);
6116  		debugfs_create_file("transaction_log",
6117  				    0444,
6118  				    binder_debugfs_dir_entry_root,
6119  				    &binder_transaction_log,
6120  				    &binder_transaction_log_fops);
6121  		debugfs_create_file("failed_transaction_log",
6122  				    0444,
6123  				    binder_debugfs_dir_entry_root,
6124  				    &binder_transaction_log_failed,
6125  				    &binder_transaction_log_fops);
6126  	}
6127  
6128  	if (!IS_ENABLED(CONFIG_ANDROID_BINDERFS) &&
6129  	    strcmp(binder_devices_param, "") != 0) {
6130  		/*
6131  		* Copy the module_parameter string, because we don't want to
6132  		* tokenize it in-place.
6133  		 */
6134  		device_names = kstrdup(binder_devices_param, GFP_KERNEL);
6135  		if (!device_names) {
6136  			ret = -ENOMEM;
6137  			goto err_alloc_device_names_failed;
6138  		}
6139  
6140  		device_tmp = device_names;
6141  		while ((device_name = strsep(&device_tmp, ","))) {
6142  			ret = init_binder_device(device_name);
6143  			if (ret)
6144  				goto err_init_binder_device_failed;
6145  		}
6146  	}
6147  
6148  	ret = init_binderfs();
6149  	if (ret)
6150  		goto err_init_binder_device_failed;
6151  
6152  	return ret;
6153  
6154  err_init_binder_device_failed:
6155  	hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
6156  		misc_deregister(&device->miscdev);
6157  		hlist_del(&device->hlist);
6158  		kfree(device);
6159  	}
6160  
6161  	kfree(device_names);
6162  
6163  err_alloc_device_names_failed:
6164  	debugfs_remove_recursive(binder_debugfs_dir_entry_root);
6165  
6166  	return ret;
6167  }

debugfs_create_dir 是指在 debugfs 文件系统中创建一个目录,返回值是指向dentry 的指针。

binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
static struct dentry *binder_debugfs_dir_entry_root;

当 kernel 中禁用 debugfs 的话,返回值是-%ENODEV。默认是禁用的。如果需要打开,在目录/kernel/arch/arm64/configs/下找到目标defconfig 文件中添加一行 CONFIG_DEBUG_FS=y,再重新编译版本,即可打开debug_fs。

在android 11中我们发现:

__init binder_init(void)中核心的操作是在:
while ((device_name = strsep(&device_tmp, ","))) {
6142  			ret = init_binder_device(device_name);
6143  			if (ret)
6144  				goto err_init_binder_device_failed;
6145  		}

那么其中:init_binder_device 这个是很核心的方法:

static int __init init_binder_device(const char *name)
6052  {
6053  	int ret;
6054  	struct binder_device *binder_device;
6055  
6056  	binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
6057  	if (!binder_device)
6058  		return -ENOMEM;
6059  
6060  	binder_device->miscdev.fops = &binder_fops;
6061  	binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
6062  	binder_device->miscdev.name = name;
6063  
6064  	refcount_set(&binder_device->ref, 1);
6065  	binder_device->context.binder_context_mgr_uid = INVALID_UID;
6066  	binder_device->context.name = name;
6067  	mutex_init(&binder_device->context.context_mgr_node_lock);
6068  
6069  	ret = misc_register(&binder_device->miscdev);
6070  	if (ret < 0) {
6071  		kfree(binder_device);
6072  		return ret;
6073  	}
6074  
6075  	hlist_add_head(&binder_device->hlist, &binder_devices);
6076  
6077  	return ret;
6078  }

这断代码的核心就是:misc_register  注册 misc 设备

      2.1.1 misc_register

     注册 misc 设备,miscdevice 结构体,便是前面注册 misc 设备时传递进去的参数 static struct miscdevice binder_miscd

当我们在去看的时候发现:misc_register  kernel/msm-4.14/drivers/char/misc.c在这个里面,而

  misc_register(&binder_device->miscdev); 在  /drivers/android/binder.c里面 如何关联

extern int misc_register(struct miscdevice *misc);在kernel/msm-4.14/include/linux/miscdevice.h

而:  /misc.c 

#include <linux/miscdevice.h>

binder.c 中:

#include <linux/miscdevice.h>

extern表明变量或者函数是定义在其他其他文件中的

引用了头文件,就可以调用其中的方法,通过search 发现方法的定义在:misc.c文件里面

回到正题发现是:miscdev结构体参数,那么我们下面来看一下这个结构体:

/include/linux/miscdevice.h

 struct miscdevice  {
66  	int minor;
67  	const char *name;
68  	const struct file_operations *fops;
69  	struct list_head list;
70  	struct device *parent;
71  	struct device *this_device;
72  	const struct attribute_group **groups;
73  	const char *nodename;
74  	umode_t mode;
75  }  
binder_device->miscdev.fops = &binder_fops; //设备的文件操作结构,这是 file_operations 结构  
上面的备注的真实来源是:
    const struct file_operations binder_fops = {
6041  	.owner = THIS_MODULE,
6042  	.poll = binder_poll,
6043  	.unlocked_ioctl = binder_ioctl,
6044  	.compat_ioctl = binder_ioctl,
6045  	.mmap = binder_mmap,
6046  	.open = binder_open,
6047  	.flush = binder_flush,
6048  	.release = binder_release,
6049  };
6061  	binder_device->miscdev.minor = MISC_DYNAMIC_MINOR; //设备号 动态分配
6062  	binder_device->miscdev.name = name;  //设备名
6063  
6064  	refcount_set(&binder_device->ref, 1);
6065  	binder_device->context.binder_context_mgr_uid = INVALID_UID;
6066  	binder_device->context.name = name;
6067  	mutex_init(&binder_device->context.context_mgr_node_lock);
6068  
6069  	ret = misc_register(&binder_device->miscdev);

2.2 binder_open

核心类里面:/kernel/msm-4.14/drivers/android/binder.c

    打开 binder 驱动设备

 static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc; // binder 进程 
proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 为 binder_proc 结构体在分配kernel 内存空间
if (proc == NULL)
return -ENOMEM;
get_task_struct(current);
proc->tsk = current; //将当前线程的 task 保存到 binder 进程的 tsk
INIT_LIST_HEAD(&proc->todo); //初始化 todo 列表
init_waitqueue_head(&proc->wait); //初始化 wait 队列
proc->default_priority = task_nice(current); //将当前进程的nice 值转换为进程优先级
binder_lock(__func__); //同步锁,因为 binder 支持多线程访问
binder_stats_created(BINDER_STAT_PROC); //BINDER_PROC 对象创建数加1

hlist_add_head(&proc->proc_node, &binder_procs); //将 proc_node 节点添加到binder_procs 为表头的队列
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death); //初始化已分发的死亡通知列表filp->private_data = proc; //file 文件指针的 private_data 变量指向binder_proc 数据
binder_unlock(__func__); //释放同步锁
return 0;
}

创建 binder_proc 对象,并把当前进程等信息保存到 binder_proc 对象,该对象管理 IPC 所需的各种信息并拥有其他结构体的根结构体;再把binder_proc 对象保存到文件指针 filp,以及把 binder_proc 加入到全局链表binder_procs。

Binder 驱动中通过 static HLIST_HEAD(binder_procs);,创建了全局的哈希链表binder_procs,用于保存所有的 binder_proc 队列,每次新创建的binder_proc对象都会加入 binder_procs 链表中。

2.3 binder_mmap 

binder_mmap(文件描述符,用户虚拟内存空间)

主要功能:首先在内核虚拟地址空间,申请一块与用户虚拟内存相同大小的内存;然后再申请 1 个 page 大小的物理内存,再将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,从而实现了用户空间的Buffer 和内核空间的Buffer 同步操作的功能。

static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
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;
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M; //保证映射内存大小不超过4M
mutex_lock(&binder_mmap_lock); //同步锁
//采用 IOREMAP 方式,分配一个连续的内核虚拟空间,与进程虚拟空间大小一致

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;
}
proc->buffer = area->addr; //指向内核虚拟空间的地址
//地址偏移量 = 用户虚拟地址空间 - 内核虚拟地址空间
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
mutex_unlock(&binder_mmap_lock); //释放锁
...
//分配物理页的指针数组,数组大小为 vma 的等效 page 个数;
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;
//分配物理页面,同时映射到内核空间和进程空间,先分配 1 个物理页  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;
}
buffer = proc->buffer; //binder_buffer 对象 指向 proc 的 buffer 地址INIT_LIST_HEAD(&proc->buffers); //创建进程的 buffers 链表头
list_add(&buffer->entry, &proc->buffers); //将 binder_buffer 地址加入到所属进程的 buffers 队列
buffer->free = 1;
//将空闲 buffer 放入 proc->free_buffers 中
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;
return 0;
...// 错误 flags 跳转处,free 释放内存之类的操作
return ret;
}

binder_mmap 通过加锁,保证一次只有一个进程分配内存,保证多进程间的并发访问。其中 user_buffer_offset 是虚拟进程地址与虚拟内核地址的差值(该值为负数)。也就是说同一物理地址,当内核地址为 kernel_addr,则进程地址为proc_addr = kernel_addr + user_buffer_offset。

2.3.1 binder_update_page_range

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; // 内存结构体
if (vma)
mm = NULL; //binder_mmap 过程 vma 不为空,其他情况都为空else
mm = get_task_mm(proc->tsk); //获取 mm 结构体
if (mm) {
down_write(&mm->mmap_sem); //获取 mm_struct 的写信号量
vma = proc->vma;
}
//此处 allocate 为 1,代表分配过程。如果为 0 则代表释放过程
if (allocate == 0)
goto free_range;
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
int ret;
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
//分配一个 page 的物理内存
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
//物理空间映射到虚拟内核空间
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);
user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset;
//物理空间映射到虚拟进程空间
ret = vm_insert_page(vma, user_page_addr, page[0]);
}
if (mm) {
up_write(&mm->mmap_sem); //释放内存的写信号量
mmput(mm); //减少 mm->mm_users 计数
}
return 0;
free_range:
... //释放内存的流程
return -ENOMEM;
}

主要工作如下:

binder_update_page_range 主要完成工作:分配物理空间,将物理空间映射到内核空间,将物理空间映射到进程空间. 另外,不同参数下该方法也可以释放物理页面。

binder_update_page_range 的调用时机:  binder_mmap: 用于分配内存,分配大小为 1page, vma 不为空; binder_alloc_buf:用于分配内存,vma 为空; binder_free_buf: 用于释放内存,vma 为空;  binder_delete_free_buffer:同样用于释放内存,vma 为空。

关于 mm_struct 结构体,定义在 mm_types.h 文件:

struct mm_struct {
struct vm_area_struct *mmap; //VMA 列表
struct rb_root mm_rb;
pgd_t * pgd;
atomic_t mm_users; //使用该内存的进程个数
atomic_t mm_count; //结构体 mm_struct 的引用个数
struct rw_semaphore mmap_sem; //读写信号量,用于同步
unsigned long flags;
...
};

下面,再说一说 binder_alloc_buf 过程

2.3.2 binder_alloc_buf

通过 binder_alloc_buf()方法来分配 binder_buffer 结构体, 只有在binder_transaction 过程才需要分配 buffer.

static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc,
size_t data_size, size_t offsets_size, int is_async)
{
struct rb_node *n = proc->free_buffers.rb_node;
struct binder_buffer *buffer;
size_t buffer_size;
struct rb_node *best_fit = NULL;
void *has_page_addr;
void *end_page_addr;
size_t size;
if (proc->vma == NULL) {
return NULL; //虚拟地址空间为空,直接返回
}
size = ALIGN(data_size, sizeof(void *)) + ALIGN(offsets_size, sizeof(void
*));
if (size < data_size || size < offsets_size) {
return NULL; //非法的 size
}
if (is_async && proc->free_async_space < size + sizeof(struct binder_buffer)){
return NULL; // 剩余可用的异步空间,小于所需的大小
}
while (n) { //从 binder_buffer 的红黑树中查找大小相等的 buffer 块buffer = rb_entry(n, struct binder_buffer, rb_node);
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;
}
}
if (best_fit == NULL) {
return NULL; //内存分配失败,地址空间为空
}
if (n == NULL) {
buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
buffer_size = binder_buffer_size(proc, buffer);
}
has_page_addr =(void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MAS
K);
if (n == NULL) {
if (size + sizeof(struct binder_buffer) + 4 >= buffer_size)
buffer_size = size;
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);
}
buffer->data_size = data_size;
buffer->offsets_size = offsets_size;
buffer->async_transaction = is_async;
if (is_async) {
proc->free_async_space -= size + sizeof(struct binder_buffer);
}
}

这里介绍的 binder_alloc_buf 是内存分配函数。除此之外,还有内存释放相关方法:

binder_free_buf

 binder_delete_free_buffer

 binder_transaction_buffer_release

这里涉及强弱引用相关函数的操作:

2.4 binder_ioctl

binder_ioctl()函数负责在两个进程间收发 IPC 数据和 IPC reply 数据。

ioctl(文件描述符,ioctl 命令,数据类型)

(1) 文件描述符,是通过 open()方法打开 Binder Driver 后返回值;(2) ioctl 命令和数据类型是一体的,不同的命令对应不同的数据类型

这些命令中 BINDER_WRITE_READ命令使用率最为频繁,也是ioctl 最为核心的命令。

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread; // binder 线程
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
//进入休眠状态,直到中断唤醒
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
binder_lock(__func__);
//获取 binder_thread【见 2.4.1】
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
switch (cmd) {
case BINDER_WRITE_READ: //进行 binder 的读写操作
ret = binder_ioctl_write_read(filp, cmd, arg, thread); //【见2.4.2】if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS: //设置 binder 最大支持的线程数
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))){
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR: //成为 binder 的上下文管理者,也就是ServiceManager成为守护进程
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
case BINDER_THREAD_EXIT: //当 binder 线程退出,释放 binder 线程binder_free_thread(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: { //获取 binder 的版本号
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);
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}

执行 ioctl 过程,便需要加上 binder lock.

2.4.1 binder_get_thread 

从 binder_proc 中查找 binder_thread,如果当前线程已经加入到proc 的线程队列则直接返回,如果不存在则创建 binder_thread,并将当前线程添加到当前的proc

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;
while (*p) { //根据当前进程的 pid,从 binder_proc 中查找相应的 binder_thread
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) {
thread = kzalloc(sizeof(*thread), GFP_KERNEL); //新建 binder_thread 结构体if (thread == NULL)
return NULL;
binder_stats_created(BINDER_STAT_THREAD);
thread->proc = proc;
thread->pid = current->pid; //保存当前进程(线程)的 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;
}

2.4.2 binder_ioctl_write_read

对于 ioctl()方法中,传递进来的命令是 cmd = BINDER_WRITE_READ 时执行该方法,arg 是一个 binder_write_read 结构体

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;
}
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { //把用户空间数据ubuf 拷贝到bwrret = -EFAULT;
goto out;
}
if (bwr.write_size > 0) {
//当写缓存中有数据,则执行 binder 写操作
ret = binder_thread_write(proc, thread,
bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) { //当写失败,再将 bwr 数据写回用户空间,并返回bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
if (bwr.read_size > 0) {
//当读缓存中有数据,则执行 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) { //当读失败,再将 bwr 数据写回用户空间,并返回if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { //将内核数据 bwr 拷贝到用户空间ubuf
ret = -EFAULT;
goto out;
}out:
return ret;
}
对于 binder_ioctl_write_read 的流程图,如下:

流程:

      首先,把用户空间数据 ubuf 拷贝到内核空间 bwr

    当 bwr 写缓存有数据,则执行 binder_thread_write;当写失败则将bwr 数据写回用户空间并退出;

当 bwr 读缓存有数据,则执行 binder_thread_read;当读失败则再将bwr 数据写回用户空间并退出;

最后,把内核数据 bwr 拷贝到用户空间 ubuf。

这里涉及两个核心方法 binder_thread_write()和 binder_thread_read()方法

2.4 Command 使用场景

 ioctl 命令常见命令的使用场景,其中 BINDER_WRITE_READ 最为频繁

 BINDER_WRITE_READ

            Binder 读写交互场景,IPC.talkWithDriver

BINDER_SET_CONTEXT_MGR

              servicemanager 进 程 成 为 上 下文管理者,binder_become_context_manager()

BINDER_SET_MAX_THREADS

             初始化 ProcessState 对象,open_driver()

             主动调整参数,ProcessState.setThreadPoolMaxThreadCount()

BINDER_VERSION

             初始化 ProcessState 对象,open_driver()

2.5 小节

     binder_init:初始化字符设备;

    binder_open:打开驱动设备,过程需要持有 binder_main_lock 同步锁;

    binder_mmap:申请内存空间,该过程需要持有binder_mmap_lock 同步锁;

   binder_ioctl:执行相应的 ioctl 操作,该过程需要持有binder_main_lock同步锁;

当处于 binder_thread_read 过程,read_buffer 无数据则释放同步锁,并处于 wait_event_freezable 过程,等有数据到来则唤醒并尝试持有同步锁。

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/434667.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何刻录光盘文件

常识补充刻录机简介光盘刻录机是一种数据写入设备&#xff0c;利用激光将数据写到空光盘上从而实现数据的储存。其写入过程可以看做普通光驱读取光盘的逆过程。基本原理刻入数据时&#xff0c;利用高功率的激光束反射到盘片&#xff0c;使盘片上发生变化&#xff0c;模拟出二进…

计算机网络常见协议

文章目录 计算机网络TCP/IP协议TCP协议的三次握手和四次挥手TCP连接建立过程TCP连接断开过程为什么要三次握手&#xff1f;为什么要四次挥手&#xff1f; UDP协议HTTP协议 计算机网络 学习计算机网络&#xff0c;来记录一下。 TCP/IP协议 TCP/IP协议是Internet最基本的协议、…

报错-crontab -e 定时任务执行失败排查

使用 crontab -e 定时启动 jar 包服务失败&#xff0c;排查过程如下&#xff1a; 1、查看 crontab 服务 crontab -l陈列出了待执行任务列表&#xff0c;crontab 正常。 2、检查脚本 单独执行脚本没有问题&#xff0c;脚本内容为检查线程&#xff0c;杀死线程&#xff0c;重…

Python每日一练(20230419)

目录 1. N皇后 II &#x1f31f;&#x1f31f;&#x1f31f; 2. 迷宫问题(递归) &#x1f31f;&#x1f31f;&#x1f31f; 3. 体操比赛成绩统计 ※ &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每…

分布式ID的生成方法

问题的提出 如今随着互联网的发展&#xff0c;数据的量级也是呈指数的增长&#xff0c;从GB到TB到PB.对数据的各种操作也是愈 加的困难&#xff0c;如何解决这个问题呢?此时就需要做数据库集群&#xff0c;为了提高查询性能将一一个数据库的数据分散 到不同的数据库中存储&am…

JVM垃圾回收与调优

文章目录 1、如何判断对象可以回收1.1、 引用计数法1.2、可达性分析法1.3、五种引用类型1.3.1 、强引用1.3.2 、软、弱引用1.3.3 、虚引用、终结器引用1.3.4、 终结器引用1.3.5 、总结 2. 垃圾清除算法2.1、标记清除2.2 、标记整理2.3、 复制 3. 分代垃圾回收3.1 、新生代、老年…

Excel技能之时间,士别三日让boss刮目相看

爱因斯坦说&#xff1a;“复利是世界第八大奇迹。”复利离不开时间&#xff0c;你也离不开时间。时间是如此重要&#xff0c;对每个人都是公平的。 曾经的你&#xff0c;看日历&#xff0c;数手指才能算清楚日期&#xff0c;不懂时间函数&#xff0c;太烦躁了。以下用真实的使…

哪种无线耳机音质最好?盘点2023四款好音质蓝牙耳机

随着蓝牙技术的发展&#xff0c;近几年人们对于蓝牙耳机的需求也在不断增加。但&#xff0c;蓝牙耳机自始至终都是用来听的&#xff0c;所以音质对于一款蓝牙耳机来说还是很重要的。下面&#xff0c;我来给大家推荐四款好音质蓝牙耳机&#xff0c;可以当个参考。 一、南卡小音舱…

沉岛思想(BFS)-朋友圈思想(并查集)

本篇博客旨在记录自已笔记&#xff0c;同时希望可给小伙伴一些帮助。本人也是算法小白&#xff0c;水平有限&#xff0c;如果文章中有什么错误之处&#xff0c;希望小伙伴们可以在评论区指出来&#xff0c;共勉 &#x1f4aa;。 沉岛思想&#xff1a; 题目&#xff1a; 给定一…

Sharding-JDBC之水平分库水平分表

目录 一、简介二、maven依赖三、数据库3.1、创建数据库3.2、创建表 四、配置&#xff08;二选一&#xff09;4.1、properties配置4.2、yml配置 五、实现5.1、实体5.2、持久层5.3、服务层5.4、测试类5.4.1、保存数据5.4.2、查询数据 一、简介 这里的水平分库分表是指 水平分库 …

台湾精锐APEX行星减速机直齿轮和斜齿轮有什么区别?如何选择?

台湾精锐APEX行星减速机是带太阳齿轮/行星齿轮/齿圈的机械装置。行星减速机是由太阳齿轮&#xff0c;行星齿轮的齿轮架和齿圈组成的机械装置。太阳齿轮位于中心&#xff0c;将扭矩传递到围绕太阳齿轮旋转的行星齿轮。行星齿轮和太阳齿轮位于齿圈内。 APEX减速机分为直齿轮和斜…

7.2 参数区间的估计

学习目标&#xff1a; 要学习参数的区间估计&#xff0c;我会采取以下步骤&#xff1a; 学习理论知识&#xff1a;首先&#xff0c;我会学习与参数的区间估计相关的理论知识&#xff0c;包括置信区间、抽样分布、中心极限定理、样本容量对置信区间的影响等。 掌握计算方法&am…

【小程序】小程序组件-2

目录 一. 滚轮选框 二. 音频组件 一. 滚轮选框 说真的&#xff0c;感谢微信开发者工具&#xff0c;让我这种笨比能够轻松学会这种看起来相当复杂的组件 picker组件的mode有几种模式&#xff0c;region啦&#xff0c;date啦&#xff0c;time啦&#xff0c;可以自行尝试 针对…

牛客社区项目

创建项目 认识Spring Spring Ioc Inversion 偶发Control 控制反转&#xff0c;是一种面向对象的设计思想。Dependecy Injection 依赖注入&#xff0c;是Ioc思想的实现方式。Ioc Container Ioc容器&#xff0c;是实现依赖注入的关键&#xff0c;本质上是一个工厂。 下面通过…

解决若依验证码异常:Error: image == null

前言 前两天在改项目突然发现若依的框架可以正常启动但是验证码加载不出来了&#xff0c;一直弹窗提示异常信息&#xff0c;下边是关于问题的描述和解决方案&#xff0c;没有耐心看过程的建议直接滑到最底下看解决方式 问题原因 登录页面一直提示 image null 如图 1 所示&…

最新研究!充分发挥混合量子经典算法新潜力

日本理化学研究所RIKEN的研究人员开发了一种量子计算算法&#xff0c;可高效准确地计算复杂材料中的原子级相互作用。物理学家理查德费曼于1981年首次提出量子计算机的应用&#xff0c;而该算法有可能为凝聚态物理学和量子化学带来前所未有的新局面。 量子计算机有望增强数字处…

数据格式转换(labelme、labelimg、yolo格式相互转换)

&#x1f468;‍&#x1f4bb;个人简介&#xff1a; 深度学习图像领域工作者 &#x1f389;总结链接&#xff1a; 链接中主要是个人工作的总结&#xff0c;每个链接都是一些常用demo&#xff0c;代码直接复制运行即可。包括&#xff1a; &am…

【鸿蒙应用ArkTS开发系列】- 常量类定义和使用

本篇为入门基础知识介绍&#xff0c;作为代码学习记录使用&#xff0c;请选择性阅读。 一、常量类定义 在ArkTS中&#xff0c;定一个常量很简单&#xff0c;具体如下&#xff1a; export const TAB_HOME_INDEX : number 1;export const TAB_HOME_NAME : string "首…

MobileNetV2详细原理(含torch源码)

目录 MobilneNetV2原理 MobileNetV2的创新点&#xff1a; MobileNetV2对比MobileNetV1 MobilneNetV2源码&#xff08;torch版&#xff09; 训练10个epoch的效果 MobilneNetV2原理 MobileNetV2是由谷歌开发的一种用于移动设备的轻量级卷积神经网络。与传统卷积神经网络相比…

RapidOCR调优尝试教程

目录 引言常见错例种类个别字丢失调优篇个别字识别错误调优篇情况一&#xff1a;轻量中英文模型识别对个别汉字识别错误情况二&#xff1a;轻量中英文模型对个别英文或数字识别错误 相关链接 引言 由于小伙伴们使用OCR的场景多种多样&#xff0c;单一的参数配置往往不能满足要…