原创 Android中Binder机制的实现(一)

2010-6-28 00:43 5241 13 13 分类: MCU/ 嵌入式

from:http://blog.csdn.net/chenweiwu1986/archive/2009/09/11/4540385.aspx


看了Android的进程间通信好一段时间了,现在把自己的研究成果发上来,供以后和我一样要研究Binder的人参考一下。在看Android源码的过程中,受到了以下文章的帮助和启发,其中我的文章里面也直接引用了一些段落。对文章的作者表示感谢(转贴太多,我都不知道下面的链接是转贴还是原处了。。。):


http://hi.baidu.com/albertchen521/blog/item/30c32d3f4bee993a71cf6ca0.html


http://tech.it168.com/a2009/0331/270/000000270388.shtml


http://blog.csdn.net/woshishushangdezhu/archive/2009/08/04/4408293.aspx


Android binder机制深入探讨


                                                     http://blog.csdn.net/chenweiwu1986   


概述binderAndroid中使用的进程间通信机制(IPC)。进程间通信机制有很多种,例如linux中可以采用管道,消息队列,信号,共享内存,socket等,这些都可以实现进程间的通信。至于为什么Android采用binder,是由binder的高效率决定的。Binder是通过驱动的形式来实现,驱动程序的部分是在源代码的以下的文件中:


kernel/ include/linux/binder.h
kernel/drivers/android/binder.c


binder通信是基于ServiceClient的,有一个ServiceManager的守护进程管理着系统的各个服务,它负责监听是否有其他程序向其发送请求,如果有请求就响应。每个服务都要在ServiceManager中注册,而请求服务的客户端去ServiceManager请求服务。


 


binder的通信操作类似线程迁移(thread migration),binder的用户空间为每一个进程维护着一个可用的线程池, 用来处理到来的IPC以及执行本地消息,两个进程间通信就好像是一个进程进入另一个进程执行代码然后带着执行的结果返回。


         下面对其实现机制做一个详细的介绍。


一、ServiceManager


         Service Manager是一个守护进程,用于这个进程的和/dev/binder通讯,从而达到管理系统中各个服务的作用。Android虚拟机启动之前系统会先启动Service Manager进程,Service Manager打开binder驱动,并通知binder kernel驱动程序这个进程将作为System Service Manager,然后该进程将进入一个循环,等待处理来自其他进程的数据。


具体的源代码是在frameworks\base\cmds\servicemanager文件夹中的service_manager.c, binder.hbinder.c文件。下面将根据源代码介绍其运行机制。


         service_manager.c中可以找到一个main方法,它是servicemanager程序的入口:


int main(int argc, char **argv)


{


struct binder_state *bs;                //定义一个binder驱动结构


//#define BINDER_SERVICE_MANAGER ((void*) 0)


    void *svcmgr = BINDER_SERVICE_MANAGER;   //服务管理进程的句柄


    bs = binder_open(128*1024);           //打开驱动,并为驱动映射内存


//调用ioctl告诉Binder Kernel驱动程序这是一个服务管理进程


if (binder_become_context_manager(bs)) {  


        LOGE("cannot become context manager (%s)\n", strerror(errno));


        return -1;


    }


svcmgr_handle = svcmgr;   //全局变量void *svcmgr_handle


                     //进入无限循环,调用binder_loop等待来自其他进程的数据


  //binder_handler为处理函数int svcmgr_handler(struct binder_state *bs,


                                                           struct binder_txn *txn,


                                                           struct binder_io *msg,


                                                           struct binder_io *reply)


    binder_loop(bs, svcmgr_handler);


    return 0;


}


 


binder_state是表示驱动状态的一个数据结构,里面记录了打开驱动的句柄,分配的内存空间以及内存空间的大小。程序首先打开binder的驱动程序,然后通过binder_become_context_manager告诉binder这个进程是一个服务管理进程。binder_loop启动监听,处理来自其他程序的请求。


 


binder kernel注册这个进程为服务进程的binder_become_context_manager方法为:


int binder_become_context_manager(struct binder_state *bs)


{


    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);


}


Android和驱动程序通信采用linuxioctl机制。下面先简单介绍一下ioctl机制。


l          什么是ioctl


ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用函数如下:int ioctl(int fd, ind cmd, );其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道。


l          ioctl的必要性


如果不用ioctl的话,也可以实现对设备I/O通道的控制,但那就太复杂了。例如,我们可以在驱动程序中实现write的时候检查一下是否有特殊约定的数据流通过,如果有的话,那么后面就跟着控制命令(一般在socket编程中常常这样做)。但是如果这样做的话,会导致代码分工不明,程序结构混乱,程序员自己也会头昏眼花的。所以,我们就使用ioctl来实现控制的功能。要记住,用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。


l          ioctl如何实现


