一:前言,Binder机制原理
对于进程间通信,Linux有很多实现的方式,像管道、信号量、共享内存、Socket等,那么为什么Android要使用Binder而不使用传统的IPC,钟爱Binder自然有其中的原因,从本节开始,分析Android Framework层原理
1.1 Binder是什么?
Binder作为Android进程间通信的机制,可以看做是一个驱动,因为本身也是一个Java类,Binder.java : IBinder
那么在Android中,常见的进程间通信有哪些?例如系统类的:打电话、闹钟等;自己创建的:像WebView、视频播放、音频播放、大图浏览等
1.2 那么进程间通信的优势在哪?
· 内存
Android 虚拟机 会给每个进程分配内存,如果是单一进程,像WebView,或者大图浏览会占用进程的很多内存,严重的时候还是会出现OOM,因此如果采用多进程的方式,那么虚拟机会给每个进程都分配内存,能够避免出现内存不足的情况
· 风险隔离
如果是单个进程,某个模块出现问题,那么就会导致整个app崩溃;如果采用多进程的方式,其中某个进程崩溃,主app不会受到影响。
1.3 相对于传统IPC Binder的优势在哪?
· 性能
相对于传统IPC的两次拷贝,Binder是拷贝数据是1次拷贝,性能低于共享内存,但是高于传统的IPC
· 安全性
传统的IPC方式没法获取进程的UID(UID是android系统对每个APP赋予的唯一标识,可以用它做权限验证),都依赖上层协议,无法通过UID来验证,只是通过PID并不安全;
Binder是系统能够分配给进程UID,可以通过UID来鉴权,更安全;
而且Binder支持实名服务和匿名服务:实名服务指的是系统服务,通过getSystemService获取的服务;匿名服务是自己创建的服务,例如继承Service
1.4 Binder如何完成了1次拷贝?
通过了解Binder驱动的原理,就能知道Binder实现数据1次拷贝的原理
1.5 内存的划分
image.png
我画的这幅图中,可以看到进程内存的划分,每个进程之间用户空间内存是隔离的,而内核空间是共享的,因此在传统的IPC数据通信时,是在用户空间发起方 copy_from_user 将数据拷贝到内核空间数据缓存区,然后数据缓存区 copy_to_user 将数据拷贝到数据接受方
虚拟内存:Android进程都是运行在自己进程的虚拟内存空间,操作也只能在虚拟内存中完成;例如32位的操作系统,最大虚拟内存空间为4G,其中内核空间 1G ,用户空间 3G,但是并不是所有的内存空间都能为app进程所用。
物理内存:内存条,RAM,就可以看做是物理内存,速度很快,其中虚拟内存可以看做是物理内存的代理 / 映射
1.6 虚拟内存
因为Android的进程都是运行在虚拟内存,因此所有的操作都是在虚拟内存中完成。虚拟内存分为用户空间 和 内核空间,其中内核空间用来管理所有的系统资源,像读写磁盘文件、分配收回内存、读取接口数据等,用户空间的作用是发起请求,通过系统调用,让内核空间完成这些事情
因此在用户空间,是不用能直接操作文件的!
那么举个例子,往磁盘中写数据的流程是这样的:
1 用户空间调用IO流的write方法,告诉内核空间数据的起始地址以及长度;
2 内核 copy_from_user 将数据拷贝到数据缓存区;
3 通过系统调用,将数据拷贝到磁盘,完成文件的写入
这也是2次拷贝的过程
1.7 mmap的原理
上面讲到了传统IPC的2次拷贝,进程之前通过内核空间二次拷贝数据,完成进程间的通信;Binder能做到1次拷贝,就是通过mmap实现内存映射,接收方和内核空间共享一块物理内存,因为操作文件只能在内核空间完成,必须要将数据从用户空间拷贝到内核空间,这1次拷贝少不了!
mmap就是人为地将一块虚拟内存空间跟磁盘对象(物理内存)绑定,只要数据在这块物理内存中,那么用户空间就能感知到直接获取数据
image.png
前面提到过,虚拟内存就是其所对应的物理内存的映射,当数据data,从用户空间拷贝到数据缓存区后,数据缓存区跟binder_mmap物理内存映射,其实数据是存到了共享物理内存,那么接收方用户空间跟这块物理内存也是映射关系,当物理内存存进去数据后,用户空间同样感受到了,就拿到了这块数据,这就是mmap的作用。
二、Binder驱动源码分析
从App启动,Binder是怎样被初始化的,还有就是一些细节的处理,从最底层的C++代码看Binder驱动是怎么实现的
如果大家想要看系统的源码,其实不用去下载源码,直接在在线看就行在线看系统源码,点击就可以看
2.1:binder_init作用
1.文件位置:/drivers/staging/android/binder.c
//设备驱动入口函数
device_initcall(binder_init);
//binder_init方法
static int __init binder_init(void)
{
int ret;
//创建名为binder的单线程工作队列
binder_deferred_workqueue = create_singlethread_workqueue("binder");
if (!binder_deferred_workqueue)
return -ENOMEM;
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_register函数进行misc binder设备注册
ret = misc_register(&binder_miscdev);
if (binder_debugfs_dir_entry_root) {
debugfs_create_file("state",
S_IRUGO,
binder_debugfs_dir_entry_root,
NULL,
&binder_state_fops);
debugfs_create_file("stats",
S_IRUGO,
binder_debugfs_dir_entry_root,
NULL,
&binder_stats_fops);
debugfs_create_file("transactions",
S_IRUGO,
binder_debugfs_dir_entry_root,
NULL,
&binder_transactions_fops);
debugfs_create_file("transaction_log",
S_IRUGO,
binder_debugfs_dir_entry_root,
&binder_transaction_log,
&binder_transaction_log_fops);
debugfs_create_file("failed_transaction_log",
S_IRUGO,
binder_debugfs_dir_entry_root,
&binder_transaction_log_failed,
&binder_transaction_log_fops);
}
return ret;
}
misc_register方法:
int misc_register(struct miscdevice * misc)
{
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
if (misc->minor == MISC_DYNAMIC_MINOR) {
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
if (i >= DYNAMIC_MINORS) {
err = -EBUSY;
goto out;
}
misc->minor = DYNAMIC_MINORS - i - 1;
set_bit(i, misc_minors);
} else {
struct miscdevice *c;
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
err = -EBUSY;
goto out;
}
}
}
dev = MKDEV(MISC_MAJOR, misc->minor);
//创建misc设备方法device_create
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name);
if (IS_ERR(misc->this_device)) {
int i = DYNAMIC_MINORS - misc->minor - 1;
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors);
err = PTR_ERR(misc->this_device);
goto out;
}
/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
list_add(&misc->list, &misc_list);
out:
mutex_unlock(&misc_mtx);
return err;
}
以下是定义的结构体binder_miscdev 和 binder_fops
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,
};
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "binder",
.fops = &binder_fops
};
在binder_init函数中,调用了misc_register函数,其中传入的参数binder_miscdev,定义了misc设备的名称”binder“,就是注册了一个binder设备,MISC_DYNAMIC_MINOR是动态分配的一个设备号;还有一个数据结构,binder_fops,其中包含了binder_open、binder_ioctl、binder_mmap等重要的函数,也是在这个时候完成了初始化,具体怎么完成的初始化,接着往下看
misc_register调用到device_create方法,device_create就是用来创建binder misc device设备
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
//调用device_create_vargs方法
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
struct device *device_create_vargs(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt,
va_list args)
{
//调用device_create_groups_vargs方法
return device_create_groups_vargs(class, parent, devt, drvdata, NULL,
fmt, args);
}
static struct device *device_create_groups_vargs(struct class *class, struct device *parent,
dev_t devt, void *drvdata,
const struct attribute_group **groups,
const char *fmt, va_list args)
{
struct device *dev = NULL;
int retval = -ENODEV;
if (class == NULL || IS_ERR(class))
goto error;
//给binder设备分配内存GFP_KERNEL
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
retval = -ENOMEM;
goto error;
}
device_initialize(dev);
dev->devt = devt;
dev->class = class;
dev->parent = parent;
dev->groups = groups;
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata);
retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
if (retval)
goto error;
retval = device_add(dev);
if (retval)
goto error;
return dev;
error:
put_device(dev);
return ERR_PTR(retval);
}
其中GFP_KERNEL的作用:
GFP_KERNEL是linux内存分配器的标志,标识着内存分配器将要采取的行为。
分配器标志分为行为修饰符,区修饰符及类型。行为修饰符表示内核应当如何分配所需的内存。
区修饰符表示内存区应当从何处分配。类型就是行为修饰符和区修饰符的合体。
在include/linux/gfp.h中定义,GFP_KERNEL是内核内存分配时最常用的,无内存可用时可引起休眠.
#define GFP_KERNEL(__GFP_WAIT | __GFP_IO | __GFP_FS)
__GFP_WAIT : 缺内存页的时候可以睡眠;
__GFP_IO : 允许启动磁盘IO;
总结binder_init的主要作用就是:
1 初始化binder misc设备
2 给设备分配内存
3 调用list_add,将misc device设备放入设备列表中misc_list
三:binder_open作用
当Android要进行进程间通信的时候,通过客户端或者服务端调用binder_open,打开Binder驱动,例如在app中,调用bindService,就是通过Framework层,JNI调用底层binder_open打开驱动
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
get_task_struct(current);
proc->tsk = current;
INIT_LIST_HEAD(&proc->todo);
init_waitqueue_head(&proc->wait);
proc->default_priority = task_nice(current);
binder_lock(__func__);
binder_stats_created(BINDER_STAT_PROC);
//将proc添加到链表中
hlist_add_head(&proc->proc_node, &binder_procs);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
filp->private_data = proc;
binder_unlock(__func__);
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
}
return 0;
}
在binder_open中,首先给binder_proc分配内存,然后将当前进程的信息,保存到binder_proc中,然后将binder_proc添加到binder_procs表中(hlist_add_head),每个进程拥有一个binder_proc,独一份!
四:binger_mmap作用
这里就到了Binder机制的核心原理,mmap的处理,看怎么实现内存的映射关系的
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;
//进程的虚拟内存,不能超过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;
}
/*
* 在内核中,分配一块跟用户空间大小相同的一块虚拟内存
* 其中struct vm_area_struct *vma指的是用户空间的虚拟内存,
* struct vm_struct *area为内核空间的虚拟内存
**/
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);
#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
//分配物理内存
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;
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;
INIT_LIST_HEAD(&proc->buffers);
list_add(&buffer->entry, &proc->buffers);
buffer->free = 1;
binder_insert_free_buffer(proc, buffer);
//异步,缓存大小就是一半buffer_size / 2,(默认是同步,缓存大小为buffer_size)
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;
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;
}
首先明白几个变量代表的含义
vm_area_struct *vma : 用户态的虚拟内存
vm_struct *area : 内核态的虚拟内存
4.1 代码分析:
4.1.1
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
在这里,驱动做了限制,用户态的虚拟内存,不能超过4M,应用所占用的大概有1M - 8K(异步为一半,这里是同步的标准),所以为什么Intent传递数据时,不能超过1M,就是因为Intent传递数据基于Binder机制,因此不能超过1M,这是Binder驱动定的。
4.1.2
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
在内核空间中,分配一块跟用户空间大小相同的虚拟内存
4.1.3
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;
}
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 vm_struct tmp_area;
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;
}
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);
*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;
}
tmp_area.addr = page_addr;
tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
//内核虚拟内存与物理内存完成映射
ret = map_vm_area(&tmp_area, PAGE_KERNEL, page);
if (ret) {
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;
//用户空间与物理内存完成映射
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, NULL);
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;
}
allocate == 1,意味着分配1页的物理内存(4KB),并与内核空间的虚拟内存完成映射(map_vm_area);vm_insert_page 将用户空间的虚拟内存与物理内存完成映射
image.png看一下这个图就能明白binder_mmap的作用了
五:binder_ioctl方法作用
binder_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;
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);*/
trace_binder_ioctl(cmd, arg);
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
binder_lock(__func__);
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);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS:
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR:
//设置ServiceManager为大管家,下一节会走到这个地方
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
ret = security_binder_set_context_mgr(proc->tsk);
if (ret < 0)
goto err;
break;
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;
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;
}
从源码中可以看到,binder_ioctl只要是通过cmd命令来执行相应的操作,这里主要看下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))) {
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);
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
if (bwr.read_size > 0) {
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) {
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);
//调用copy_to_user将内核空间缓存数据拷贝到用户空间中
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
out:
return ret;
}
读写操作主要是判断write_size和read_size是否存在读写的数据,通过copy_from_user 和 copy_to_user完成数据的读写操作
————————————————
版权声明:本文为CSDN博主「Awesome_lay」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_33235287/article/details/122893770
网友评论