分析Android的进程通信机制
2015-11-27 20:57
555 查看
原文地址:Android进程间通信(IPC)机制Binder简要介绍和学习计划
1. Service
Manager是如何成为一个守护进程的?即Service Manager是如何告知Binder驱动程序它是Binder机制的上下文管理者。
2. Server和Client是如何获得Service
Manager接口的?即defaultServiceManager接口是如何实现的。
3. Server是如何把自己的服务启动起来的?Service
Manager在Server启动的过程中是如何为Server提供服务的?即IServiceManager::addService接口是如何实现的。
4 Service
Manager是如何为Client提供服务的?即IServiceManager::getService接口是如何实现的。
5. Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析。
2、Binder是一种进程间通信机制,这是一种类似于COM和CORBA分布式组件架构,其实就是提供远程过程调用(RPC)功能。
3、在Android系统的Binder机制中,有一系列组件组成,分别是Client、Server、Service Manager和Binder驱动程序,其中前三者运行在用户空间,最后一个运行在内核空间。
4、学习Binder蛮难的,最好的方法是去阅读源码。
5、组件Client、Server、Service Manager和Binder驱动程序的关系如图所示:
Client、Server和Service Manager实现在用户空间中,Binder驱动程序实现在内核空间中。
注意点一:Binder驱动程序和Service Manager在Android平台中已经实现,开发者只需要在用户空间实现自己的Client和Service。
注意点二:Binder驱动程序提供设备文件“/dev/binder”与用户空间交互,Client、Server和Service Manager通过文件操作函数open()和ioctl()与Binder驱动程序进行通信。
注意点三:Service Manager是一个守护进程,用来管理Server,并向Clident提供查询Server接口的能力。
注意点四:Client和Server之间的进程间通信通过Binder驱动程序间接实现。
参考博客:binder总结
参考博客:Android Service Manager源码剖析
1、Service Manager必然要和Client以及Server进行通信,它们三者是分别运行在独立的进程当中的,这样它们之间也属于进程间的通信,也是采用Binder机制进行通信的,所以它同时也充当Server的角色。
2、Service Manager在用户空间的源代码位于frameworks/base/cmds/servicemanager目录下,主要由文件binder.h、binder.c和service_manager.c组成。
3、Service Manager的入口位于文件service_manager.c中的函数main()中,代码如下:
“我的理解”:下面bs用来表示一个binder的状态,其中包含了binder的文件描述符,binder的进程空间起始地址以及占用的空间大小,就是说bs代表了一个binder和它的属性,然后通过binder_open来打开一个binder设备文件,这样bs就算被真正赋值了,随后通过binder_become_context_manager将bs设为守护进程,最后把bs传到binder_loop中让它一直等待Client的请求。
上述函数main()主要有如下三个功能:
(1)打开Binder设备文件。
(2)告诉Binder驱动程序自己是Binder上下文管理者,即我们前面所说的守护进程。
(3)进入一个无穷循环,充当Service的角色,等待Client的请求。
其中的结构体 binder_state 定义在文件 frameworks/base/cmds/servicemanager/binder.c 中,代码如下:
宏 BINDER_SERVICE_MANAGER 在文件 frameworks/base/cmds/servicemanager/binder.h 中定义,代码如下:
通过文件操作函数open()打开设备文件“/dev/binder”,此设备文件是在Binder驱动程序模块初始化的时候创建的。接下来先看一下这个设备文件的创建过程,来到 kernel/common/drivers/staging/android目录,打开文件 binder.c,可以看到如下模块初始化入口binder_init:
疑问点!!!!下面这个函数是从哪里蹦出来的!!!!
上述函数的主要作用是创建一个名为 binder_proc 的数据结构,用此数据结构来保存打开设备文件 “/dev/binder” 的进程的上下文信息,并且将这个进程上下文信息保存在打开文件结构 file 的私有数据成员变量 private_data 中。这样在执行其他文件操作时,就通过打开文件结构 file 来取回这个进程上下文信息了。 这个进程上下文信息同时还会保存在一个全局哈希表 binder_procs 中,供驱动程序内部使用。哈希表 binder_procs 定义在文件的开头:
而结构体 struct binder_proc 也被定义在文件 kernel/common/drivers/staging/android/binder.c 中:
它里面基本上都是结构体变量,其中有四个比较重要的成员变量,它们都是表示红黑树的节点,也就是说,binder_proc 分别挂着4个红黑树下:
(1)threads树:struct rb_root threads;
用来保存 binder_proc 进程内用于处理用户请求的线程,它的最大数量由 max_threads 来决定。
(2)node树:struct rb_root nodes;
用来保存 binder_proc 进程内的Binder实体。
(3)(4)refs_by_desc树和refs_by_node树:struct rb_root refs_by_desc,refs_by_node;
用来保存 binder_proc 进程内的Binder引用,即引用其他进程的Binder实体,他分别用两种方式来组织红黑树:一种是以句柄作为key值来组织;另一种是以引用的实体节点的地址值作为key值来组织。它们都是表示同一样东西,只不过是为了内部查找方便而用两个红黑树表示。
注意点一(第一段代码中):在Linux系统中,结构体vm_area_struct表示的虚拟地址是给进程使用的,而结构体vm_struct表示的虚拟地址是给内核使用的,它们对应的物理页面都可以是不连续的。
结构体vm_area_struct表示的徐地址空间范围是0~3G,而结构体vm_struct表示的地址空间范围是(3G+896M+8M)~4G。
为什么结构体vm_struct表示的地址空间范围不是3~4G呢?因为3G~(3G+896M)范围的地址是用来映射连续的物理页面的,这个范围的虚拟地址和对应的实际的物理地址有着简单的对应关系,即对应0~896M的物理地址空间。
而(3G+896M)~(3G+896M+8M)是安全保护区域。例如所有指向这8M地址空间的指针都是非法的。
那此处为什么会同时使用进程虚拟地址控件和内核虚拟地址空间来映射同一个物理页面呢?这就是BInder进程间通信机制的精髓所在了。在同一个物理页面,一方映射到进程虚拟地址空间,一方映射到内核虚拟地址空间,这样进程和内核之间就可以减少一次内存拷贝工作,提高了进程之间的通信效率。举个例子如,Client要将一块内存数据传递给Server,一般的做法是,Client将这块数据从它的进程空间拷贝到内核空间中,然后内核再将这个数据从内核空间拷贝到Server的进程空间,这样,Server就可以访问这个数据了。但是在这种方法中,执行了两次内存拷贝操作,而采用我们上面提到的方法,只需要把Client进程空间的数据拷贝一次到内核空间,然后Server与内核共享这个数据就可以了,整个过程只需要执行一次内存拷贝,提高了效率。
binder_mmap的原理讲完了,这个函数的逻辑就好理解了。不过,这里还是先要解释一下struct binder_proc结构体的几个成员变量:
buffer成员变量是一个void*指针,它表示要映射的物理内存在内核空间中的起始位置;
buffer_size成员变量是一个size_t类型的变量,表示要映射的内存的大小;
pages成员变量是一个struct page*类型的数组,struct
page是用来描述物理页面的数据结构;
user_buffer_offset成员变量是一个ptrdiff_t类型的变量,它表示的是内核使用的虚拟地址与进程使用的虚拟地址之间的差值,即如果某个物理页面在内核空间中对应的虚拟地址是addr的话,那么这个物理页面在进程空间对应的虚拟地址就为addr + user_buffer_offset。
接下来还需要看一下Binder驱动程序管理内存映射地址空间的方法,即如何管理buffer~(buffer+buffer_size)这段地址空间的,这个地址空间被划分为一段一段来管理,每一段是用结构体binder_buffer来描述的:
首先是调用alloc_page来分配一个物理页面,这个函数返回一个struct page物理页面描述符,根据这个描述的内容初始化好struct vm_struct tmp_area结构体,然后通过map_vm_area将这个物理页面插入到tmp_area描述的内核空间去,接着通过page_addr + proc->user_buffer_offset获得进程虚拟空间地址,并通过vm_insert_page函数将这个物理页面插入到进程地址空间去,参数vma代表了要插入的进程的地址空间。
这样,frameworks/base/cmds/servicemanager/binder.c文件中的binder_open函数就描述完了,回到frameworks/base/cmds/servicemanager/service_manager.c文件中的main函数,下一步就是调用binder_become_context_manager来通知Binder驱动程序自己是Binder机制的上下文管理者,即守护进程。binder_become_context_manager函数位于frameworks/base/cmds/servicemanager/binder.c文件中:
就是通过调用 ioctl 文件操作函数通知Binder驱动程序自己是守护进程,命令号是后面的那个宏定义:
binder_ioctl()函数中涉及到了两个数据结构:
(1)结构体binder_thread:表示一个线程,这里就是执行binder_become_context_manager函数的线程:
struct binder_proc有一个成员变量threads,它的类型是rb_root,它表示一查红黑树,把属于这个进程的所有线程都组织起来,struct
binder_thread的成员变量rb_node就是用来链入这棵红黑树的节点了。
looper成员变量表示线程的状态,它可以取下面这几个值:
todo表示发往该线程的数据列表,
return_error和return_error2表示操作结果返回码,
wait用来阻塞线程等待某个事件的发生,
stats用来保存一些统计信息。这些成员变量遇到的时候再分析它们的作用。
(2)数据结构binder_node:表示一个binder实体:
rb_node和dead_node组成一个联合体。 如果这个Binder实体还在正常使用,则使用rb_node来连入proc->nodes所表示的红黑树的节点,这棵红黑树用来组织属于这个进程的所有Binder实体;如果这个Binder实体所属的进程已经销毁,而这个Binder实体又被其它进程所引用,则这个Binder实体通过dead_node进入到一个哈希表中去存放。
proc成员变量就是表示这个Binder实例所属于进程了。
refs成员变量把所有引用了该Binder实体的Binder引用连接起来构成一个链表。
internal_strong_refs、local_weak_refs和local_strong_refs表示这个Binder实体的引用计数。
ptr和cookie成员变量分别表示这个Binder实体在用户空间的地址以及附加数据。其余的成员变量就不描述了,遇到的时候再分析。
这样就进入到Binder驱动程序的函数binder_ioctl():我们只关注BINDER_SET_CONTEXT_MGR命令:
在binder_ioctl函数中,首先是通过filp->private_data获得proc变量,这里binder_mmap函数是一样的。接着通过binder_get_thread函数获得线程信息,我们来看一下这个函数:
在binder_get_thread()中,把当前线程current的pid作为键值,在进程proc->threads表示的红黑树中进行查找,看是否已经为当前线程创建过了 binder_thread 信息。在这个场景下,由于当前线程是第一次进到这里,所以肯定找不到,即*p == NULL成立,于是就为当前线程创建一个线程上下文信息结构体binder_thread,并初始化相应成员变量,并插入到proc->threads所表示的红黑树中去,下次要使用时就可以从proc中找到了。注意,这里的thread->looper=BINDER_LOOPER_STATE_NEED_RRETURN。
再回到binder_ioctl函数中,接下来会有两个全局变量 binder_context_mgr_node 和 binder_context_mgr_uid,定义如下:
binder_context_mgr_node用来表示Service Manager实体,binder_context_mgr_uid表示Service Manager守护进程的uid。在这个场景下,由于当前线程是第一次进到这里,所以binder_context_mgr_node为NULL,binder_context_mgr_uid为-1,于是初始化binder_context_mgr_uid为current->cred->euid,这样,当前线程就成为Binder机制的守护进程了,并且通过binder_new_node为Service
Manager创建Binder实体:
binder_new_node函数中传来的ptr和cookie都为NULL。函数中首先检查proc->nodes红黑树中是否已经存在以ptr为键值的node,如果已经存在则返回NULL。在这种情况下,由于当前线程是第一次进入到这里,所以肯定不存在,于是就新建了一个ptr为NULL的binder_node,并且初始化其他成员变量,并插入到proc->nodes红黑树中去。
当binder_new_node返回到函数binder_ioctl()后,会把新建的binder_node指针保存在binder_content_mgr_node中,然后又初始化binder_context_mgr_node的引用计数值。这样的执行BINDER_SET)CONTEXT_MGR命令完毕,在函数binger_ioctl()返回之前执行下面的语句:
首先通过binder_write执行BC_ENTER_LOOPER命令以告诉Binder驱动程序,Service Manager马上要进入循环。
由此可见,这个binder_write其实是设置了binder_write_read结构体里面的write部分,而read部分为空(read_size为0,read_buffer为NULL),然后通过ioctl发送BINDER_WRITE_READ命令,然后又是调用ioctl函数进入到Binder驱动程序的binder_ioctl函数,这里我们也只是关注BC_ENTER_LOOPER相关的逻辑:
在此还需要理解设备文件“/dev/binder”操作函数 ioctl 的操作码BINDER_WRITE_READ,首先看其定义:
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
此io操纵码有一个形式为struct binder_write_read的参数:
这里顺便说一下,用户空间程序和Binder驱动程序交互大多数都是通过BINDER_WRITE_READ命令的,write_bufffer和read_buffer所指向的数据结构还指定了具体要执行的操作,write_bufffer和read_buffer所指向的结构体是struct binder_transaction_data:
有一个联合体target,当这个BINDER_WRITE_READ命令的目标对象是本地Binder实体时,就使用ptr来表示这个对象在本进程中的地址,否则就使用handle来表示这个Binder实体的引用。只有目标对象是Binder实体时,cookie成员变量才有意义,表示一些附加数据,由Binder实体来解释这个个附加数据。code表示要对目标对象请求的命令代码,有很多请求代码,这里就不列举了,在这个场景中,就是BC_ENTER_LOOPER了,用来告诉Binder驱动程序,
Service Manager要进入循环了。其余的请求命令代码可以参考kernel/common/drivers/staging/android/binder.h文件中定义的两个枚举类型BinderDriverReturnProtocol和BinderDriverCommandProtocol。
flags成员变量表示事务标志:每一个标志位所表示的意义看注释就行了,遇到时再具体分析。
open("/dev/binder", O_RDWR);
(2)建立128K内存映射:
mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
(3)通知Binder驱动程序它是守护进程:
binder_become_context_manager(bs);
(4)进入循环等待请求的到来:
binder_loop(bs, svcmgr_handler);
在这个过程中,在Binder驱动程序中建立了一个struct binder_proc结构、一个struct binder_thread结构和一个struct binder_node结构,这样,Service Manager就在Android系统的进程间通信机制Binder担负起守护进程的职责了。
我们知道,Service Manager在Binder机制中既充当守护进程的角色,同时它也充当着Server角色,然而它又与一般的Server不一样。对于普通的Server来说,Client如果想要获得Server的远程接口,那么必须通过Service Manager远程接口提供的getService接口来获得,这本身就是一个使用Binder机制来进行进程间通信的过程。而对于Service
Manager这个Server来说,Client如果想要获得Service Manager远程接口,却不必通过进程间通信机制来获得,因为Service Manager远程接口是一个特殊的Binder引用,它的引用句柄一定是0。
获取Service Manager远程接口的函数是 defaultServiceManager(),此函数声明在文件 frameworks/base/include/binder/IServiceManager.h中,代码如下:
函数defaultServiceManager在 frameworks/base/include/binder/IServiceManager.cpp文件中实现:
其中gDefaultServiceManagerLock和gDefaultServiceManager是全局变量,定义在frameworks/base/libs/binder/Static.cpp文件中:
在继续介绍interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL))的实现之前,先来看一个类图,这能够帮助我们了解Service Manager远程接口的创建过程:
参考资料Android深入浅出之Binder机制一文的读者,应该会比较容易理解这个图。这个图表明了,BpServiceManager类继承了BpInterface<IServiceManager>类,BpInterface是一个模板类,它定义在frameworks/base/include/binder/IInterface.h文件中:
理解了这些概念之后,就可以继续分析创建Service Manager远程接口的过程了,最终目的是要创建一个BpServiceManager实例,并且返回它的IServiceManager接口。创建Service Manager远程接口主要是下面语句:
首先是调用ProcessState::self函数,self函数是ProcessState的静态成员函数,它的作用是返回一个全局唯一的ProcessState实例变量,就是单例模式了,这个变量名为gProcess。如果gProcess尚未创建,就会执行创建操作,在ProcessState的构造函数中,会通过open文件操作函数打开设备文件/dev/binder,并且返回来的设备文件描述符保存在成员变量mDriverFD中。接着调用gProcess->getContextObject函数来获得一个句柄值为0的Binder引用,即BpBinder了,于是创建Service
Manager远程接口的语句可以简化为:
在Android系统的Binder机制中,Server和Client拿到这个Service Manager远程接口之后怎么用呢?
(1) 对Server来说,就是调用IServiceManager::addService这个接口来和Binder驱动程序交互了,即调用BpServiceManager::addService 。而BpServiceManager::addService又会调用通过其基类BpRefBase的成员函数remote获得原先创建的BpBinder实例,接着调用BpBinder::transact成员函数。在BpBinder::transact函数中,又会调用IPCThreadState::transact成员函数,这里就是最终与Binder驱动程序交互的地方了。回忆一下前面的类图,IPCThreadState有一个PorcessState类型的成中变量mProcess,而mProcess有一个成员变量mDriverFD,它是设备文件/dev/binder的打开文件描述符,因此,IPCThreadState就相当于间接在拥有了设备文件/dev/binder的打开文件描述符,于是,便可以与Binder驱动程序交互了。
(2)对Client来说,就是调用IServiceManager::getService这个接口来和Binder驱动程序交互了。具体过程上述Server使用Service Manager的方法是一样的,这里就不再累述了。
IServiceManager::addService和IServiceManager::getService这两个函数的具体实现,在下面两篇文章中,会深入到Binder驱动程序这一层,进行详细的源代码分析,以便更好地理解Binder进程间通信机制,敬请关注。
原文地址:Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
本文通过一个具体的例子来说明Binder机制中Server的启动过程。我们知道,在Android系统中,提供了多媒体播放的功能,这个功能是以服务的形式来提供的。这里,我们就通过分析MediaPlayerService的实现来了解Media Server的启动过程。
首先,看一下MediaPlayerService的类图,以便我们理解下面要描述的内容。
我们将要介绍的主角MediaPlayerService继承于BnMediaPlayerService类,熟悉Binder机制的同学应该知道BnMediaPlayerService是一个Binder Native类,用来处理Client请求的。BnMediaPlayerService继承于BnInterface<IMediaPlayerService>类,BnInterface是一个模板类,它定义在frameworks/base/include/binder/IInterface.h文件中:
实际上,BnMediaPlayerService并不是直接接收到Client处发送过来的请求,而是使用了IPCThreadState接收Client处发送过来的请求,而IPCThreadState又借助了ProcessState类来与Binder驱动程序交互。有关IPCThreadState和ProcessState的关系,可以参考上一篇文章浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service
Manager接口之路,接下来也会有相应的描述。IPCThreadState接收到了Client处的请求后,就会调用BBinder类的transact函数,并传入相关参数,BBinder类的transact函数最终调用BnMediaPlayerService类的onTransact函数,于是,就开始真正地处理Client的请求了。
了解了MediaPlayerService类结构之后,就要开始进入到本文的主题了。
首先,看看MediaPlayerService是如何启动的。启动MediaPlayerService的代码位于frameworks/base/media/mediaserver/main_mediaserver.cpp文件中:
这里我们不关注AudioFlinger和CameraService相关的代码。
先看下面这句代码:
这个函数有两个关键地方,一是通过open_driver函数打开Binder设备文件/dev/binder,并将打开设备文件描述符保存在成员变量mDriverFD中;二是通过mmap来把设备文件/dev/binder映射到内存中。
先看open_driver函数的实现,这个函数同样位于frameworks/base/libs/binder/ProcessState.cpp文件中:
open在Binder驱动程序中的具体实现,请参考前面一篇文章浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路,这里不再重复描述。打开/dev/binder设备文件后,Binder驱动程序就为MediaPlayerService进程创建了一个struct
binder_proc结构体实例来维护MediaPlayerService进程上下文相关信息。
我们来看一下ioctl文件操作函数执行BINDER_VERSION命令的过程:
很简单,只是将BINDER_CURRENT_PROTOCOL_VERSION写入到传入的参数arg指向的用户缓冲区中去就返回了。BINDER_CURRENT_PROTOCOL_VERSION是一个宏,定义在kernel/common/drivers/staging/android/binder.h文件中:
这里有一个重要的地方要注意的是,由于这里是打开设备文件/dev/binder之后,第一次进入到binder_ioctl函数,因此,这里调用binder_get_thread的时候,就会为当前线程创建一个struct binder_thread结构体变量来维护线程上下文信息,具体可以参考浅谈Service
Manager成为Android进程间通信(IPC)机制Binder守护进程之路一文。
接着我们再来看一下ioctl文件操作函数执行BINDER_SET_MAX_THREADS命令的过程:
1. Service
Manager是如何成为一个守护进程的?即Service Manager是如何告知Binder驱动程序它是Binder机制的上下文管理者。
2. Server和Client是如何获得Service
Manager接口的?即defaultServiceManager接口是如何实现的。
3. Server是如何把自己的服务启动起来的?Service
Manager在Server启动的过程中是如何为Server提供服务的?即IServiceManager::addService接口是如何实现的。
4 Service
Manager是如何为Client提供服务的?即IServiceManager::getService接口是如何实现的。
5. Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析。
一、Android的进程间通信(IPC)机制Binder
1、Service运行在独立的进程中,Activity有可能运行在同一个进程中,也有可能运行中不同的进程中。2、Binder是一种进程间通信机制,这是一种类似于COM和CORBA分布式组件架构,其实就是提供远程过程调用(RPC)功能。
3、在Android系统的Binder机制中,有一系列组件组成,分别是Client、Server、Service Manager和Binder驱动程序,其中前三者运行在用户空间,最后一个运行在内核空间。
4、学习Binder蛮难的,最好的方法是去阅读源码。
5、组件Client、Server、Service Manager和Binder驱动程序的关系如图所示:
Client、Server和Service Manager实现在用户空间中,Binder驱动程序实现在内核空间中。
注意点一:Binder驱动程序和Service Manager在Android平台中已经实现,开发者只需要在用户空间实现自己的Client和Service。
注意点二:Binder驱动程序提供设备文件“/dev/binder”与用户空间交互,Client、Server和Service Manager通过文件操作函数open()和ioctl()与Binder驱动程序进行通信。
注意点三:Service Manager是一个守护进程,用来管理Server,并向Clident提供查询Server接口的能力。
注意点四:Client和Server之间的进程间通信通过Binder驱动程序间接实现。
二、Service Manager是Binder机制的上下文管理者
参考博客:Android Service Manager源码剖析参考博客:binder总结
参考博客:Android Service Manager源码剖析
1、Service Manager必然要和Client以及Server进行通信,它们三者是分别运行在独立的进程当中的,这样它们之间也属于进程间的通信,也是采用Binder机制进行通信的,所以它同时也充当Server的角色。
2、Service Manager在用户空间的源代码位于frameworks/base/cmds/servicemanager目录下,主要由文件binder.h、binder.c和service_manager.c组成。
3、Service Manager的入口位于文件service_manager.c中的函数main()中,代码如下:
int main(int argc, char **argv) { struct binder_state *bs; // void *svcmgr = BINDER_SERVICE_MANAGER; //句柄值为0 bs = binder_open(128*1024); //128K if (binder_become_context_manager(bs)) { ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } svcmgr_handle = svcmgr; binder_loop(bs, svcmgr_handler); return 0; }
“我的理解”:下面bs用来表示一个binder的状态,其中包含了binder的文件描述符,binder的进程空间起始地址以及占用的空间大小,就是说bs代表了一个binder和它的属性,然后通过binder_open来打开一个binder设备文件,这样bs就算被真正赋值了,随后通过binder_become_context_manager将bs设为守护进程,最后把bs传到binder_loop中让它一直等待Client的请求。
上述函数main()主要有如下三个功能:
(1)打开Binder设备文件。
(2)告诉Binder驱动程序自己是Binder上下文管理者,即我们前面所说的守护进程。
(3)进入一个无穷循环,充当Service的角色,等待Client的请求。
其中的结构体 binder_state 定义在文件 frameworks/base/cmds/servicemanager/binder.c 中,代码如下:
struct binder_state{ int fd; // 文件描述符,即表示打开的“/dev/binder”设备文件描述符。 void *mapped; // 表示把设备文件“/dev/binder”映射到进程空间的起始地址。 unsigned mapsize; // 表示上述内存映射空间的大小。 }
宏 BINDER_SERVICE_MANAGER 在文件 frameworks/base/cmds/servicemanager/binder.h 中定义,代码如下:
<span style="font-size:14px;">/* the one magic object */ #define BINDER_SERVICE_MANAGER ((void*) 0) </span>这表示Service Manager的句柄为0。Binder通信机制使用句柄来代表远程接口,此句柄的意义和Windows编程中用到的句柄是差不多的。Service Manager也充当Server角色,它的句柄值为0,这是它的特殊之处。其他的Server的远程接口句柄值都是一个大于0且由Binder驱动程序自动进行分配的值。
(1)打开Binder设备文件。
4、随后main函数首先打开Binder设备文件的操作函数 binder_open(),它放在 frameworks/base/cmds/servicemanager/binder.c 中,代码如下:struct binder_state *binder_open(unsigned mapsize) { struct binder_state *bs; //先创建出对象来,然后再给它结构体中的内容赋值,最后返回这个打开的binder对象 bs = malloc(sizeof(*bs)); if (!bs) { errno = ENOMEM; return 0; } //打开“/dev/binder” bs->fd = open("/dev/binder", O_RDWR); if (bs->fd < 0) { fprintf(stderr,"binder: cannot open device (%s)\n", strerror(errno)); goto fail_open; } //设置占用内存大小,这个大小是通过参数传进来的,在main中写了128*1024,也就是128K的大小 bs->mapsize = mapsize; bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); //然后进行内存映射操作,在这里对bs的第二个参数进行了设置。重点是倒数第二个参数。 if (bs->mapped == MAP_FAILED) { fprintf(stderr,"binder: cannot map device (%s)\n", strerror(errno)); goto fail_map; } /* TODO: check version */ //返回我们打开的binder。 return bs; fail_map: close(bs->fd); fail_open: free(bs); return 0; }
通过文件操作函数open()打开设备文件“/dev/binder”,此设备文件是在Binder驱动程序模块初始化的时候创建的。接下来先看一下这个设备文件的创建过程,来到 kernel/common/drivers/staging/android目录,打开文件 binder.c,可以看到如下模块初始化入口binder_init:
static struct file_operations binder_fops = { .owner = THIS_MODULE, .poll = binder_poll, .unlocked_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 }; static int __init binder_init(void) { int ret; binder_proc_dir_entry_root = proc_mkdir("binder", NULL); if (binder_proc_dir_entry_root) binder_proc_dir_entry_proc = proc_mkdir("proc", binder_proc_dir_entry_root); ret =<strong> misc_register</strong>(&binder_miscdev); if (binder_proc_dir_entry_root) { create_proc_read_entry("state", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_state, NULL); create_proc_read_entry("stats", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_stats, NULL); create_proc_read_entry("transactions", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_transactions, NULL); create_proc_read_entry("transaction_log", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_transaction_log, &binder_transaction_log); create_proc_read_entry("failed_transaction_log", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_transaction_log, &binder_transaction_log_failed); } return ret; } device_initcall(binder_init);创建设备文件(“/dev/binder”)的地方在misc_register函数里面,关于misc设备的注册,我们在Android日志系统驱动程序Logger源代码分析一文中有提到,有兴趣的读取不访去了解一下。其余的逻辑主要是在/proc目录创建各种Binder相关的文件,供用户访问。从设备文件的操作方法binder_fops可以看出,前面的binder_open函数执行语句:
<span style="font-size:14px;"> bs->fd = open("/dev/binder", O_RDWR); </span>就进入到Binder驱动程序的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); #ifdef RT_PRIO_INHERIT proc->default_rt_prio = current->rt_priority; proc->default_policy = current->policy; #endif binder_lock(__func__); binder_stats_created(BINDER_STAT_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_proc 的数据结构,用此数据结构来保存打开设备文件 “/dev/binder” 的进程的上下文信息,并且将这个进程上下文信息保存在打开文件结构 file 的私有数据成员变量 private_data 中。这样在执行其他文件操作时,就通过打开文件结构 file 来取回这个进程上下文信息了。 这个进程上下文信息同时还会保存在一个全局哈希表 binder_procs 中,供驱动程序内部使用。哈希表 binder_procs 定义在文件的开头:
static HLIST_HEAD(binder_procs);
而结构体 struct binder_proc 也被定义在文件 kernel/common/drivers/staging/android/binder.c 中:
struct binder_proc { struct hlist_node proc_node; struct rb_root threads;//保存binder_proc进程内用于处理用户请求的线程,它的最大数量由max_threads来决定 struct rb_root nodes;//保存binder_proc进程内的Binder实体 struct rb_root refs_by_desc;//以句柄作为key值来保存binder_proc进程内的Binder引用,即引用的其它进程的Binder实体 struct rb_root refs_by_node;//以引用的实体节点的地址值为key来保存binder_proc进程内的Binder引用,即引用的其它进程的Binder实体 int pid; struct vm_area_struct *vma; struct task_struct *tsk; struct files_struct *files; struct hlist_node deferred_work_node; int deferred_work; void *buffer;//映射的物理内存在内核空间中的起始位置 ptrdiff_t user_buffer_offset;//内核使用的虚拟地址与进程使用的虚拟地址之间的差值,即如果某个物理页面在内核空间中对应的虚拟地址是addr的话,那么这个物理页面在进程控件对应的虚拟地址就为 //“addr+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;//默认优先级 struct dentry *debugfs_entry; };
它里面基本上都是结构体变量,其中有四个比较重要的成员变量,它们都是表示红黑树的节点,也就是说,binder_proc 分别挂着4个红黑树下:
(1)threads树:struct rb_root threads;
用来保存 binder_proc 进程内用于处理用户请求的线程,它的最大数量由 max_threads 来决定。
(2)node树:struct rb_root nodes;
用来保存 binder_proc 进程内的Binder实体。
(3)(4)refs_by_desc树和refs_by_node树:struct rb_root refs_by_desc,refs_by_node;
用来保存 binder_proc 进程内的Binder引用,即引用其他进程的Binder实体,他分别用两种方式来组织红黑树:一种是以句柄作为key值来组织;另一种是以引用的实体节点的地址值作为key值来组织。它们都是表示同一样东西,只不过是为了内部查找方便而用两个红黑树表示。
(2)建立128K内存映射
这样打开设备文件 /dev/binder 的操作就完成了,接下来需要对打开的设备文件进行内存映射操作 mmap:static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; struct vm_struct *area; //内核用的 // 首先通过filp->private_data得到在打开设备文件“/dev/binder”时创建的结构binder_proc。 struct binder_proc *proc = filp->private_data; const char *failure_string; struct binder_buffer *buffer; // 内存映射信息存放在vma参数中。 // 需要注意的是,这里的vma的数据类型是结构 vm_area_struct,他表示的是一块连续的虚拟地址空间区域(进程使用)。 // 在函数变量声明的地方,我们还看到有一个类似的结构体 vm_struct,这个数据结构也是表示一块连续的虚拟地址空间区域(内核使用)。 // 对参数进行检查,要映射的内存大小不能超过SIZE_4M,即4M。 // 在service_manager.c中的main函数中,bs = binder_open(128*1024); //128K,所以大小符合标准。 if ((vma->vm_end - vma->vm_start) > SZ_4M) vma->vm_end = vma->vm_start + SZ_4M; if (binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE) printk(KERN_INFO "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; if (proc->buffer) { ret = -EBUSY; failure_string = "already mapped"; goto err_already_mapped; }
/* * 这儿才开始做事。首先vma已经传过来了,属于进程的一段空间,用于与内核空间映射用的。 * get_vm_area的目的是在内核的vmalloc区域获得一个相同大小的连续空间,获得一个空闲的vm_struct区间, * 表示为vm_area,同时将该结构加入到vm_list统一管理 * */ 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结构体的这些成员变量: proc->buffer = area->addr;//kernel continus addr proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; VONNYFLY_printk("vma->vm_start=0x%lx,proc->buffer=0x%lx,proc->user_buffer_offset=%l",vma->vm_start,(uintptr_t)proc->buffer,proc->user_buffer_offset); #ifdef CONFIG_CPU_CACHE_VIPT if (cache_is_vipt_aliasing()) { while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) { printk(KERN_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;
/* * 这儿又开始做事了。 * 此时proc->buffer指向内核的vmalloc 区域。 * 我们有了vma(vm_area_struct) area(vm_struct),接下来很显然要: * 1、分配物理页 * 2、分别对vma用户空间建立页表、对vmalloc区域建立页表映射关系。待会儿详细分析源码…… * */ //调用binder_update_page_range为虚拟地址空间proc->buffer ~ proc->buffer + PAGE_SIZE分配一个空闲的物理页面, 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来描述,分别插入到proc_buffers链表和proc->free_buffers红黑树中去。 buffer = proc->buffer; INIT_LIST_HEAD(&proc->buffers); list_add(&buffer->entry, &proc->buffers); buffer->free = 1; binder_insert_free_buffer(proc, buffer); //初始化proc结构体中的这三个成员变量: proc->free_async_space = proc->buffer_size / 2; barrier(); proc->files = get_files_struct(current); proc->vma = vma; /*printk(KERN_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: vfree(proc->buffer); proc->buffer = NULL; err_get_vm_area_failed: err_already_mapped: err_bad_arg: printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n", proc->pid, vma->vm_start, vma->vm_end, failure_string, ret); return ret; }
注意点一(第一段代码中):在Linux系统中,结构体vm_area_struct表示的虚拟地址是给进程使用的,而结构体vm_struct表示的虚拟地址是给内核使用的,它们对应的物理页面都可以是不连续的。
结构体vm_area_struct表示的徐地址空间范围是0~3G,而结构体vm_struct表示的地址空间范围是(3G+896M+8M)~4G。
为什么结构体vm_struct表示的地址空间范围不是3~4G呢?因为3G~(3G+896M)范围的地址是用来映射连续的物理页面的,这个范围的虚拟地址和对应的实际的物理地址有着简单的对应关系,即对应0~896M的物理地址空间。
而(3G+896M)~(3G+896M+8M)是安全保护区域。例如所有指向这8M地址空间的指针都是非法的。
那此处为什么会同时使用进程虚拟地址控件和内核虚拟地址空间来映射同一个物理页面呢?这就是BInder进程间通信机制的精髓所在了。在同一个物理页面,一方映射到进程虚拟地址空间,一方映射到内核虚拟地址空间,这样进程和内核之间就可以减少一次内存拷贝工作,提高了进程之间的通信效率。举个例子如,Client要将一块内存数据传递给Server,一般的做法是,Client将这块数据从它的进程空间拷贝到内核空间中,然后内核再将这个数据从内核空间拷贝到Server的进程空间,这样,Server就可以访问这个数据了。但是在这种方法中,执行了两次内存拷贝操作,而采用我们上面提到的方法,只需要把Client进程空间的数据拷贝一次到内核空间,然后Server与内核共享这个数据就可以了,整个过程只需要执行一次内存拷贝,提高了效率。
binder_mmap的原理讲完了,这个函数的逻辑就好理解了。不过,这里还是先要解释一下struct binder_proc结构体的几个成员变量:
buffer成员变量是一个void*指针,它表示要映射的物理内存在内核空间中的起始位置;
buffer_size成员变量是一个size_t类型的变量,表示要映射的内存的大小;
pages成员变量是一个struct page*类型的数组,struct
page是用来描述物理页面的数据结构;
user_buffer_offset成员变量是一个ptrdiff_t类型的变量,它表示的是内核使用的虚拟地址与进程使用的虚拟地址之间的差值,即如果某个物理页面在内核空间中对应的虚拟地址是addr的话,那么这个物理页面在进程空间对应的虚拟地址就为addr + user_buffer_offset。
接下来还需要看一下Binder驱动程序管理内存映射地址空间的方法,即如何管理buffer~(buffer+buffer_size)这段地址空间的,这个地址空间被划分为一段一段来管理,每一段是用结构体binder_buffer来描述的:
<span style="font-weight: normal;"><span style="font-size:14px;">//管理binder_proc中buffer ~ (buffer + buffer_size)这段地址空间的,这个地址空间被划分为一段一段来管理,每一段是结构体struct binder_buffer来描述
struct binder_buffer {
struct list_head entry; //按从低址到高地址链入到struct binder_proc中的buffers表示的链表中去
struct rb_node rb_node; //空闲的链入到struct binder_proc中的free_buffers表示的红黑树中去
//正在使用的链入到struct binder_proc中的allocated_buffers表示的红黑树中
unsigned free:1;//空闲标志
unsigned allow_user_free:1;
unsigned async_transaction:1;
unsigned debug_id:29;
struct binder_transaction *transaction;
struct binder_node *target_node;//目标节点
size_t data_size;//表示数据的大小
size_t offsets_size;//偏移量
uint8_t data[0];//用于存储实际数据
};</span></span>
每一个binder_buffer通过其成员entry按从低址到高地址连入到struct binder_proc中的buffers表示的链表中去,同时,每一个binder_buffer又分为正在使用的和空闲的,通过free成员变量来区分,空闲的binder_buffer通过成员变量rb_node连入到struct
binder_proc中的free_buffers表示的红黑树中去,正在使用的binder_buffer通过成员变量rb_node连入到struct binder_proc中的allocated_buffers表示的红黑树中去。这样做当然是为了方便查询和维护这块地址空间了,这一点我们可以从其它的代码中看到,等遇到的时候我们再分析。
终于可以回到binder_mmap这个函数来了,首先是对参数作一些健康体检(sanity check),例如,要映射的内存大小不能超过SIZE_4M,即4M,回到service_manager.c中的main 函数,这里传进来的值是128 * 1024个字节,即128K,这个检查没有问题。通过健康体检后,调用get_vm_area函数获得一个空闲的vm_struct区间,并初始化proc结构体的buffer、user_buffer_offset、pages和buffer_size和成员变量,接着调用binder_update_page_range来为虚拟地址空间proc->buffer
~ proc->buffer + PAGE_SIZE分配一个空闲的物理页面,同时这段地址空间使用一个binder_buffer来描述,分别插入到proc->buffers链表和proc->free_buffers红黑树中去,最后,还初始化了proc结构体的free_async_space、files和vma三个成员变量。
这里,我们继续进入到binder_update_page_range函数中去看一下Binder驱动程序是如何实现把一个物理页面同时映射到内核空间和进程空间去的:
/* * 这个函数不但可以分配物理页面,而且可以用来释放物理页面,这可以通过参数allocate来区别。 * 要分配物理页面的虚拟地址空间范围为(start~end)。 * 前面都是一些逻辑检查,我们主要关注这个函数中间的for循环。 * */ 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; if (binder_debug_mask & BINDER_DEBUG_BUFFER_ALLOC) printk(KERN_INFO "binder: %d: %s pages %p-%p\n", proc->pid, allocate ? "allocate" : "free", start, end); if (end <= start) return 0; if (vma) mm = NULL; else mm = get_task_mm(proc->tsk); if (mm) { down_write(&mm->mmap_sem); vma = proc->vma; } if (allocate == 0) goto free_range; if (vma == NULL) { printk(KERN_ERR "binder: %d: binder_alloc_buf failed to " "map pages in userspace, no vma\n", proc->pid); goto err_no_vma; } /* * 分配页面了,但是为什么在GFP_KERNEL区分配呢?没高明白,既然都要更新页表,何不到highmemory分配呢。也许认为lowmem足够大了吧,毕竟896M,没有多少arm设备有这么大的物理ram。 */ for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { int ret; struct page **page_array_ptr; page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE]; BUG_ON(*page); // 先调用alloc_page分配一个物理页面,此函数返回一个结构体page物理页面描述符。 *page = alloc_page(GFP_KERNEL | __GFP_ZERO); if (*page == NULL) { printk(KERN_ERR "binder: %d: binder_alloc_buf failed " "for page at %p\n", proc->pid, page_addr); goto err_alloc_page_failed; } // 根据这个page物理页面描述符的内容初始化好结构体tmp_area: tmp_area.addr = page_addr; tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */; page_array_ptr = page; /*为vmalloc 区域的连续地址空间进行页表映射, * 当然需要vm_struct (提供虚拟地址)参数和 page参数(用来make pte的),这就完成了内核区的映射*/ // 然后通过map_vm_area将这个物理页面插入到tmp_area描述的内核空间去: ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr); if (ret) { printk(KERN_ERR "binder: %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; /*更新vma对应的页表,这样就是实现了mmap功能*/ // 并通过vm_insert_page将这个物理页面插入到进程虚拟地址空间去,参数vma表示要插入的进程的地址空间。 ret = vm_insert_page(vma, user_page_addr, page[0]); if (ret) { printk(KERN_ERR "binder: %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; }
首先是调用alloc_page来分配一个物理页面,这个函数返回一个struct page物理页面描述符,根据这个描述的内容初始化好struct vm_struct tmp_area结构体,然后通过map_vm_area将这个物理页面插入到tmp_area描述的内核空间去,接着通过page_addr + proc->user_buffer_offset获得进程虚拟空间地址,并通过vm_insert_page函数将这个物理页面插入到进程地址空间去,参数vma代表了要插入的进程的地址空间。
这样,frameworks/base/cmds/servicemanager/binder.c文件中的binder_open函数就描述完了,回到frameworks/base/cmds/servicemanager/service_manager.c文件中的main函数,下一步就是调用binder_become_context_manager来通知Binder驱动程序自己是Binder机制的上下文管理者,即守护进程。binder_become_context_manager函数位于frameworks/base/cmds/servicemanager/binder.c文件中:
(3)告诉Binder驱动程序自己是Binder上下文管理者,即我们前面所说的守护进程。
函数binder_become_context_manager()位于文件 frameworks/base/cmds/servicemanager/binder.c 中,具体代码如下:int binder_become_context_manager(struct binder_state *bs){ return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); }
就是通过调用 ioctl 文件操作函数通知Binder驱动程序自己是守护进程,命令号是后面的那个宏定义:
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
binder_ioctl()函数中涉及到了两个数据结构:
(1)结构体binder_thread:表示一个线程,这里就是执行binder_become_context_manager函数的线程:
struct binder_thread { struct binder_proc *proc;//所属进程 struct rb_node rb_node;//链入binder_proc成员threads这棵红黑树的节点 int pid; int looper;//线程的状态 struct binder_transaction *transaction_stack;//要接收和发送的进程和线程信息,表示线程正在处理的事务 struct list_head todo;//表示该线程的数据列表 uint32_t return_error; //操作结果返回码 uint32_t return_error2; //操作结果返回码 wait_queue_head_t wait; //用来阻塞线程等待某个事件的发生 struct binder_stats stats; //用来保存一些统计信息 };proc表示这个线程所属的进程。
struct binder_proc有一个成员变量threads,它的类型是rb_root,它表示一查红黑树,把属于这个进程的所有线程都组织起来,struct
binder_thread的成员变量rb_node就是用来链入这棵红黑树的节点了。
looper成员变量表示线程的状态,它可以取下面这几个值:
enum { BINDER_LOOPER_STATE_REGISTERED = 0x01, BINDER_LOOPER_STATE_ENTERED = 0x02, BINDER_LOOPER_STATE_EXITED = 0x04, BINDER_LOOPER_STATE_INVALID = 0x08, BINDER_LOOPER_STATE_WAITING = 0x10, BINDER_LOOPER_STATE_NEED_RETURN = 0x20 }transaction_stack表示线程正在处理的事务,
todo表示发往该线程的数据列表,
return_error和return_error2表示操作结果返回码,
wait用来阻塞线程等待某个事件的发生,
stats用来保存一些统计信息。这些成员变量遇到的时候再分析它们的作用。
(2)数据结构binder_node:表示一个binder实体:
struct binder_node { int debug_id; struct binder_work work; union { struct rb_node rb_node;//如果这个Binder实体还在正常使用,则使用rb_node来链入proc->nodes所表示的红黑树的节点,这棵红黑树用来组织属于这个进程的所有Binder实体。 struct hlist_node dead_node;//如果这个Binder实体所属的进程已经销毁,而这个Binder实体又被其它进程所引用,则这个Binder实体通过dead_node进入到一个哈希表中去存放。 }; struct binder_proc *proc;//所属进程 struct hlist_head refs;//引用了该Binder实体的Binder引用连接起来构成一个链表 int internal_strong_refs;//引用计数 int local_weak_refs;//引用计数 int local_strong_refs;//引用计数 void __user *ptr;//在用户空间的地址 void __user *cookie;//附加数据 unsigned has_strong_ref:1; unsigned pending_strong_ref:1; unsigned has_weak_ref:1; unsigned pending_weak_ref:1; unsigned has_async_transaction:1; unsigned accept_fds:1; unsigned min_priority:8; struct list_head async_todo; };
rb_node和dead_node组成一个联合体。 如果这个Binder实体还在正常使用,则使用rb_node来连入proc->nodes所表示的红黑树的节点,这棵红黑树用来组织属于这个进程的所有Binder实体;如果这个Binder实体所属的进程已经销毁,而这个Binder实体又被其它进程所引用,则这个Binder实体通过dead_node进入到一个哈希表中去存放。
proc成员变量就是表示这个Binder实例所属于进程了。
refs成员变量把所有引用了该Binder实体的Binder引用连接起来构成一个链表。
internal_strong_refs、local_weak_refs和local_strong_refs表示这个Binder实体的引用计数。
ptr和cookie成员变量分别表示这个Binder实体在用户空间的地址以及附加数据。其余的成员变量就不描述了,遇到的时候再分析。
这样就进入到Binder驱动程序的函数binder_ioctl():我们只关注BINDER_SET_CONTEXT_MGR命令:
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret; //首先是通过filp->private_data获得proc变量,这里binder_mmap函数是一样的。 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); //接着通过binder_get_thread函数获得线程信息: thread = binder_get_thread(proc); if (thread == NULL) { ret = -ENOMEM; goto err; } switch (cmd) { ...... case BINDER_SET_CONTEXT_MGR: //全局变量:binder_context_mgr_node用来表示Service Manager实体 if (binder_context_mgr_node != NULL) { printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n"); ret = -EBUSY; goto err; } //全局变量:binder_context_mgr_uid表示Service Manager守护进程的uid if (binder_context_mgr_uid != -1) { if (binder_context_mgr_uid != current->cred->euid) { printk(KERN_ERR "binder: BINDER_SET_" "CONTEXT_MGR bad uid %d != %d\n", current->cred->euid, binder_context_mgr_uid); ret = -EPERM; goto err; } }else //由于当前线程是第一次进到这里,所以binder_context_mgr_node为NULL,binder_context_mgr_uid为-1, //于是初始化binder_context_mgr_uid为current->cred->euid, //这样,当前线程就成为Binder机制的守护进程了, binder_context_mgr_uid = current->cred->euid; //并且通过binder_new_node为Service Manager创建Binder实体: //binder_new_node返回到binder_ioctl函数后,就把新建的binder_node指针保存在binder_context_mgr_node中了, binder_context_mgr_node = binder_new_node(proc, NULL, NULL); if (binder_context_mgr_node == NULL) { ret = -ENOMEM; goto err; } //紧接着,又初始化了binder_context_mgr_node的引用计数值。 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; ...... default: ret = -EINVAL; goto err; } ret = 0; err: //binder_ioctl函数返回之前,执行了下面语句: //回忆上面执行binder_get_thread时,thread->looper = BINDER_LOOPER_STATE_NEED_RETURN, //执行了这条语句后,thread->looper = 0。 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; }
在binder_ioctl函数中,首先是通过filp->private_data获得proc变量,这里binder_mmap函数是一样的。接着通过binder_get_thread函数获得线程信息,我们来看一下这个函数:
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; //这里把当前线程current的pid作为键值, //在进程proc->threads表示的红黑树中进行查找,看是否已经为当前线程创建过了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; } //在这个场景下,由于当前线程是第一次进到这里,所以肯定找不到,即*p == NULL成立, //于是,就为当前线程创建一个线程上下文信息结构体binder_thread,并初始化相应成员变量, //并插入到proc->threads所表示的红黑树中去,下次要使用时就可以从proc中找到了。 //注意,这里的thread->looper = BINDER_LOOPER_STATE_NEED_RETURN。 if (*p == NULL) { thread = kzalloc(sizeof(*thread), GFP_KERNEL); if (thread == NULL) return NULL; binder_stats.obj_created[BINDER_STAT_THREAD]++; thread->proc = proc; 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; }
在binder_get_thread()中,把当前线程current的pid作为键值,在进程proc->threads表示的红黑树中进行查找,看是否已经为当前线程创建过了 binder_thread 信息。在这个场景下,由于当前线程是第一次进到这里,所以肯定找不到,即*p == NULL成立,于是就为当前线程创建一个线程上下文信息结构体binder_thread,并初始化相应成员变量,并插入到proc->threads所表示的红黑树中去,下次要使用时就可以从proc中找到了。注意,这里的thread->looper=BINDER_LOOPER_STATE_NEED_RRETURN。
再回到binder_ioctl函数中,接下来会有两个全局变量 binder_context_mgr_node 和 binder_context_mgr_uid,定义如下:
static struct binder_node *binder_context_mgr_node; static uid_t binder_context_mgr_uid = -1;
binder_context_mgr_node用来表示Service Manager实体,binder_context_mgr_uid表示Service Manager守护进程的uid。在这个场景下,由于当前线程是第一次进到这里,所以binder_context_mgr_node为NULL,binder_context_mgr_uid为-1,于是初始化binder_context_mgr_uid为current->cred->euid,这样,当前线程就成为Binder机制的守护进程了,并且通过binder_new_node为Service
Manager创建Binder实体:
//注意,这里传进来的ptr和cookie均为NULL。 static struct binder_node *binder_new_node(struct binder_proc *proc, void __user *ptr, void __user *cookie) { struct rb_node **p = &proc->nodes.rb_node; struct rb_node *parent = NULL; struct binder_node *node; //函数首先检查proc->nodes红黑树中是否已经存在以ptr为键值的node,如果已经存在,就返回NULL。 //在这个场景下,由于当前线程是第一次进入到这里,所以肯定不存在, //于是就新建了一个ptr为NULL的binder_node,并且初始化其它成员变量,并插入到proc->nodes红黑树中去。 while (*p) { parent = *p; node = rb_entry(parent, struct binder_node, rb_node); if (ptr < node->ptr) p = &(*p)->rb_left; else if (ptr > node->ptr) p = &(*p)->rb_right; else return NULL; } node = kzalloc(sizeof(*node), GFP_KERNEL); if (node == NULL) return NULL; binder_stats.obj_created[BINDER_STAT_NODE]++; rb_link_node(&node->rb_node, parent, p); rb_insert_color(&node->rb_node, &proc->nodes); node->debug_id = ++binder_last_id; node->proc = proc; node->ptr = ptr; node->cookie = cookie; node->work.type = BINDER_WORK_NODE; INIT_LIST_HEAD(&node->work.entry); INIT_LIST_HEAD(&node->async_todo); if (binder_debug_mask & BINDER_DEBUG_INTERNAL_REFS) printk(KERN_INFO "binder: %d:%d node %d u%p c%p created\n", proc->pid, current->pid, node->debug_id, node->ptr, node->cookie); return node; }注意,这里传进来的ptr和cookie均为NULL。函数首先检查proc->nodes红黑树中是否已经存在以ptr为键值的node,如果已经存在,就返回NULL。在这个场景下,由于当前线程是第一次进入到这里,所以肯定不存在,于是就新建了一个ptr为NULL的binder_node,并且初始化其它成员变量,并插入到proc->nodes红黑树中去。
binder_new_node函数中传来的ptr和cookie都为NULL。函数中首先检查proc->nodes红黑树中是否已经存在以ptr为键值的node,如果已经存在则返回NULL。在这种情况下,由于当前线程是第一次进入到这里,所以肯定不存在,于是就新建了一个ptr为NULL的binder_node,并且初始化其他成员变量,并插入到proc->nodes红黑树中去。
当binder_new_node返回到函数binder_ioctl()后,会把新建的binder_node指针保存在binder_content_mgr_node中,然后又初始化binder_context_mgr_node的引用计数值。这样的执行BINDER_SET)CONTEXT_MGR命令完毕,在函数binger_ioctl()返回之前执行下面的语句:
if(thread){ thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN; }在执行 binder_get_thread时,thread->looper = BINDER_LOOPER_STATE_NEED_RETURN,执行了这条语句后,thread->looper = 0;
(4)进入一个无穷循环,充当Service的角色,等待Client的请求。
回到frameworks/base/cmds/servicemanager/service_manager.c文件中的main函数,下一步就是调用binder_loop函数进入循环,等待Client来请求了。binder_loop函数定义在frameworks/base/cmds/servicemanager/binder.c文件中:<pre name="code" class="java">//函数前面的代码就不解释了, //同前面调用binder_become_context_manager是一样的,只不过这里调用binder_get_thread函数获取binder_thread, //就能从proc中直接找到了,不需要创建一个新的。 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: case BINDER_WRITE_READ: { struct binder_write_read bwr; if (size != sizeof(struct binder_write_read)) { ret = -EINVAL; goto err; } // 首先是通过copy_from_user(&bwr, ubuf, sizeof(bwr))语句把用户传递进来的参数转换成struct binder_write_read结构体, //并保存在本地变量bwr中,这里可以看出bwr.write_size等于4, //于是进入binder_thread_write函数,这里我们只关注BC_ENTER_LOOPER相关的代码: 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; } } //回到binder_ioctl函数,由于bwr.read_size == 0, //binder_thread_read函数就不会被执行了,这样,binder_ioctl的任务就完成了。 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; } ...... 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; }
首先通过binder_write执行BC_ENTER_LOOPER命令以告诉Binder驱动程序,Service Manager马上要进入循环。
int binder_write(struct binder_state *bs, void *data, unsigned len) { //这个binder_write其实是设置了binder_write_read结构体里面的write部分, //而read部分为空(read_size为0,read_buffer为NULL), struct binder_write_read bwr; int res; //// bwr.write_size = len; bwr.write_consumed = 0; bwr.write_buffer = (unsigned) data; //// bwr.read_size = 0; bwr.read_consumed = 0; bwr.read_buffer = 0; //然后通过ioctl发送BINDER_WRITE_READ命令。 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); if (res < 0) { fprintf(stderr,"binder_write: ioctl failed (%s)\n", strerror(errno)); } return res; }
由此可见,这个binder_write其实是设置了binder_write_read结构体里面的write部分,而read部分为空(read_size为0,read_buffer为NULL),然后通过ioctl发送BINDER_WRITE_READ命令,然后又是调用ioctl函数进入到Binder驱动程序的binder_ioctl函数,这里我们也只是关注BC_ENTER_LOOPER相关的逻辑:
在此还需要理解设备文件“/dev/binder”操作函数 ioctl 的操作码BINDER_WRITE_READ,首先看其定义:
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
此io操纵码有一个形式为struct binder_write_read的参数:
//用户空间通过ioctl读写bind的数据结构 struct binder_write_read { signed long write_size; //写数据大小 signed long write_consumed; //被消耗的写数据 unsigned long write_buffer; //地址为一个struct binder_transaction_data signed long read_size; //读数据大小 signed long read_consumed; //被消耗的读数据 unsigned long read_buffer; //地址为一个struct binder_transaction_data };
这里顺便说一下,用户空间程序和Binder驱动程序交互大多数都是通过BINDER_WRITE_READ命令的,write_bufffer和read_buffer所指向的数据结构还指定了具体要执行的操作,write_bufffer和read_buffer所指向的结构体是struct binder_transaction_data:
struct binder_transaction_data { /* The first two are only used for bcTRANSACTION and brTRANSACTION, * identifying the target and contents of the transaction. */ union { size_t handle; //处理此事件的目标对象的句柄 void *ptr; //目标对象是本地Binder实体时,就使用ptr来表示这个对象在本进程中的地址 } target; void *cookie; //附加数据 unsigned int code; //对目标对象请求的命令代码 /* General information about the transaction. */ unsigned int flags; pid_t sender_pid;//发送者进程的pid uid_t sender_euid;//发送者进程有效用户ID size_t data_size; //data.buffer缓冲区的大小 size_t offsets_size;//data.offsets缓冲区的大小,指定在data.buffer缓冲区中,所有Binder实体或者引用的偏移位置 /* If this transaction is inline, the data immediately * follows here; otherwise, it ends with a pointer to * the data buffer. */ union { struct { /* transaction data */ const void *buffer;//真正要传输的数据,分为两类数据: // 1、普通数据 //2、Binder实体或者Binder引用 /* offsets from buffer to flat_binder_object structs */ const void *offsets; } ptr; uint8_t buf[8]; } data; };
有一个联合体target,当这个BINDER_WRITE_READ命令的目标对象是本地Binder实体时,就使用ptr来表示这个对象在本进程中的地址,否则就使用handle来表示这个Binder实体的引用。只有目标对象是Binder实体时,cookie成员变量才有意义,表示一些附加数据,由Binder实体来解释这个个附加数据。code表示要对目标对象请求的命令代码,有很多请求代码,这里就不列举了,在这个场景中,就是BC_ENTER_LOOPER了,用来告诉Binder驱动程序,
Service Manager要进入循环了。其余的请求命令代码可以参考kernel/common/drivers/staging/android/binder.h文件中定义的两个枚举类型BinderDriverReturnProtocol和BinderDriverCommandProtocol。
flags成员变量表示事务标志:每一个标志位所表示的意义看注释就行了,遇到时再具体分析。
enum transaction_flags { TF_ONE_WAY = 0x01, /* this is a one-way call: async, no return */ TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */ TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */ TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */ };
sender_pid和sender_euid表示发送者进程的pid和euid。
data_size表示data.buffer缓冲区的大小,
offsets_size表示data.offsets缓冲区的大小。这里需要解释一下data成员变量,命令的真正要传输的数据就保存在data.buffer缓冲区中,前面的一成员变量都是一些用来描述数据的特征的。data.buffer所表示的缓冲区数据分为两类,一类是普通数据,Binder驱动程序不关心,一类是Binder实体或者Binder引用,这需要Binder驱动程序介入处理。为什么呢?想想,如果一个进程A传递了一个Binder实体或Binder引用给进程B,那么,Binder驱动程序就需要介入维护这个Binder实体或者引用的引用计数,防止B进程还在使用这个Binder实体时,A却销毁这个实体,这样的话,B进程就会crash了。所以在传输数据时,如果数据中含有Binder实体和Binder引用,就需要告诉Binder驱动程序它们的具体位置,以便Binder驱动程序能够去维护它们。data.offsets的作用就在这里了,它指定在data.buffer缓冲区中,所有Binder实体或者引用的偏移位置。每一个Binder实体或者引用,通过struct
flat_binder_object 来表示:
/*
* This is the flattened representation of a Binder object for transfer
* between processes. The 'offsets' supplied as part of a binder transaction
* contains offsets into the data where these structures occur. The Binder
* driver takes care of re-writing the structure type and data as it moves
* between processes.
*/
struct flat_binder_object {
/* 8 bytes for large_flat_header. */
unsigned long type;
unsigned long flags;
/* 8 bytes of data. */
union {
void *binder; /* local object */
signed long handle; /* remote object */
};
/* extra data associated with local object */
void *cookie;
};
type表示Binder对象的类型,它取值如下所示:
enum {
BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
};
flags表示Binder对象的标志,该域只对第一次传递Binder实体时有效,因为此刻驱动需要在内核中创建相应的实体节点,有些参数需要从该域取出。
type和flags的具体意义可以参考Android Binder设计与实现一文。
最后,binder表示这是一个Binder实体,handle表示这是一个Binder引用,当这是一个Binder实体时,cookie才有意义,表示附加数据,由进程自己解释。
数据结构分析完了,回到binder_loop函数中,首先是执行BC_ENTER_LOOPER命令:
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(unsigned));
进入到binder_write函数中:
int binder_write(struct binder_state *bs, void *data, unsigned len)
{
struct binder_write_read bwr;
int res;
bwr.write_size = len;
bwr.write_consumed = 0;
bwr.write_buffer = (unsigned) data;
bwr.read_size = 0;
bwr.read_consumed = 0;
bwr.read_buffer = 0;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
fprintf(stderr,"binder_write: ioctl failed (%s)\n",
strerror(errno));
}
return res;
}
函数前面的代码就不解释了,同前面调用binder_become_context_manager是一样的,只不过这里调用binder_get_thread函数获取binder_thread,就能从proc中直接找到了,不需要创建一个新的。
首先是通过copy_from_user(&bwr, ubuf, sizeof(bwr))语句把用户传递进来的参数转换成struct binder_write_read结构体,并保存在本地变量bwr中,这里可以看出bwr.write_size等于4,于是进入binder_thread_write函数,这里我们只关注BC_ENTER_LOOPER相关的代码:
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_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);
}
//回忆前面执行binder_become_context_manager到binder_ioctl时,
//调用binder_get_thread函数创建的thread->looper值为0,所以这里执行完BC_ENTER_LOOPER时,
//thread->looper值就变为BINDER_LOOPER_STATE_ENTERED了,表明当前线程进入循环状态了。
thread->looper |= BINDER_LOOPER_STATE_ENTERED;
break;
......
default:
printk(KERN_ERR "binder: %d:%d unknown command %d\n", proc->pid, thread->pid, cmd);
return -EINVAL;
}
*consumed = ptr - buffer;
}
return 0;
}
回忆前面执行binder_become_context_manager到binder_ioctl时,调用binder_get_thread函数创建的thread->looper值为0,所以这里执行完BC_ENTER_LOOPER时,thread->looper值就变为BINDER_LOOPER_STATE_ENTERED了,表明当前线程进入循环状态了。
回到binder_ioctl函数,由于bwr.read_size == 0,binder_thread_read函数就不会被执行了,这样,binder_ioctl的任务就完成了。
回到binder_loop函数,进入for循环:
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;
}
}
又是执行一个ioctl命令,注意,这里的bwr参数各个成员的值:
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (unsigned) readbuf;
再次进入到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;
}
......
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;
}
这次,bwr.write_size等于0,于是不会执行binder_thread_write函数,bwr.read_size等于32,于是进入到binder_thread_read函数:
static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
void __user *buffer, int size, signed long *consumed, int non_block)
{
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
int ret = 0;
int wait_for_proc_work;
// 传入的参数*consumed == 0,
//于是写入一个值BR_NOOP到参数ptr指向的缓冲区中去,即用户传进来的bwr.read_buffer缓冲区。
if (*consumed == 0) {
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
}
retry:
//这时候,thread->transaction_stack == NULL,并且thread->todo列表也是空的,
//这表示当前线程没有事务需要处理,于是wait_for_proc_work为true,
//表示要去查看proc是否有未处理的事务。
wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
//当前thread->return_error == BR_OK,这是前面创建binder_thread时初始化设置的。
if (thread->return_error != BR_OK && ptr < end) {
if (thread->return_error2 != BR_OK) {
if (put_user(thread->return_error2, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
if (ptr == end)
goto done;
thread->return_error2 = BR_OK;
}
if (put_user(thread->return_error, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
thread->return_error = BR_OK;
goto done;
}
//设置thread的状态为BINDER_LOOPER_STATE_WAITING,表示线程处于等待状态。
thread->looper |= BINDER_LOOPER_STATE_WAITING;
if (wait_for_proc_work)
proc->ready_threads++;
mutex_unlock(&binder_lock);
if (wait_for_proc_work) {
if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))) {
binder_user_error("binder: %d:%d ERROR: Thread waiting "
"for process work before calling BC_REGISTER_"
"LOOPER or BC_ENTER_LOOPER (state %x)\n",
proc->pid, thread->pid, thread->looper);
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
}
//调用binder_set_nice函数设置当前线程的优先级别为proc->default_priority,
//这是因为thread要去处理属于proc的事务,因此要将此thread的优先级别设置和proc一样。
binder_set_nice(proc->default_priority);
if (non_block) {
//这个场景中,proc也没有事务处理,即binder_has_proc_work(proc, thread)为false。
//如果文件打开模式为非阻塞模式,即non_block为true,那么函数就直接返回-EAGAIN,要求用户重新执行ioctl;
//否则的话,就通过当前线程就通过wait_event_interruptible_exclusive函数进入休眠状态,等待请求到来再唤醒了。
if (!binder_has_proc_work(proc, thread))
ret = -EAGAIN;
} else
ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));
} else {
if (non_block) {
if (!binder_has_thread_work(thread))
ret = -EAGAIN;
} else
ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));
}
.......
}
传入的参数*consumed == 0,于是写入一个值BR_NOOP到参数ptr指向的缓冲区中去,即用户传进来的bwr.read_buffer缓冲区。这时候,thread->transaction_stack == NULL,并且thread->todo列表也是空的,这表示当前线程没有事务需要处理,于是wait_for_proc_work为true,表示要去查看proc是否有未处理的事务。当前thread->return_error
== BR_OK,这是前面创建binder_thread时初始化设置的。于是继续往下执行,设置thread的状态为BINDER_LOOPER_STATE_WAITING,表示线程处于等待状态。调用binder_set_nice函数设置当前线程的优先级别为proc->default_priority,这是因为thread要去处理属于proc的事务,因此要将此thread的优先级别设置和proc一样。在这个场景中,proc也没有事务处理,即binder_has_proc_work(proc, thread)为false。如果文件打开模式为非阻塞模式,即non_block为true,那么函数就直接返回-EAGAIN,要求用户重新执行ioctl;否则的话,就通过当前线程就通过wait_event_interruptible_exclusive函数进入休眠状态,等待请求到来再唤醒了。
总结:
(1)打开/dev/binder文件:open("/dev/binder", O_RDWR);
(2)建立128K内存映射:
mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
(3)通知Binder驱动程序它是守护进程:
binder_become_context_manager(bs);
(4)进入循环等待请求的到来:
binder_loop(bs, svcmgr_handler);
在这个过程中,在Binder驱动程序中建立了一个struct binder_proc结构、一个struct binder_thread结构和一个struct binder_node结构,这样,Service Manager就在Android系统的进程间通信机制Binder担负起守护进程的职责了。
三、分析Server和Client获得Service Manager的过程
原文博客:浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路我们知道,Service Manager在Binder机制中既充当守护进程的角色,同时它也充当着Server角色,然而它又与一般的Server不一样。对于普通的Server来说,Client如果想要获得Server的远程接口,那么必须通过Service Manager远程接口提供的getService接口来获得,这本身就是一个使用Binder机制来进行进程间通信的过程。而对于Service
Manager这个Server来说,Client如果想要获得Service Manager远程接口,却不必通过进程间通信机制来获得,因为Service Manager远程接口是一个特殊的Binder引用,它的引用句柄一定是0。
获取Service Manager远程接口的函数是 defaultServiceManager(),此函数声明在文件 frameworks/base/include/binder/IServiceManager.h中,代码如下:
sp<IServiceManager> defaultServiceManager();
函数defaultServiceManager在 frameworks/base/include/binder/IServiceManager.cpp文件中实现:
sp<IServiceManager> defaultServiceManager() { if (gDefaultServiceManager != NULL) return gDefaultServiceManager; { AutoMutex _l(gDefaultServiceManagerLock); if (gDefaultServiceManager == NULL) { gDefaultServiceManager = interface_cast<IServiceManager>( ProcessState::self()->getContextObject(NULL)); } } return gDefaultServiceManager; }
其中gDefaultServiceManagerLock和gDefaultServiceManager是全局变量,定义在frameworks/base/libs/binder/Static.cpp文件中:
Mutex gDefaultServiceManagerLock; sp<IServiceManager> gDefaultServiceManager;从这个函数可以看出,gDefaultServiceManager是单例模式,调用defaultServiceManager函数时,如果gDefaultServiceManager已经创建,则直接返回,否则通过interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL))来创建一个,并保存在gDefaultServiceManager全局变量中。
在继续介绍interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL))的实现之前,先来看一个类图,这能够帮助我们了解Service Manager远程接口的创建过程:
参考资料Android深入浅出之Binder机制一文的读者,应该会比较容易理解这个图。这个图表明了,BpServiceManager类继承了BpInterface<IServiceManager>类,BpInterface是一个模板类,它定义在frameworks/base/include/binder/IInterface.h文件中:
template<typename INTERFACE> class BpInterface : public INTERFACE, public BpRefBase { public: BpInterface(const sp<IBinder>& remote); protected: virtual IBinder* onAsBinder(); };IServiceManager类继承了IInterface类,而IInterface类和BpRefBase类又分别继承了RefBase类。在BpRefBase类中,有一个成员变量mRemote,它的类型是IBinder*,实现类为BpBinder,它表示一个Binder引用,引用句柄值保存在BpBinder类的mHandle成员变量中。BpBinder类通过IPCThreadState类来和Binder驱动程序并互,而IPCThreadState又通过它的成员变量mProcess来打开/dev/binder设备文件,mProcess成员变量的类型为ProcessState。ProcessState类打开设备/dev/binder之后,将打开文件描述符保存在mDriverFD成员变量中,以供后续使用。
理解了这些概念之后,就可以继续分析创建Service Manager远程接口的过程了,最终目的是要创建一个BpServiceManager实例,并且返回它的IServiceManager接口。创建Service Manager远程接口主要是下面语句:
gDefaultServiceManager = interface_cast<IServiceManager>( ProcessState::self()->getContextObject(NULL));
首先是调用ProcessState::self函数,self函数是ProcessState的静态成员函数,它的作用是返回一个全局唯一的ProcessState实例变量,就是单例模式了,这个变量名为gProcess。如果gProcess尚未创建,就会执行创建操作,在ProcessState的构造函数中,会通过open文件操作函数打开设备文件/dev/binder,并且返回来的设备文件描述符保存在成员变量mDriverFD中。接着调用gProcess->getContextObject函数来获得一个句柄值为0的Binder引用,即BpBinder了,于是创建Service
Manager远程接口的语句可以简化为:
gDefaultServiceManager = interface_cast<IServiceManager>(new BpBinder(0));再来看函数interface_cast<IServiceManager>的实现,它是一个模板函数,定义在framework/base/include/binder/IInterface.h文件中:
template<typename INTERFACE> inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj) { return INTERFACE::asInterface(obj); }这里的INTERFACE是IServiceManager,于是调用了IServiceManager::asInterface函数。IServiceManager::asInterface是通过DECLARE_META_INTERFACE(ServiceManager)宏在IServiceManager类中声明的,它位于framework/base/include/binder/IServiceManager.h文件中:
DECLARE_META_INTERFACE(ServiceManager);展开即为:
#define DECLARE_META_INTERFACE(ServiceManager) \ static const android::String16 descriptor; \ static android::sp<IServiceManager> asInterface( \ const android::sp<android::IBinder>& obj); \ virtual const android::String16& getInterfaceDescriptor() const; \ IServiceManager(); \ virtual ~IServiceManager();IServiceManager::asInterface的实现是通过IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager")宏定义的,它位于framework/base/libs/binder/IServiceManager.cpp文件中:
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");展开即为:
#define IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager") \ const android::String16 IServiceManager::descriptor("android.os.IServiceManager"); \ const android::String16& \ IServiceManager::getInterfaceDescriptor() const { \ return IServiceManager::descriptor; \ } \ android::sp<IServiceManager> IServiceManager::asInterface( \ const android::sp<android::IBinder>& obj) \ { \ android::sp<IServiceManager> intr; \ if (obj != NULL) { \ intr = static_cast<IServiceManager*>( \ obj->queryLocalInterface( \ IServiceManager::descriptor).get()); \ if (intr == NULL) { \ intr = new BpServiceManager(obj); \ } \ } \ return intr; \ } \ IServiceManager::IServiceManager() { } \ IServiceManager::~IServiceManager() { }估计写这段代码的员工是从Microsoft跳槽到Google的。这里我们关注IServiceManager::asInterface的实现:
android::sp<IServiceManager> IServiceManager::asInterface(const android::sp<android::IBinder>& obj) { android::sp<IServiceManager> intr; if (obj != NULL) { intr = static_cast<IServiceManager*>( obj->queryLocalInterface(IServiceManager::descriptor).get()); if (intr == NULL) { intr = new BpServiceManager(obj); } } return intr; }这里传进来的参数obj就则刚才创建的new BpBinder(0)了,BpBinder类中的成员函数queryLocalInterface继承自基类IBinder,IBinder::queryLocalInterface函数位于framework/base/libs/binder/Binder.cpp文件中:
sp<IInterface> IBinder::queryLocalInterface(const String16& descriptor) { return NULL; }由此可见,在IServiceManager::asInterface函数中,最终会调用下面语句:
intr = new BpServiceManager(obj);即为:
intr = new BpServiceManager(new BpBinder(0));回到defaultServiceManager函数中,最终结果为:
gDefaultServiceManager = new BpServiceManager(new BpBinder(0));这样,Service Manager远程接口就创建完成了,它本质上是一个BpServiceManager,包含了一个句柄值为0的Binder引用。
在Android系统的Binder机制中,Server和Client拿到这个Service Manager远程接口之后怎么用呢?
(1) 对Server来说,就是调用IServiceManager::addService这个接口来和Binder驱动程序交互了,即调用BpServiceManager::addService 。而BpServiceManager::addService又会调用通过其基类BpRefBase的成员函数remote获得原先创建的BpBinder实例,接着调用BpBinder::transact成员函数。在BpBinder::transact函数中,又会调用IPCThreadState::transact成员函数,这里就是最终与Binder驱动程序交互的地方了。回忆一下前面的类图,IPCThreadState有一个PorcessState类型的成中变量mProcess,而mProcess有一个成员变量mDriverFD,它是设备文件/dev/binder的打开文件描述符,因此,IPCThreadState就相当于间接在拥有了设备文件/dev/binder的打开文件描述符,于是,便可以与Binder驱动程序交互了。
(2)对Client来说,就是调用IServiceManager::getService这个接口来和Binder驱动程序交互了。具体过程上述Server使用Service Manager的方法是一样的,这里就不再累述了。
IServiceManager::addService和IServiceManager::getService这两个函数的具体实现,在下面两篇文章中,会深入到Binder驱动程序这一层,进行详细的源代码分析,以便更好地理解Binder进程间通信机制,敬请关注。
原文地址:Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
四、Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
在前面介绍了在Android系统中Binder进程间通信机制中的Server角色是如何获得Service Manager远程接口的,即defaultServiceManager函数的实现。Server获得了Service Manager远程接口之后,就要把自己的Service添加到Service Manager中去,然后把自己启动起来,等待Client的请求。本文将通过分析源代码了解Server的启动过程是怎么样的。本文通过一个具体的例子来说明Binder机制中Server的启动过程。我们知道,在Android系统中,提供了多媒体播放的功能,这个功能是以服务的形式来提供的。这里,我们就通过分析MediaPlayerService的实现来了解Media Server的启动过程。
首先,看一下MediaPlayerService的类图,以便我们理解下面要描述的内容。
我们将要介绍的主角MediaPlayerService继承于BnMediaPlayerService类,熟悉Binder机制的同学应该知道BnMediaPlayerService是一个Binder Native类,用来处理Client请求的。BnMediaPlayerService继承于BnInterface<IMediaPlayerService>类,BnInterface是一个模板类,它定义在frameworks/base/include/binder/IInterface.h文件中:
template<typename INTERFACE> class BnInterface : public INTERFACE, public BBinder { public: virtual sp<IInterface> queryLocalInterface(const String16& _descriptor); virtual const String16& getInterfaceDescriptor() const; protected: virtual IBinder* onAsBinder(); };这里可以看出,BnMediaPlayerService实际是继承了IMediaPlayerService和BBinder类。IMediaPlayerService和BBinder类又分别继承了IInterface和IBinder类,IInterface和IBinder类又同时继承了RefBase类。
实际上,BnMediaPlayerService并不是直接接收到Client处发送过来的请求,而是使用了IPCThreadState接收Client处发送过来的请求,而IPCThreadState又借助了ProcessState类来与Binder驱动程序交互。有关IPCThreadState和ProcessState的关系,可以参考上一篇文章浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service
Manager接口之路,接下来也会有相应的描述。IPCThreadState接收到了Client处的请求后,就会调用BBinder类的transact函数,并传入相关参数,BBinder类的transact函数最终调用BnMediaPlayerService类的onTransact函数,于是,就开始真正地处理Client的请求了。
了解了MediaPlayerService类结构之后,就要开始进入到本文的主题了。
首先,看看MediaPlayerService是如何启动的。启动MediaPlayerService的代码位于frameworks/base/media/mediaserver/main_mediaserver.cpp文件中:
int main(int argc, char** argv) { // 这句代码的作用是通过ProcessState::self()调用创建一个ProcessState实例。 // ProcessState::self()是ProcessState类的一个静态成员变量 sp<ProcessState> proc(ProcessState::self()); //获得Service Manager远程接口 sp<IServiceManager> sm = defaultServiceManager(); LOGI("ServiceManager: %p", sm.get()); AudioFlinger::instantiate(); MediaPlayerService::instantiate(); CameraService::instantiate(); AudioPolicyService::instantiate(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); }
这里我们不关注AudioFlinger和CameraService相关的代码。
先看下面这句代码:
sp<ProcessState> proc(ProcessState::self());这句代码的作用是通过ProcessState::self()调用创建一个ProcessState实例。ProcessState::self()是ProcessState类的一个静态成员变量,定义在frameworks/base/libs/binder/ProcessState.cpp文件中:
sp<ProcessState> ProcessState::self() { if (gProcess != NULL) return gProcess; AutoMutex _l(gProcessMutex); if (gProcess == NULL) gProcess = new ProcessState; return gProcess; }这里可以看出,这个函数作用是返回一个全局唯一的ProcessState实例gProcess。全局唯一实例变量gProcess定义在frameworks/base/libs/binder/Static.cpp文件中:
Mutex gProcessMutex; sp<ProcessState> gProcess;再来看ProcessState的构造函数:
// 一是通过open_driver函数打开Binder设备文件/dev/binder, // 并将打开设备文件描述符保存在成员变量mDriverFD中; ProcessState::ProcessState() : mDriverFD(open_driver()) , mVMStart(MAP_FAILED) , mManagesContexts(false) , mBinderContextCheckFunc(NULL) , mBinderContextUserData(NULL) , mThreadPoolStarted(false) , mThreadPoolSeq(1) { if (mDriverFD >= 0) { // XXX Ideally, there should be a specific define for whether we // have mmap (or whether we could possibly have the kernel module // availabla). #if !defined(HAVE_WIN32_IPC) // mmap the binder, providing a chunk of virtual address space to receive transactions. // 二是通过mmap来把设备文件/dev/binder映射到内存中。 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); if (mVMStart == MAP_FAILED) { // *sigh* LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); close(mDriverFD); mDriverFD = -1; } #else mDriverFD = -1; #endif } if (mDriverFD < 0) { // Need to run without the driver, starting our own thread pool. } }
这个函数有两个关键地方,一是通过open_driver函数打开Binder设备文件/dev/binder,并将打开设备文件描述符保存在成员变量mDriverFD中;二是通过mmap来把设备文件/dev/binder映射到内存中。
先看open_driver函数的实现,这个函数同样位于frameworks/base/libs/binder/ProcessState.cpp文件中:
static int open_driver() { if (gSingleProcess) { return -1; } // 通过open文件操作函数来打开/dev/binder设备文件 int fd = open("/dev/binder", O_RDWR); if (fd >= 0) { fcntl(fd, F_SETFD, FD_CLOEXEC); int vers; // 再调用ioctl文件控制函数来分别执行BINDER_VERSION和BINDER_SET_MAX_THREADS两个命令来和Binder驱动程序进行交互, // 前者用于获得当前Binder驱动程序的版本号,后者用于通知Binder驱动程序, #if defined(HAVE_ANDROID_OS) status_t result = ioctl(fd, BINDER_VERSION, &vers); #else status_t result = -1; errno = EPERM; #endif if (result == -1) { LOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); close(fd); fd = -1; } if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { LOGE("Binder driver protocol does not match user space protocol!"); close(fd); fd = -1; } #if defined(HAVE_ANDROID_OS) // MediaPlayerService最多可同时启动15个线程来处理Client端的请求。 size_t maxThreads = 15; result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); if (result == -1) { LOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); } #endif } else { LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); } return fd; }这个函数的作用主要是通过open文件操作函数来打开/dev/binder设备文件,然后再调用ioctl文件控制函数来分别执行BINDER_VERSION和BINDER_SET_MAX_THREADS两个命令来和Binder驱动程序进行交互,前者用于获得当前Binder驱动程序的版本号,后者用于通知Binder驱动程序,MediaPlayerService最多可同时启动15个线程来处理Client端的请求。
open在Binder驱动程序中的具体实现,请参考前面一篇文章浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路,这里不再重复描述。打开/dev/binder设备文件后,Binder驱动程序就为MediaPlayerService进程创建了一个struct
binder_proc结构体实例来维护MediaPlayerService进程上下文相关信息。
我们来看一下ioctl文件操作函数执行BINDER_VERSION命令的过程:
status_t result = ioctl(fd, BINDER_VERSION, &vers);这个函数调用最终进入到Binder驱动程序的binder_ioctl函数中,我们只关注BINDER_VERSION相关的部分逻辑:
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_VERSION: if (size != sizeof(struct binder_version)) { ret = -EINVAL; goto err; } // 很简单,只是将BINDER_CURRENT_PROTOCOL_VERSION写入到传入的参数arg指向的用户缓冲区中去就返回了。 if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &((struct binder_version *)ubuf)->protocol_version)) { ret = -EINVAL; goto err; } break; ...... } ret = 0; err: ...... return ret; }
很简单,只是将BINDER_CURRENT_PROTOCOL_VERSION写入到传入的参数arg指向的用户缓冲区中去就返回了。BINDER_CURRENT_PROTOCOL_VERSION是一个宏,定义在kernel/common/drivers/staging/android/binder.h文件中:
/* This is the current protocol version. */ #define BINDER_CURRENT_PROTOCOL_VERSION 7这里为什么要把ubuf转换成struct binder_version之后,再通过其protocol_version成员变量再来写入呢,转了一圈,最终内容还是写入到ubuf中。我们看一下struct binder_version的定义就会明白,同样是在kernel/common/drivers/staging/android/binder.h文件中:
/* Use with BINDER_VERSION, driver fills in fields. */ struct binder_version { /* driver protocol version -- increment with incompatible change */ signed long protocol_version; };从注释中可以看出来,这里是考虑到兼容性,因为以后很有可能不是用signed long来表示版本号。
这里有一个重要的地方要注意的是,由于这里是打开设备文件/dev/binder之后,第一次进入到binder_ioctl函数,因此,这里调用binder_get_thread的时候,就会为当前线程创建一个struct binder_thread结构体变量来维护线程上下文信息,具体可以参考浅谈Service
Manager成为Android进程间通信(IPC)机制Binder守护进程之路一文。
接着我们再来看一下ioctl文件操作函数执行BINDER_SET_MAX_THREADS命令的过程:
相关文章推荐
- Android中的消息通知Toast和Notification
- 源码解析EventBus
- 浅谈屏幕适配之measure(上)
- Android开发者的Kotlin:书
- mac下反编译Android的apk步骤
- android分页代码
- Android开发之Spinner控件使用
- Android编程之BitmapFactory.decodeResource加载图片缩小的原因及解决方法
- 王学岗帧动画(一)——帧动画的简单实用
- 源码级分析Android系统启动流程
- 【Android笔记】ListView
- mac电脑批量解压android apk文件图形化工具--apkDecode
- android JNI生成.so文件(arm64-v8a等)配置
- 获取android 应用程序包名、图标、应用名称
- android studio开发环境搭建,国内服务器代理下载android sdk
- Android中使用Handler造成内存泄露的分析和解决
- 国内Android开发牛人
- Android简单的使用listview动态生成不同页面
- android下运行时动态链接dlopen()和dlsym()的实现
- Android自定义View动画(进度条)