在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case


对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自


己的事情,因为设备都是特定的。关键在于怎么样组织命令码,因为在ioctl中命令


码是唯一联系用户程序命令和驱动程序支持的途径。命令码的组织是有一些讲究的,


因为我们一定要做到命令和设备是一一对应的,这样才不会将正确的命令发给错误的设备,或者是把错误的命令发给正确的设备,或者是把错误的命令发给错误的设备。这些错误都会导致不可预料的事情发生,而当程序员发现了这些奇怪的事情的时候,再来调试程序查找错误,那将是非常困难的事情。所以在Linux核心中是这样定义一个命令码的:


|设备类型 |序列号| 方向 |数据尺寸|    


|--------------|---------|--------|-------------|    


| 8 bit     |  8 bit | 2 bit  |8~14 bit  |


|--------------|---------|---------|------------|


这样一来,一个命令就变成了一个整数形式的命令码。但是命令码非常的不直观,所以Linux Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或者是从命令码得到一些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据传输尺寸。幻数是一个字母,数据长度也是8,所以就用一个特定的字母来标明设备类型,这和用一个数字是一样的,只是更加利于记忆和理解。


         再来看看在Android中的具体实现。我们的例子中BINDER_SET_CONTEXT_MGR就是命令参数cmd,可以在文件kernel\include\linux中找到这样的定义:


         #define BINDER_CURRENT_PROTOCOL_VERSION 7


 


#define   BINDER_WRITE_READ                     _IOWR('b', 1, struct binder_write_read)


#define       BINDER_SET_IDLE_TIMEOUT                   _IOW('b', 3, int64_t)


#define       BINDER_SET_MAX_THREADS                  _IOW('b', 5, size_t)


#define       BINDER_SET_IDLE_PRIORITY          _IOW('b', 6, int)


#define       BINDER_SET_CONTEXT_MGR                  _IOW('b', 7, int)


#define       BINDER_THREAD_EXIT           _IOW('b', 8, int)


#define BINDER_VERSION                            _IOWR('b', 9, struct binder_version)


这些都是命令。至于_IOW代表的什么意思,可以在


bionic\libc\kernel\common\asm-generic\Ioctl.h


中找到:


#define   _IOC_NRBITS        8                            //序数(number)字段的字位宽度,8bits


#define   _IOC_TYPEBITS      8                            //类型(type)字段的字位宽度,8bits


#define   _IOC_SIZEBITS      14                            //大小(size)字段的字位宽度,14bits


#define   _IOC_DIRBITS       2                            //方向(direction)字段的字位宽度,2bits


 


#define   _IOC_NRMASK       ((1 << _IOC_NRBITS)-1)    //序数字段的掩码,0x000000FF


#define   _IOC_TYPEMASK     ((1 << _IOC_TYPEBITS)-1)  //类型字段的掩码,0x000000FF


#define   _IOC_SIZEMASK     ((1 << _IOC_SIZEBITS)-1)   //大小字段的掩码,0x00003FFF


#define   _IOC_DIRMASK      ((1 << _IOC_DIRBITS)-1)    //方向字段的掩码,0x00000003


 


#define   _IOC_NRSHIFT     0                                //序数字段在整个字段中的位移,0


#define   _IOC_TYPESHIFT   (_IOC_NRSHIFT+_IOC_NRBITS)       //类型字段的位移,8


#define   _IOC_SIZESHIFT   (_IOC_TYPESHIFT+_IOC_TYPEBITS)   //大小字段的位移,16


#define   _IOC_DIRSHIFT    (_IOC_SIZESHIFT+_IOC_SIZEBITS)   //方向字段的位移,30


 


//方向标志


#define _IOC_NONE    0U     //没有数据传输


#define _IOC_WRITE   1U     //向设备写入数据,驱动程序必须从用户空间读入数据


#define _IOC_READ    2U     //从设备中读取数据,驱动程序必须向用户空间写入数据


 


//构造命令


#define _IOC(dir,type,nr,size) \


       (((dir)  << _IOC_DIRSHIFT) | \


        ((type) << _IOC_TYPESHIFT) | \


        ((nr)   << _IOC_NRSHIFT) | \


        ((size) << _IOC_SIZESHIFT))


 


//构造无参数的命令编号


#define _IO(type,nr)             _IOC(_IOC_NONE,(type),(nr),0)


 


//构造从驱动程序中读取数据的命令编号


#define _IOR(type,nr,size)     _IOC(_IOC_READ,(type),(nr),sizeof(size))


 


//用于向驱动程序写入数据命令


#define _IOW(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),sizeof(size))


 


//用于双向传输


#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))


 


//从命令参数中解析出数据方向,即写进还是读出


#define _IOC_DIR(nr)          (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)


 


//从命令参数中解析出类型type


