概述
StartThreadPool和joinThreadPool分析:
startThreadPool()的实现如下所示:
上面spawnpooledThread()函数的实现如下所示:
PoolThread是在IPCThreadState中定义的一个Thread子类,它的实现如下所示:
下面看看IPCThreadState的joinThreadPool的实现,因为新创建的线程也会调用这个函数,具体代码如下所示:
我们的两个伙计在talkWithDriver,他们希望能从binder设备那里找到点可做的事情。
到底有多少个线程在为Service服务呢?目前看来是两个。
1. startThreadPool中新启动的线程通过joinThreadPool读取binder设备,查看是否有请求。
2. 主线程也调用joinThradPool读取binder设备,查看是否有请求。看来binder设备是支持多线程操作的,启动一定是做了同步方面的工作
MediaServer这个进程一共注册了4个服务,繁忙的时候,两个线程会不会显得有点少呢?另外,如果实现的服务负担不是很重,完全可以不调用startThreadPool创建新的线程,使用主线程即可胜任。
Binder通信层和业务层的关系:
服务总管ServiceManager
ServiceManager的原理:
前面说过,defaultServiceManager返回的是一个BpServiceManager,通过它可以把命令请求发送给handle值为0的目的端。按照之前的IServiceManager“家谱”来看,无论如何也应该有一个类从BnServiceanager派生出来并处理这些来自远方的请求把?
很可惜,源码中没有这样一个类存在。而是由ServceManager来完成了BnServiceManager未尽的工作。代码在Service_manager.c中。
ServiceManager的入口函数:
打开binder设备,实现如下:
成为老大:
怎么才成为系统中独一无二的manager了呢?manager的实现如下:
binder_loop是一个很尽责的函数,它老是围绕者binder设备转悠,实现如下:
集中处理:
往binder_loop中传的函数指针是svcmgr_handler,它的代码如下:
服务的注册:
上面提到的switch/case语句,将实现IServiceManager中定义的各个业务函数,下面重点看do_add_service这个函数,它最终完成了对addService请求的处理,代码如下:
先将上面的函数暂时放一下,先介绍svc_can_register函数
1. 不是什么都可以注册的
do_add_service函数中的svc_can_register,是用来判断注册服务的进程是否有权限的,代码如下所示:
allowed结构数组控制那些权限达不到root和system的进程,它的定义如下所示:
所以,如果Server进程权限不够root和system,那么就要记住allowed中添加线应的项。
添加服务项。
再回到我们的do_add_service中,如下所示:
到此为止,服务注册分析完毕。可以看到,ServiceManager不过就是保存了一些服务的信息。这样做有什么意义?
1. ServiceManager能集中管理系统内的所有服务,它能施加权限控制,并不是任何进程都能注册服务的。
2. ServiceManager支持通过字符串名称来查看对应的Service。这个功能很像DNS。
3. 由于各种原因的影响,Server进程可能生死无常。如果让每个Client都去检测,压力太大了。现在有了统一的管理机构,Client只需要查询ServiceManager,就能把握动向,得到最新信息。这可能正是ServiceManager存在的最大意义把。
MediaPlayerService和它的Client
前面一直在讨论ServiceManager和它的Client,现在以MediaPlayerService的Client来进行分析。由于ServiceManager不是从BnServiceManager中派生的,所以之前没有讲请求数据是如何从通信层传递到业务层并进行处理的。
我们以MediaPlayerService和它的Client作为分析对象,试着解决这些遗留问题。
查询ServiceManager
前面分析过ServiceManager的作用,一个Client想要得到某个Service的信息,就必须线和ServiceManager打交道,通过调用getService函数来获取对应的Service的信息。下面看下源于IMediaDeathNotifier.cpp中的例子getMediaPlayerService(),它的代码如下所示:
有了BpMediaPlayerService,就能够使用任何IMediaPlayerService提供的业务逻辑函数了。例如createMediaRecorder和createMetadataRetriever等
显而易见的是,调用的这些函数都将把请求数据打包发送给Binder驱动,并由BpBinder中的handle值找到对应端的处理者来处理。这中间的过程为:
1. 通信层接受到请求。
2. 递交给业务层处理。
下面进一步了解这中间的过程:
子承父业:
根据前面的分析可知,MediaPlayerService驻留在MediaServer进程中,这个进程有两个线程在talkWithDriver。假设其中有意个线程收到了请求信息,它最终会通过executeCommand调用来处理这个请求,实现代码如下所示:
BBinder和业务层有什么关系?我们以MediaPlayerService为例,来梳理一下其派生关系,如下所示:
BnMediaPlayerService实现了onTransact函数,它将根据消息码调用对应的业务逻辑函数,这些业务函数由MediaPlayerService来实现,这一路的历程,如下面的代码所示:
下面看看onTransact的实现:
Binder和线程的关系:
以MS为例,如果现在程序运行正常,此时MS则:
1. 通过startThreadPool启动一个线程,这个线程在talkWithDriver
2. 主线程通过joinThreadPool也在talkWithDriver。
至此我们直到,有两个线程在和BInder设备打交道。这时在业务逻辑上需要与ServiceManager交互,比如要调用listServices打印所有服务的名字,假设这是MS中的第三个线程。按照之前的分析,它最终会调用IPCThradState的transact函数,这个函数会talkWithDriver并把请求发到ServiceManager进程,然后等待来自Binder设备的回复。那么现在一共有三个线程都在talkWithDriver。
ServiceManager处理完了listServices,把回复结果写回Binder驱动,那么MS中哪个线程会收到回复呢?
显而易见,当然是调用listServices的那个线程会得到结果。为什么?因为如果不这么做,则会导致下面的情况发生:
1. 如果是没有调用listServices的线程1或线程2得到回复,那么它们应该唤醒调用listServices的线程3.因为这时已经有了结果,线程3应该从listServices函数调用中返回
2. 这其中的线程等待,唤醒,切换会浪费不少宝贵的时间片,而且代码逻辑会及其复杂。
看来,。Binder设备把发起请求的线程牢牢地栓住了,必须收到回复才会放它离开。这种一一对应的方式极大简化了代码层的处理逻辑。
在socket编程中,如果一个socket关闭了,我们会无比希望另一端的select/poll/epoll/WaitForXXX有相应的返回,以示通知:
最后
以上就是苗条大船为你收集整理的Binder机制(3)的全部内容,希望文章能够帮你解决Binder机制(3)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复