#define _IOC_TYPE(nr)              (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)


 


//从命令参数中解析出序数number


#define _IOC_NR(nr)           (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)


 


//从命令参数中解析出用户数据大小


#define _IOC_SIZE(nr)         (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)


 


#define IOC_IN            (_IOC_WRITE << _IOC_DIRSHIFT)


#define IOC_OUT           (_IOC_READ << _IOC_DIRSHIFT)


#define IOC_INOUT         ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)


#define IOCSIZE_MASK      (_IOC_SIZEMASK << _IOC_SIZESHIFT)


#define IOCSIZE_SHIFT     (_IOC_SIZESHIFT)


所以从#define    BINDER_SET_CONTEXT_MGR       _IOW('b', 7, int)可以得出命令


BINDER_SET_CONTEXT_MGR的方向是向驱动写入数据,类型是’b’,命令编号是7,大小是sizeof(int)


         在驱动中(kernel\include\linux\Binder.c)可以找到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;


 


         /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/


 


         ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);


         if (ret)


                   return ret;


 


         mutex_lock(&binder_lock);


         thread = binder_get_thread(proc);


         if (thread == NULL) {


                   ret = -ENOMEM;


                   goto err;


         }


 


         switch (cmd) {


         case BINDER_WRITE_READ: {


                   struct binder_write_read bwr;


                   if (size != sizeof(struct binder_write_read)) {


                            ret = -EINVAL;


                            goto err;


                   }


                   if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {


                            ret = -EFAULT;


                            goto err;


                   }


                   if (binder_debug_mask & BINDER_DEBUG_READ_WRITE)


                            printk(KERN_INFO "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",


                                   proc->pid, thread->pid, bwr.write_size, bwr.write_buffer, bwr.read_size, bwr.read_buffer);


                   if (bwr.write_size > 0) {


                            ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);


                            if (ret < 0) {


                                     bwr.read_consumed = 0;


                                     if (copy_to_user(ubuf, &bwr, sizeof(bwr)))


                                               ret = -EFAULT;


                                     goto err;


                            }


                   }


                   if (bwr.read_size > 0) {


                            ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);


                            if (!list_empty(&proc->todo))


                                     wake_up_interruptible(&proc->wait);


                            if (ret < 0) {


                                     if (copy_to_user(ubuf, &bwr, sizeof(bwr)))


                                               ret = -EFAULT;


                                     goto err;


                            }


                   }


                   if (binder_debug_mask & BINDER_DEBUG_READ_WRITE)


                            printk(KERN_INFO "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",


                                   proc->pid, thread->pid, bwr.write_consumed, bwr.write_size, bwr.read_consumed, bwr.read_size);


                   if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {


                            ret = -EFAULT;


                            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:


                   if (binder_context_mgr_node != NULL) {


                            printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");


                            ret = -EBUSY;


                            goto err;


                   }


                   if (binder_context_mgr_uid != -1) {


                            if (binder_context_mgr_uid != current->euid) {


                                     printk(KERN_ERR "binder: BINDER_SET_"


                                            "CONTEXT_MGR bad uid %d != %d\n",


                                            current->euid,


                                            binder_context_mgr_uid);


                                     ret = -EPERM;


                                     goto err;


                            }


                   } else


                            binder_context_mgr_uid = current->euid;


                   binder_context_mgr_node = binder_new_node(proc, NULL, NULL);


                   if (binder_context_mgr_node == NULL) {


                            ret = -ENOMEM;


                            goto err;


                   }


                   binder_context_mgr_node->local_weak_refs++;


                   binder_context_mgr_node->local_strong_refs++;


                   binder_context_mgr_node->has_strong_ref = 1;


                   binder_context_mgr_node->has_weak_ref = 1;


                   break;


         case BINDER_THREAD_EXIT:


                   if (binder_debug_mask & BINDER_DEBUG_THREADS)


                            printk(KERN_INFO "binder: %d:%d exit\n",


                                   proc->pid, thread->pid);


                   binder_free_thread(proc, thread);


                   thread = NULL;


                   break;


         case BINDER_VERSION:


                   if (size != sizeof(struct binder_version)) {


                            ret = -EINVAL;


                            goto err;


                   }


                   if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &((struct binder_version *)ubuf)->protocol_version)) {


                            ret = -EINVAL;


                            goto err;


                   }


                   break;


         default:


                   ret = -EINVAL;


                   goto err;


         }


         ret = 0;


err:


         if (thread)


                   thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;


         mutex_unlock(&binder_lock);


         wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);


         if (ret && ret != -ERESTARTSYS)


                   printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);


         return ret;


}


下面来分析一下这个函数,首先看传进来的参数,第一个是一个file类型的指针,这里传过来的是binder驱动被打开以后建立的binder文件信息,其私有数据类型private_data是一个binder_proc的结构,主要包含了当前进程、进程ID、内存映射信息、binder的统计信息和线程信息等。在kernel\drivers\android\Binder.c中可以找到它的定义


struct binder_proc {


     struct hlist_node proc_node;                //实现双向链表


     struct rb_root threads;                     //线程队列,双向链表


     struct rb_root nodes;


     struct rb_root refs_by_desc;


     struct rb_root refs_by_node;


     int pid;                                  //进程ID


     struct vm_area_struct *vma;


     struct task_struct *tsk;


     void *buffer;


     size_t user_buffer_offset;


 


     struct list_head buffers;


     struct rb_root free_buffers;


     struct rb_root allocated_buffers;


     size_t free_async_space;


 


     struct page **pages;


     size_t buffer_size;


     uint32_t buffer_free;


     struct list_head todo;


     wait_queue_head_t wait;


     struct binder_stats stats;


     struct list_head delivered_death;


     int max_threads;


     int requested_threads;


     int requested_threads_started;


     int ready_threads;


     long default_priority;


};


binder_proc可以得到当前线程的信息,因为所有的线程信息都是存在struct rb_root threads;这个队列中,通过thread = binder_get_thread(proc);就可以找到当前的线程信息,具体做法是通过在队列中比较各个线程的pid和当前线程pid是否相同,如果找到了,那么把它的线程信息返回,如果没找到,那么新建一个线程,并把它加入到队列中,并初始化就绪线程队列等。返回的线程信息存放在一个binder_thread结构中:


struct binder_thread {


     struct binder_proc *proc;                   //所属进程


     struct rb_node rb_node;


     int pid;                                 //线程pid


     int looper;                               //线程状态信息


     struct binder_transaction *transaction_stack;    //定义了要接收和要发送的进程线程信息


     struct list_head todo;


     uint32_t return_error; /* Write failed, return error code in read buf */


     uint32_t return_error2; /* Write failed, return error code in read */


              /* buffer. Used when sending a reply to a dead process that */


              /* we are also waiting on */


     wait_queue_head_t wait;


     struct binder_stats stats;


};


第二个参数cmd就是BINDER_SET_CONTEXT_MGR,第三个参数在这里为0


执行ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);将是调用ioctl的进程挂起,直到binder返回。接下来来到switch(cmd)语句,在这里是binder对于接收的命令的响应,可以找到case BINDER_SET_CONTEXT_MGR这一项,这里主要做的事情是设置驱动中的全局变量binder_context_mgr_uid为当前进程的uid,并初始化一个binder_node赋值给全局变量binder_context_mgr_node。至此完成了注册Service Manager的工作。


         接下来程序将进入binder_loop,这是Service Manager程序的主体,在这里进入循环,开启监听,响应其他程序的请求。它的实现为:


<frameworks\base\cmds\servicemanager\Binder.c>


void binder_loop(struct binder_state *bs, binder_handler func)


{


    int res;


    struct binder_write_read bwr;


    unsigned readbuf[32];


 


    bwr.write_size = 0;


    bwr.write_consumed = 0;


    bwr.write_buffer = 0;


   


    readbuf[0] = BC_ENTER_LOOPER;


    binder_write(bs, readbuf, sizeof(unsigned));


 


    for (;;) {


        bwr.read_size = sizeof(readbuf);


        bwr.read_consumed = 0;


        bwr.read_buffer = (unsigned) readbuf;


 


        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);


 


        if (res < 0) {


            LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));


            break;


        }


 


        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);


        if (res == 0) {


            LOGE("binder_loop: unexpected reply?!\n");


            break;


        }


        if (res < 0) {


            LOGE("binder_loop: io error %d %s\n", res, strerror(errno));


            break;


        }


    }


}


 


binder_write_read是进行线程读写的数据结构,在kernel\include\linux中定义


struct binder_write_read {


         signed long write_size; /* bytes to write */


         signed long write_consumed; /* bytes consumed by driver */


         unsigned long      write_buffer;


         signed long read_size;   /* bytes to read */


         signed long read_consumed;  /* bytes consumed by driver */


         unsigned long      read_buffer;


};


binder_loop首先定义了一个数组unsigned readbuf[32],然后把第一个字节设置为BC_ENTER_LOOPER,这也是一个控制命令,同样的可以在驱动程序文件中看到其定义为:


enum BinderDriverCommandProtocol {


         BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),


         BC_REPLY = _IOW('c', 1, struct binder_transaction_data),


         /*


          * binder_transaction_data: the sent command.


          */


 


         BC_ACQUIRE_RESULT = _IOW('c', 2, int),


         /*


          * not currently supported


          * int:  0 if the last BR_ATTEMPT_ACQUIRE was not successful.


          * Else you have acquired a primary reference on the object.


          */


 


         BC_FREE_BUFFER = _IOW('c', 3, int),


         /*


          * void *: ptr to transaction data received on a read


          */


 


         BC_INCREFS = _IOW('c', 4, int),


         BC_ACQUIRE = _IOW('c', 5, int),


         BC_RELEASE = _IOW('c', 6, int),


         BC_DECREFS = _IOW('c', 7, int),


         /*


          * int:        descriptor


          */


 


         BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),


         BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),


         /*


          * void *: ptr to binder


          * void *: cookie for binder


          */


 


         BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc),


         /*


          * not currently supported


          * int: priority


          * int: descriptor


          */


 


         BC_REGISTER_LOOPER = _IO('c', 11),


         /*


          * No parameters.


          * Register a spawned looper thread with the device.


          */


 


         BC_ENTER_LOOPER = _IO('c', 12),


         BC_EXIT_LOOPER = _IO('c', 13),


         /*


          * No parameters.


          * These two commands are sent as an application-level thread


          * enters and exits the binder loop, respectively.  They are


          * used so the binder can have an accurate count of the number


          * of looping threads it has available.


          */


 


         BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_ptr_cookie),


         /*


          * void *: ptr to binder


          * void *: cookie


          */


 


         BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_ptr_cookie),


         /*


          * void *: ptr to binder


          * void *: cookie


          */


 


         BC_DEAD_BINDER_DONE = _IOW('c', 16, void *),


         /*


          * void *: cookie


          */


};


这是一组驱动请求协议,这和前面的分析是统一的。


函数binder_write(struct binder_state *bs, void *data, unsigned len)在初始化一个读写结构(把写数据的大小设置为len,字段设置为data,而读数据设置为0),发送了命令ioctl(bs->fd, BINDER_WRITE_READ, &bwr),这样跳到我们的binder_loop函数体对应的部分可以看到发生了什么事情。                   struct binder_write_read bwr;


                case BINDER_WRITE_READ: {


if (size != sizeof(struct binder_write_read)) {


                            ret = -EINVAL;


                            goto err;


                   }


                   if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {


                            ret = -EFAULT;


                            goto err;


                   }


                   if (binder_debug_mask & BINDER_DEBUG_READ_WRITE)


                            printk(KERN_INFO "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",


                                   proc->pid, thread->pid, bwr.write_size, bwr.write_buffer, bwr.read_size, bwr.read_buffer);


                   if (bwr.write_size > 0) {


                            ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);


                            if (ret < 0) {


                                     bwr.read_consumed = 0;


                                     if (copy_to_user(ubuf, &bwr, sizeof(bwr)))


                                               ret = -EFAULT;


                                     goto err;


                            }


                   }


                   if (bwr.read_size > 0) {


                            ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);


                            if (!list_empty(&proc->todo))


                                     wake_up_interruptible(&proc->wait);


                            if (ret < 0) {


                                     if (copy_to_user(ubuf, &bwr, sizeof(bwr)))


                                               ret = -EFAULT;


                                     goto err;


                            }


                   }


                   if (binder_debug_mask & BINDER_DEBUG_READ_WRITE)


                            printk(KERN_INFO "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",


                                   proc->pid, thread->pid, bwr.write_consumed, bwr.write_size, bwr.read_consumed, bwr.read_size);


                   if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {


                            ret = -EFAULT;


                            goto err;


                   }


                   break;


         }


       因为bwr.read_size>0,所以调用了函数binder_thread_write:


int


binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,


                       void __user *buffer, int size, signed long *consumed)


{


         uint32_t cmd;


         void __user *ptr = buffer + *consumed;


         void __user *end = buffer + size;


 


         while (ptr < end && thread->return_error == BR_OK) {


                   if (get_user(cmd, (uint32_t __user *)ptr))


                            return -EFAULT;


                   ptr += sizeof(uint32_t);


                   if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {


                            binder_stats.bc[_IOC_NR(cmd)]++;


                            proc->stats.bc[_IOC_NR(cmd)]++;


                            thread->stats.bc[_IOC_NR(cmd)]++;


                   }


                   switch (cmd) {


                   case BC_INCREFS:


                   case BC_ACQUIRE:


                   case BC_RELEASE:


                   case BC_DECREFS: {


                            uint32_t target;


                            struct binder_ref *ref;


                            const char *debug_string;


 


                            if (get_user(target, (uint32_t __user *)ptr))


                                     return -EFAULT;


                            ptr += sizeof(uint32_t);


                            if (target == 0 && binder_context_mgr_node &&


                                (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {


                                     ref = binder_get_ref_for_node(proc,


                                                      binder_context_mgr_node);


                                     if (ref->desc != target) {


                                               binder_user_error("binder: %d:"


                                                        "%d tried to acquire "


                                                        "reference to desc 0, "


                                                        "got %d instead\n",


                                                        proc->pid, thread->pid,


                                                        ref->desc);


                                     }


                            } else


                                     ref = binder_get_ref(proc, target);


                            if (ref == NULL) {


                                     binder_user_error("binder: %d:%d refcou"


                                               "nt change on invalid ref %d\n",


                                               proc->pid, thread->pid, target);


                                     break;


                            }


                            switch (cmd) {


                            case BC_INCREFS:


                                     debug_string = "IncRefs";


                                     binder_inc_ref(ref, 0, NULL);


                                     break;


                            case BC_ACQUIRE:


                                     debug_string = "Acquire";


                                     binder_inc_ref(ref, 1, NULL);


                                     break;


                            case BC_RELEASE:


                                     debug_string = "Release";


                                     binder_dec_ref(ref, 1);


                                     break;


                            case BC_DECREFS:


                            default:


                                     debug_string = "DecRefs";


                                     binder_dec_ref(ref, 0);


                                     break;


                            }


                            if (binder_debug_mask & BINDER_DEBUG_USER_REFS)


                                     printk(KERN_INFO "binder: %d:%d %s ref %d desc %d s %d w %d for node %d\n",


                                            proc->pid, thread->pid, debug_string, ref->debug_id, ref->desc, ref->strong, ref->weak, ref->node->debug_id);


                            break;


                   }


                   case BC_INCREFS_DONE:


                   case BC_ACQUIRE_DONE: {


                            void __user *node_ptr;


                            void *cookie;


                            struct binder_node *node;


 


                            if (get_user(node_ptr, (void * __user *)ptr))


                                     return -EFAULT;


                            ptr += sizeof(void *);


                            if (get_user(cookie, (void * __user *)ptr))


                                     return -EFAULT;


                            ptr += sizeof(void *);


                            node = binder_get_node(proc, node_ptr);


                            if (node == NULL) {


                                     binder_user_error("binder: %d:%d "


                                               "%s u%p no match\n",


                                               proc->pid, thread->pid,


                                               cmd == BC_INCREFS_DONE ?


                                               "BC_INCREFS_DONE" :


                                               "BC_ACQUIRE_DONE",


                                               node_ptr);


                                     break;


                            }


                            if (cookie != node->cookie) {


                                     binder_user_error("binder: %d:%d %s u%p node %d"


                                               " cookie mismatch %p != %p\n",


                                               proc->pid, thread->pid,


                                               cmd == BC_INCREFS_DONE ?


                                               "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",


                                               node_ptr, node->debug_id,


                                               cookie, node->cookie);


                                     break;


                            }


                            if (cmd == BC_ACQUIRE_DONE) {


                                     if (node->pending_strong_ref == 0) {


                                               binder_user_error("binder: %d:%d "


                                                        "BC_ACQUIRE_DONE node %d has "


                                                        "no pending acquire request\n",


                                                        proc->pid, thread->pid,


                                                        node->debug_id);


                                               break;


                                     }


                                     node->pending_strong_ref = 0;


                            } else {


                                     if (node->pending_weak_ref == 0) {


                                               binder_user_error("binder: %d:%d "


                                                        "BC_INCREFS_DONE node %d has "


                                                        "no pending increfs request\n",


                                                        proc->pid, thread->pid,


                                                        node->debug_id);


                                               break;


                                     }


                                     node->pending_weak_ref = 0;


                            }


                            binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0);


                            if (binder_debug_mask & BINDER_DEBUG_USER_REFS)


                                     printk(KERN_INFO "binder: %d:%d %s node %d ls %d lw %d\n",


                                            proc->pid, thread->pid, cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE", node->debug_id, node->local_strong_refs, node->local_weak_refs);


                            break;


                   }


                   case BC_ATTEMPT_ACQUIRE:


                            printk(KERN_ERR "binder: BC_ATTEMPT_ACQUIRE not supported\n");


                            return -EINVAL;


                   case BC_ACQUIRE_RESULT:


                            printk(KERN_ERR "binder: BC_ACQUIRE_RESULT not supported\n");


                            return -EINVAL;


 


                   case BC_FREE_BUFFER: {


                            void __user *data_ptr;


                            struct binder_buffer *buffer;


 


                            if (get_user(data_ptr, (void * __user *)ptr))


                                     return -EFAULT;


                            ptr += sizeof(void *);


 


                            buffer = binder_buffer_lookup(proc, data_ptr);


                            if (buffer == NULL) {


                                     binder_user_error("binder: %d:%d "


                                               "BC_FREE_BUFFER u%p no match\n",


                                               proc->pid, thread->pid, data_ptr);


                                     break;


                            }


                            if (!buffer->allow_user_free) {


                                     binder_user_error("binder: %d:%d "


                                               "BC_FREE_BUFFER u%p matched "


                                               "unreturned buffer\n",


                                               proc->pid, thread->pid, data_ptr);


                                     break;


                            }


                            if (binder_debug_mask & BINDER_DEBUG_FREE_BUFFER)


                                     printk(KERN_INFO "binder: %d:%d BC_FREE_BUFFER u%p found buffer %d for %s transaction\n",


                                            proc->pid, thread->pid, data_ptr, buffer->debug_id,


                                            buffer->transaction ? "active" : "finished");


 


                            if (buffer->transaction) {


                                     buffer->transaction->buffer = NULL;


                                     buffer->transaction = NULL;


                            }


                            if (buffer->async_transaction && buffer->target_node) {


                                     BUG_ON(!buffer->target_node->has_async_transaction);


                                     if (list_empty(&buffer->target_node->async_todo))


                                               buffer->target_node->has_async_transaction = 0;


                                     else


                                               list_move_tail(buffer->target_node->async_todo.next, &thread->todo);


                            }


                            binder_transaction_buffer_release(proc, buffer, NULL);


                            binder_free_buf(proc, buffer);


                            break;


                   }


 


                   case BC_TRANSACTION:


                   case BC_REPLY: {


                            struct binder_transaction_data tr;


 


                            if (copy_from_user(&tr, ptr, sizeof(tr)))


                                     return -EFAULT;


                            ptr += sizeof(tr);


                            binder_transaction(proc, thread, &tr, cmd == BC_REPLY);


                            break;


                   }


 


                   case BC_REGISTER_LOOPER:


                            if (binder_debug_mask & BINDER_DEBUG_THREADS)


                                     printk(KERN_INFO "binder: %d:%d BC_REGISTER_LOOPER\n",


                                            proc->pid, thread->pid);


                            if (thread->looper & BINDER_LOOPER_STATE_ENTERED) {


                                     thread->looper |= BINDER_LOOPER_STATE_INVALID;


                                     binder_user_error("binder: %d:%d ERROR:"


                                               " BC_REGISTER_LOOPER called "


                                               "after BC_ENTER_LOOPER\n",


                                               proc->pid, thread->pid);


                            } else if (proc->requested_threads == 0) {


                                     thread->looper |= BINDER_LOOPER_STATE_INVALID;


                                     binder_user_error("binder: %d:%d ERROR:"


                                               " BC_REGISTER_LOOPER called "


                                               "without request\n",


                                               proc->pid, thread->pid);


                            } else {


                                     proc->requested_threads--;


                                     proc->requested_threads_started++;


                            }


                            thread->looper |= BINDER_LOOPER_STATE_REGISTERED;


                            break;


                   case BC_ENTER_LOOPER:


                            if (binder_debug_mask & BINDER_DEBUG_THREADS)


                                     printk(KERN_INFO "binder: %d:%d BC_ENTER_LOOPER\n",


                                            proc->pid, thread->pid);


                            if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {


                                     thread->looper |= BINDER_LOOPER_STATE_INVALID;


                                     binder_user_error("binder: %d:%d ERROR:"


                                               " BC_ENTER_LOOPER called after "


                                               "BC_REGISTER_LOOPER\n",


                                               proc->pid, thread->pid);


                            }


                            thread->looper |= BINDER_LOOPER_STATE_ENTERED;


                            break;


                   case BC_EXIT_LOOPER:


                            if (binder_debug_mask & BINDER_DEBUG_THREADS)


                                     printk(KERN_INFO "binder: %d:%d BC_EXIT_LOOPER\n",


                                            proc->pid, thread->pid);


                            thread->looper |= BINDER_LOOPER_STATE_EXITED;


                            break;


 


                   case BC_REQUEST_DEATH_NOTIFICATION:


                   case BC_CLEAR_DEATH_NOTIFICATION: {


                            uint32_t target;


                            void __user *cookie;


                            struct binder_ref *ref;


                            struct binder_ref_death *death;


 


                            if (get_user(target, (uint32_t __user *)ptr))


                                     return -EFAULT;


                            ptr += sizeof(uint32_t);


                            if (get_user(cookie, (void __user * __user *)ptr))


                                     return -EFAULT;


                            ptr += sizeof(void *);


                            ref = binder_get_ref(proc, target);


                            if (ref == NULL) {


                                     binder_user_error("binder: %d:%d %s "


                                               "invalid ref %d\n",


                                               proc->pid, thread->pid,


                                               cmd == BC_REQUEST_DEATH_NOTIFICATION ?


                                               "BC_REQUEST_DEATH_NOTIFICATION" :


                                               "BC_CLEAR_DEATH_NOTIFICATION",


                                               target);


                                     break;


                            }


 


                            if (binder_debug_mask & BINDER_DEBUG_DEATH_NOTIFICATION)


                                     printk(KERN_INFO "binder: %d:%d %s %p ref %d desc %d s %d w %d for node %d\n",


                                            proc->pid, thread->pid,


                                            cmd == BC_REQUEST_DEATH_NOTIFICATION ?


                                            "BC_REQUEST_DEATH_NOTIFICATION" :


                                            "BC_CLEAR_DEATH_NOTIFICATION",


                                            cookie, ref->debug_id, ref->desc,


                                            ref->strong, ref->weak, ref->node->debug_id);


 


                            if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {


                                     if (ref->death) {


                                               binder_user_error("binder: %d:%"


                                                        "d BC_REQUEST_DEATH_NOTI"


                                                        "FICATION death notific"


                                                        "ation already set\n",


                                                        proc->pid, thread->pid);


                                               break;


                                     }


                                     death = kzalloc(sizeof(*death), GFP_KERNEL);


                                     if (death == NULL) {


                                               thread->return_error = BR_ERROR;


                                               if (binder_debug_mask & BINDER_DEBUG_FAILED_TRANSACTION)


                                                        printk(KERN_INFO "binder: %d:%d "


                                                                 "BC_REQUEST_DEATH_NOTIFICATION failed\n",


                                                                 proc->pid, thread->pid);


                                               break;


                                     }


                                     binder_stats.obj_created[BINDER_STAT_DEATH]++;


                                     INIT_LIST_HEAD(&death->work.entry);


                                     death->cookie = cookie;


                                     ref->death = death;


                                     if (ref->node->proc == NULL) {


                                               ref->death->work.type = BINDER_WORK_DEAD_BINDER;


                                               if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {


                                                        list_add_tail(&ref->death->work.entry, &thread->todo);


                                               } else {


                                                        list_add_tail(&ref->death->work.entry, &proc->todo);


                                                        wake_up_interruptible(&proc->wait);


                                               }


                                     }


                            } else {


                                     if (ref->death == NULL) {


                                               binder_user_error("binder: %d:%"


                                                        "d BC_CLEAR_DEATH_NOTIFI"


                                                        "CATION death notificat"


                                                        "ion not active\n",


                                                        proc->pid, thread->pid);


                                               break;


                                     }


                                     death = ref->death;


                                     if (death->cookie != cookie) {


                                               binder_user_error("binder: %d:%"


                                                        "d BC_CLEAR_DEATH_NOTIFI"


                                                        "CATION death notificat"


                                                        "ion cookie mismatch "


                                                        "%p != %p\n",


                                                        proc->pid, thread->pid,


                                                        death->cookie, cookie);


                                               break;


                                     }


                                     ref->death = NULL;


                                     if (list_empty(&death->work.entry)) {


                                               death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;


                                               if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {


                                                        list_add_tail(&death->work.entry, &thread->todo);


                                               } else {


                                                        list_add_tail(&death->work.entry, &proc->todo);


                                                        wake_up_interruptible(&proc->wait);


                                               }


                                     } else {


                                               BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER);


                                               death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;


                                     }


                            }


                   } break;


                   case BC_DEAD_BINDER_DONE: {


                            struct binder_work *w;


                            void __user *cookie;


                            struct binder_ref_death *death = NULL;


                            if (get_user(cookie, (void __user * __user *)ptr))


                                     return -EFAULT;


 


                            ptr += sizeof(void *);


                            list_for_each_entry(w, &proc->delivered_death, entry) {


                                     struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work);


                                     if (tmp_death->cookie == cookie) {


                                               death = tmp_death;


                                               break;


                                     }


                            }


                            if (binder_debug_mask & BINDER_DEBUG_DEAD_BINDER)


                                     printk(KERN_INFO "binder: %d:%d BC_DEAD_BINDER_DONE %p found %p\n",


                                            proc->pid, thread->pid, cookie, death);


                            if (death == NULL) {


                                     binder_user_error("binder: %d:%d BC_DEAD"


                                               "_BINDER_DONE %p not found\n",


                                               proc->pid, thread->pid, cookie);


                                     break;


                            }


 


                            list_del_init(&death->work.entry);


                            if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) {


                                     death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;


                                     if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {


                                               list_add_tail(&death->work.entry, &thread->todo);


                                     } else {


                                               list_add_tail(&death->work.entry, &proc->todo);


                                               wake_up_interruptible(&proc->wait);


                                     }


                            }


                   } break;


 


                   default:


                            printk(KERN_ERR "binder: %d:%d unknown command %d\n", proc->pid, thread->pid, cmd);


                            return -EINVAL;


                   }


                   *consumed = ptr - buffer;


         }


         return 0;


}

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
13
关闭 站长推荐上一条 /3 下一条