跟着《Android艺术开发探索》一书学习了android中的进程间通信,刚好和最近学习操作系统的进程间通信做了比较。
android的IPC主要就是基于Binder,Messenger、ContentProvider的底层都是基于Binder。所以这篇文章主要分析一下Binder的底层实现。
Binder
Linux下的进程间通信
Linux跨进程通信涉及到一些基本概念:
- 进程间隔离
进程与进程间内存是不共享的。两个进程就像两个平行的世界,A进程无法访问B进程的数据,B也无法访问A。两个进程间要进行数据交互需采用特殊的通信机制:进程间通信(IPC)。
- 进程空间划分:用户空间(User Space)/内核空间(Kernel Space)
现在的操作系统采用的都是虚拟存储技术。对32位系统而言,它的虚拟存储空间是2^32,就是4GB。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也可以访问底层硬件设备的权限。为保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间和内核空间。
- 系统调用:用户态/内核态
虽然划分了用户空间和内核空间,但是用户在某些情况下需要访问内核资源(文件操作等)。这时候就需要借助系统调用。
当一个进程执行系统调用而陷入内核代码中执行时,称此进程处于内核态;当进程在执行用户自己的代码的时候,称进程处于用户态
系统调用主要下面两个函数实现:
copy_from_user()//将数据从用户空间拷贝到内核空间
copy_to_user()//将数据从内核空间拷贝到用户空间
从上图可以看到传统的IPC通信存在两个问题
- 一次数据传递需要经过两次拷贝:内存缓存区-->内核缓存区-->内存缓存区
- 浪费空间和时间:接收区的缓存区由数据接收进程提供,但接收进程并不知道要多大的空间来存放传递过来的数据。
Binder下的进程间通信
Binder通信采用C/S架构,包含Client、Server、ServiceManager以及Binder驱动。其中ServiceManager用于管理系统中的各种服务。架构图如下:
从图中可以看到注册服务和获取服务都需要ServiceManager,这里的ServiceManager是Native层的(C++),不是framework层的ServiceManager(java)。SericeManager是Binder机制中的大管家,是android进程间通信的守护进程。看下图中标出的三大步骤:
- 注册服务(addService):Server进程要先注册服务到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。
- 获取服务(getService):Client进程使用某个Service之前,必须向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。
- 使用服务:Client根据得到的Service信息建立与Service所在的Server进程的通路,之后就可以与Service进行交互。该过程:Client是客户端,Server是服务端。
- 最重要的一点是,三者的交互都是基于Binder通信的,通过任意两者的关系都可以揭示Binder的奥秘。
图中Client、Server、ServiceManager彼此之间不是直接交互,而是通过Binder驱动进行交互,实现IPC方式。Binder驱动位于内核空间,它们都位于用户空间。
Binder Driver
注册服务和获取服务都基于ServiceManager,也就是需要获取ServiceManager,获取之前需要启动ServiceManager,最终会调用底层Binder驱动。所以先从Binder的底层驱动看起。
Binder驱动底层的驱动架构和Linux驱动一样,Binder驱动在以misc设备(Linux的一种杂项特殊字符设备),作为虚拟字符设备,没有直接操作硬件,只是对设备内存的处理。Binder驱动设备的初始化(binder_init),打开(binder_open),映射(binder_mmap),数据操作(binder_ioctl)。
用户态程序调用内核层程序需要陷入内核态,会进行系统调用(syscall),例如:打开Binder驱动方法的调用链为open()-->_open()-->binder_open()。open()为用户态方法,_open()是系统调用,通过查找对应调用到内核binder驱动的binder_open()方法。下面从四个方法来看binder驱动:
binder_init
注册字符驱动设备misc
binder_open
打开binder驱动设备。这里会创建binder_proc对象,也就是binder进程,为binder初始化一些信息,并把每次都创建的binder_proc对象加入binder_procs链表中。
binder_mmap
我觉得这是binder驱动的核心点,binder之所以优于传统IPC就是因为binder有内存映射。
主要功能(分三步):
- 首先在内核虚拟地址空间申请一块和用户虚拟内存大小相同的内存;
- 再申请1个page(页)大小的物理内存;
- 再将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间。
这样就实现了用户空间的Buffer和内核空间的Buffer同步操作的功能。
映射的这个想的很巧妙,这使得Binder通信只需要从用户空间复制一次信息到内核空间就可以了。但是用户空间怎么就通过映射读到了数据,是向HashMap那样有一个key对应这么,通过下面这张图可以看到:
虚拟进程地址空间和虚拟内核地址空间都映射到同一块物理内存。当client端向server端发送数据时,client把自己进程空间的通信数据拷贝(copy_from_user)到内核空间,server端和内核共享数据,不需要再拷贝数据。通过内存地址的偏移量获得内存地址(这里有点像今天刚上课听的微机原理里的寻址方法,感觉很有意思),只发生了一次拷贝。
binder_ioctl
从上面的ServiceManager交互的图中我们可以看到Client和Server实际上是通过ioctl和driver交互的。
binder_ioctl()函数负责在两个进程间收发IPC数据。这就可以对应到之前写过的Binder通信的文件。我们将数据放入输入型Parcel _data,用输出型Parcel _reply接收服务端返回的数据。执行ioctl操作时,read_buffer需要持有binder_main_lock同步锁,并处于wait_event_freezable过程,等有数据到来是则唤醒并尝试持有同步锁。
ServiceManager
分析完了驱动层知道了驱动层是怎么被初始化 打开 被分配内存。就需要再进一步了解ServiceManager,ServiceManager作为大管家这里主要分析它的启动以及注册、查询服务,如何和binder驱动建立联系。
ServiceManager自行编写了binder.c直接和binder驱动进行通信,并且只有一个循环binder_lopp来进行读取和处理事务。其本身工作相对简单,主要功能就是查询和注册服务。Binder IPC通信过程中,其实是BpBinder和BBinder之间的通信,后面会介绍到。
这里有一张别人画的ServiceManager从创建到实现自己功能的时序图,我觉得对从整体来把握ServiceManager有一定的帮助和统领的作用。
ServiceManager启动过程
参照时序图,启动过程主要分为以下几个阶段:
- 打开binder驱动:binder_open;
- 注册成为binder服务的大管家:binder_become_context_manager;
- 进入无线循环,处理client端发来的请求:binder_loop。
ServiceManager是由init进程通过解析init.rc文件而创建的。启动ServiceManager的入口函数是service_manager.c中的main()方法,代码如下:
main
int main(int argc, char **argv) {
struct binder_state *bs;
//BINDER_SERVICE_MANAGER值为null,用来标志文件的格式
void *svcmgr = BINDER_SERVICE_MANAGER;
//打开binder驱动,申请大小为128KB字节大小的内存空间
bs = binder_open(128*1024);
//成为manager(上下文管理者)
if (binder_become_context_manager(bs)) {
return -1;
}
//进入无限循环状态,处理client端发来的请求
binder_loop(bs, svcmgr_handler);
return 0;
}
binder_open
struct binder_state *binder_open(size_t mapsize)
{
struct binder_state *bs;【见小节2.2.1】
struct binder_version vers;
bs = malloc(sizeof(*bs));
if (!bs) {
errno = ENOMEM;
return NULL;
}
````
bs->mapsize = mapsize;
//通过系统调用,mmap内存映射,mmap必须是page的整数倍
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
if (bs->mapped == MAP_FAILED) {
goto fail_map; // binder设备内存无法映射
}
return bs;
`````
}
binder_open主要就是打开驱动设备,系统调用mmap内存映射。
binder_become_context_manager
int binder_become_context_manager(struct binder_state *bs)
{
//0是设置的handle,BINDER_SET_CONTEXT_MGR设置ServiceManager结点
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
binder_ioctl
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
binder_lock(__func__);
switch (cmd) {
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp);
break;
}
case :...
}
binder_unlock(__func__);
}
根据参数BINDER_SET_CONTEXT_MGR,最终调用binder_ioctl_set_ctx_mgr()方法,这个过程会持有binder_main_lock。
binder_ioctl_set_ctx_mgr
static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
kuid_t curr_euid = current_euid();
//保证只创建一次mgr_node对象
if (binder_context_mgr_node != NULL) {
ret = -EBUSY;
goto out;
}
if (uid_valid(binder_context_mgr_uid)) {
...
} else {
//设置当前线程euid作为Service Manager的uid
binder_context_mgr_uid = curr_euid;
}
//创建ServiceManager实体
binder_context_mgr_node = binder_new_node(proc, 0, 0);
````
}
在这里创建了ServiceManager实体binder_new_node()方法中创建了binder_node结构体,也就是binder实体。
binder_loop
void binder_loop(struct binder_state *bs, binder_handler func) {
int res;
struct binder_write_read bwr;
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
//将BC_ENTER_LOOPER命令发送给binder驱动,让Service Manager进入循环
//binder_write方法中调用ioctl方法,将bwr数据发送给binder驱动,调用binder_ioctl方法,获取binder_thread,进行binder的读写操作。
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); //进入循环,不断地binder读写过程
if (res < 0) {
break;
}
// 接收到请求,交给binder_parse处理
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
break;
}
if (res < 0) {
break;
}
}
}
binder_parse
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
int r = 1;
uintptr_t end = ptr + (uintptr_t) size;
while(ptr < end){
switch(cmd) {
case BR_NOOP: //无操作,退出循环
break;
case BR_TRANSACTION_COMPLETE:
break;
case BR_INCREFS:
case BR_ACQUIRE:
case BR_RELEASE:
case BR_DECREFS:
ptr += sizeof(struct binder_ptr_cookie);
break;
case BR_TRANSACTION: {
`````
//初始化reply
bio_init(&reply, rdata, sizeof(rdata), 4);
//从txn解析出binder_io信息
bio_init_from_txn(&msg, txn);
//func指向svcmgr_handler,调用svcmgr_handler
res = func(bs, txn, &msg, &reply);
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
`````
}
break;
case BR_REPLY: {
break;
}
case BR_DEAD_BINDER: {
break;
}
case BR_FAILED_REPLY:
r = -1;
break;
case BR_DEAD_REPLY:
r = -1;
break;
default:
return -1;
}
}
这个方法在binder_loop中,用于接收请求,根据请求的类别执行对应的方法。
svcmgr_handler
binder_loop中传递的函数指针是svcmgr_handler
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
`````
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
//获取服务名
s = bio_get_string16(msg, &len);
//根据名称查找相应服务,返回服务所对应的handle
handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);
//将handle封装到reply
bio_put_ref(reply, handle);
return 0;
case SVC_MGR_ADD_SERVICE:
//获取服务名
s = bio_get_string16(msg, &len);
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
//注册指定服务(检查权限、根据名称检索服务、查询到同名的释放后保存当前服务)
if (do_add_service(bs, s, len, handle, txn->sender_euid,allow_isolated, txn->sender_pid))
return -1;
break;
//得到当前系统中已经注册的所有的名字
case SVC_MGR_LIST_SERVICES:
`````
}
可以看到这个方法中:查询服务、注册服务,以及列举所有服务
总结
- ServiceManager启动流程
- 打开binder驱动(binder_open),调用mmap()方法分配128KB的内存映空间;
- 调用binder_become_context_manager(),参数BINDER_SET_CONTEXT_MGR信号传入ioctl()方法,调用binder_ioctl创建出ServiceManager实体;
- 调用binder_loop()方法,循环接收客户端传来的信息,循环读写操作。通过iotcl将数据发送给binder驱动(binder_loop()-->binder_write()-->iotcl()-->binder_ioctl()-->binder_iotcl_write_read()进行数据的读写和将数据拷贝到用户buf)
- 注册服务、查询服务。
获取服务
在进程注册服务和获取服务的过程之前,都需要先调用defaultServiceManager()方法来获取getDefaultServiceManager对象。
defaultServiceManager
sp defaultServiceManager()
{
if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
{
AutoMutex _l(gDefaultServiceManagerLock); //加锁
while (gDefaultServiceManager == NULL) {
//创建真正的gDefaultServiceManager
gDefaultServiceManager = interface_cast(
ProcessState::self()->getContextObject(NULL));
if (gDefaultServiceManager == NULL)
sleep(1);
}
}
return gDefaultServiceManager;
}
获取ServiceManager对象采用了单例模式。很有意思的一点的事,ggDefaultServiceManager是在一个循环中被创建的。这是因为当获取ServiceManager时,ServiceManager可能还未准备就绪,因此通过sleep 1s,通过循环尝试直到成功。
getDefaultServiceManager分为下面三个步骤:
- ProcessState::self():用于获取ProcessState对象;
- getContextObject():用于获取BpBinder对象;
- interface_cast
:用于获取BpServiceManager对象。
独一无二的ProcessState
每个进程只有一个ProcessState。
ProcessState::self
sp ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) {
return gProcess;
}
//实例化ProcessState
gProcess = new ProcessState;
return gProcess;
}
获取ProcessState对象也采用单例模式,保证了每个进程只有一个ProcessState对象。其中gProcess和gProcessMuted保存在static.c类的全局变量。
构造ProcessState
ProcessState::ProcessState()
: mDriverFD(open_driver())//打开binder驱动
, mVMStart(MAP_FAILED)//映射内存的起始地址
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
//mDriverFD记录binder驱动的fd,用于访问binder设备
if (mDriverFD >= 0) {
//采用内存映射函数mmap,给binder分配一块虚拟地址空间,用来接收事务
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
close(mDriverFD); //没有足够空间分配给/dev/binder,则关闭驱动
mDriverFD = -1;
}
}
}
- 因为ProcessState单例模式的唯一性,一个进程只能打开一个binder设备。
- BINDER_VM_SIZE:(102410241)-(4096*2) = 1M-8K 。
- DEFAULT_ MAX_BINDER_THREADS:binder默认的最大可并发访问线程数。
打开binder设备
static int open_driver(){
int fd = open("/dev/binder",O_RDWR);
if(fd > 0){
`````
size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
// 通过ioctl设置binder驱动,能支持的最大线程数
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
}
}
从这里可以从binder驱动层打开binder驱动的方式联系起来open_driver()-->open-->binder_open()。
Process::self函数功能:
- 打开dev/binder设备,相当于和内核的Binder驱动有了交互。
- 对返回的fd使用mmap,这样Binder驱动会分配一块内训来接收数据。
- ProcessState具有唯一性,因此一个进程只打开设备一次。
获取BpBinder对象
getContextObject
继续defaultServiceManager()中的方法
sp ProcessState::getContextObject(const sp& caller){
if(supportProcess()){
return getStrongProxyForHandle(0);
}else{
return getContextObject(String16("default"),caller);
}
}
- 该方法返回IBinder对象。
- supportProcess():根据open_driver()函数是否可以打开来判断设备是否支持进程,真实设备肯定支持process,所以调用getStrongProxyForHandle函数。
- getStrongProxyForHandle(0):它传入的参数名叫handle,是对资源的一种标识。也就是一个资源项放在了一个资源数组中,handle的值是该资源项在数组中的索引。
getStrongProxyForHandle
sp ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp result;
AutoMutex _l(mLock);
//查找handle对应的资源项【见小节3.3】
handle_entry* e = lookupHandleLocked(handle);
if (e != NULL) {
IBinder* b = e->binder;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
if (handle == 0) {
Parcel data;
//通过ping操作测试binder是否准备就绪
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, NULL, 0);
if (status == DEAD_OBJECT)
return NULL;
}
//当handle值所对应的IBinder不存在或弱引用无效时,则创建BpBinder对象【见小节3.4】
b = new BpBinder(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
result.force_set(b);
e->refs->decWeak(this);
}
}
return result;
}
这个方法主要创建BpBinder对象。
- lookipHandleLocked(handle):根据索引查找对应的资源项。如果没有,会创建新的资源项。
- 对于新的资源项,其binder为空(b==null),创建一个BpBinder。
看到这里就会好奇到底BpBinder是干什么的。BpBinder和BBinder都是android中和Binder通信相关的代表,它们都从IBinder类中派生而来。和ServieManager交互获取服务和注册服务的其实是这两个类,不是我们俗称的客户端和服务端。
从上面的图可以看到;
- BpBinder是客户端用来和Server交互的代理类。
- BBinder则是和proxy相对应的,它是和proxy交互的对端。可以说proxy代表客户端,BBinder代表服务端
- BpBinder和BBinder必须是一一对应的。Binder系统通过handle值来标识对应BBinder。在new BpBinder(handle)时传入了0,0代表的就是ServiceManager所对应的BBinder。
创建BpBinder
BpBinder::BpBinder(int32_t handle)
: mHandle(handle)
, mAlive(1)
, mObitsSent(0)
, mObituaries(NULL)
{
extendObjectLifetime(OBJECT_LIFETIME_WEAK);
IPCThreadState::self()->incWeakHandle(handle);
}
通过源码,BpBinder、BBinder这两个类并没有对ProcessState打开的/dev/binder设备进行操作,也就是他们之间没有交互。
BpServiceManager
通过下面来看到底BpBinder为什么与通信相关。
interface_cast
依旧从defaultServiceManager方法中入手:
template
inline sp interface_cast(const sp& obj)
{
return INTERFACE::asInterface(obj);
}
这是一个模板函数,等价于:
inline sp interface_cast(const sp& obj)
{
return IServiceManager::asInterface(obj);
}
这里又转移到了IServiceManager中
IServiceManager
class IServiceManager : public IInterface{
public:
//很重要的宏定义
DECLARE_META_INTERFACE(ServiceManager);
``````
//这里是ServiceManager提供的业务函数
virtual sp getService();
virtual sp checkService();
virtual status_t addService();
virtual Vector listServices() = 0;
``````
}
IServiceManager定义了ServiceManager所提供的服务,这是如何和IBinder通信家族联系在一起的。就需要宏定义。宏定义将业务和通信挂钩,展开后的代码如下:
const android::String16 IServiceManager::descriptor(“android.os.IServiceManager”);
//实现了getInterfaceDescriptor函数
const android::String16& IServiceManager::getInterfaceDescriptor() const
{
//返回android.os.IServiceManager
return IServiceManager::descriptor;
}
android::sp IServiceManager::asInterface(const android::sp& obj)
{
android::sp intr;
if(obj != NULL) {
intr = static_cast(
obj->queryLocalInterface(IServiceManager::descriptor).get());
if (intr == NULL) {
intr = new BpServiceManager(obj);
}
}
return intr;
}
IServiceManager::IServiceManager () { }
IServiceManager::~ IServiceManager() { }
- 实现了asInterface():new BpServiceManager(obj),obj是刚才创建出的BpBinder(0)。追溯回去,发现interface_cast利用BpBinder对象为参数新建了BpServiceManager对象。
gDefaultServiceManager = interface_cast(new BpBinder(0));
BpServiceManager实例化
创建BpServiceManager对象的过程,会先初始化父类对象
BpServiceManager初始化
//impl是IBinder类型
BpServiceManager(const sp& impl)
: BpInterface(impl)
{ }
BpInterface初始化
inline BpInterface::BpInterface(const sp& remote)
:BpRefBase(remote)
{ }
BpRefBase初始化
BpRefBase::BpRefBase(const sp& o)
: mRemote(o.get()), mRefs(NULL), mState(0)
{
extendObjectLifetime(OBJECT_LIFETIME_WEAK);
if (mRemote) {
mRemote->incStrong(this);
mRefs = mRemote->createWeak(this);
}
}
- 最终mRemote = new BpBinder(0)
- BpBinder对象的handle值为0,和它对应的BBinder建立一一对应;BpServiceManager对象,它的mRemote值是BpBinder。
BpServiceManager不仅实现了IServiceManager的业务函数,还有BpBinder作为通信代表。
总结
defaultServiceManager等价于new BpServiceManager(new BpBinder(0))
ProcessState::self()主要工作:
- 打开/dev/binder驱动设备
- 调用mmap(),创建大小为1M - 8k的内存地址空间
- 设定当前进程的最大并发binder线程个数为15
ServiceManager可直接与binder驱动进行交互。重要的还有IBinder的通信族。
BpServiceManager将通信层与业务层逻辑合为一体:
- 通过继承IServiceManager接口,实现接口中的业务逻辑函数(添加service等)
- 通过成员变量mRemote = new BpBinder(0)进行Binder通信工作
- BpBinder通过handle来指向所对应的BBinder
注册服务
注册服务我们通过MediaService的具体注册过程来看
media服务注册
int main(int argc __unused, char** argv)
{
...
InitializeIcuOrDie();
//获得ProcessState实例对象
sp proc(ProcessState::self());
//获取BpServiceManager对象
sp sm = defaultServiceManager();
AudioFlinger::instantiate();
//注册多媒体服务
MediaPlayerService::instantiate();
ResourceManagerService::instantiate();
CameraService::instantiate();
AudioPolicyService::instantiate();
SoundTriggerHwService::instantiate();
RadioService::instantiate();
registerExtensions();
//启动Binder线程池
ProcessState::self()->startThreadPool();
//当前线程加入到线程池
IPCThreadState::self()->joinThreadPool();
}
上面是native层中media服务注册的过程。
分别为下面几个过程:
- 获取ProcessState对象
- 获取ServiceManager对象,也就是BpManagerService
- 注册服务
- 启动binder线程池
- 将当前线程加入到线程池
获取取ProcessState对象和ServiceManager对象在上节已经分析过,这里主要看注册服务的过程。
业务层的工作
instantiate()
void MediaPlayerService::instantiate() {
//注册服务
defaultServiceManager()->addService(String16("media.player"), new MediaPlayerService());
}
注册服务MediaPlayerService:
- defaultServiceManager返回BpManagerService,同时会创建ProcessState和BpBinder对象
- 等价于调用BpServiceManager-->addService
BpSM.addService()
virtual status_t addService(const String16& name, const sp& service, bool allowIsolated) {
Parcel data, reply;
//Parcel相当于数据包,在aidl生成的文件中就出现过
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
//remote返回的就是mRemote,也就是BpBinder对象
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
可以看到,addService作为BpManagerService的业务函数,把请求数据包打包成data后传给了BpBinder的transact函数。也就是把通信的工作交给了BpBinder。
业务层的功能就是将请求数据打包,交给通信层去处理。
通信层的工作
之前在初始化BpBinder的函数中我们没有发现BpBinder与binder驱动设备有任何的交互,但是在binder的体系架构图中客户端确实是通过ioctl和binder驱动进行了通信。再继续往下看:
BpBinder::transact
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
if (mAlive) {
//把工作交给了IPCTHreadState
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
BpBinder调用transact,但是真正执行的IPCTHreadState,BpBinder相当于一个工具,让别人去工作,啊哈哈哈。。。
IPCTHreadState::self()
看样子是个通信线程?
IPCThreadState* IPCThreadState::self()
{
//第一次进来为false
if (gHaveTLS) {
restart:
const pthread_key_t k = gTLS;
//获取TLS空间中的内容
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
//new一个对象 构造函数中会调用pthread_serspecific
return new IPCThreadState;
}
if (gShutdown) return NULL;
pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS) {
//创建线程的TLS
if (pthread_key_create(&gTLS, threadDestructor) != 0) {
pthread_mutex_unlock(&gTLSMutex);
return NULL;
}
gHaveTLS = true;
}
pthread_mutex_unlock(&gTLSMutex);
goto restart;
}
这个方法中:
- gHavaTLS == true:使用pthread_getspecific获取IPCTHreadState对象,并返回这个对象
- gHaveTLS == false:便会创建线程的TLS
TLS是Thread Local Storge(线程本地存储空间)的简称。学过操作系统可知,这种空间每个线程都有,而且线程不共享这些空间。通过pthread_setspecific/pthread_getspecific函数可以设置/获取这些空间中的内容。**从线程本地存储空间中获得保存在其中的IPCTHreadState对象。
之前在操作系统课上还做过创建线程的实验,pthread_create()感觉很有意思,要好好学操作系统呀!
IPCTHreadState::IPCTHreadState()
从上面的情况来看,我们是很有必要了解IPCTHreadState的,下面看下它的构造函数
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mMyThreadId(gettid()),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
//在构造函数中,把自己保存到了线程本地存储中
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
可以看到:
- 成员变量mProcess保存了ProcessState变量(每个进程只有一个)
- 每个线程都有一个IPCTHreadState
- 每个IPCTHreadState都有一个mIn,mOut
- mIn:接收来自Binder设备的数据
- mOut:存储发往Binder设备的数据
IPCTHreadState::transact
transact这个方法是用来提交事务的,在学习binder通信的时候,学着写aidl文件对应的binder通信模板代码中就有,看看它在native层是怎么一会事。
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck(); //数据错误检查
flags |= TF_ACCEPT_FDS;
....
if (err == NO_ERROR) { // 传输数据
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
...
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
//等待响应
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
} else {
//oneway,则不需要等待reply的场景
err = waitForResponse(NULL, NULL);
}
return err;
}
transact事务处理三大流程:
- errorCheck() 数据错误检查
- writeTransactionData() 传输数据
其中的参数BC_TRANSACTION,是应用程序向binder设备发送消息的消息码。binder设备向应用程序恢复消息的消息码以BR_开头。
- waitForResponse()
IPCTHreadState::writeTransactionData()
作为通信,最重要的就是传输数据的这一方法
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
//是和binder设备通信的数据结构
binder_transaction_data tr;
tr.target.ptr = 0;
tr.target.handle = handle; // handle = 0
tr.code = code; // code = ADD_SERVICE_TRANSACTION
tr.flags = binderFlags; // binderFlags = 0
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;
// data为记录Media服务信息的Parcel对象
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize(); // mDataSize
tr.data.ptr.buffer = data.ipcData(); //mData
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t); //mObjectsSize
tr.data.ptr.offsets = data.ipcObjects(); //mObjects
} else if (statusBuffer) {
...
} else {
return (mLastError = err);
}
mOut.writeInt32(cmd); //cmd = BC_TRANSACTION
mOut.write(&tr, sizeof(tr)); //写入binder_transaction_data数据
return NO_ERROR;
}
这个方法中主要就是为binder_transaction_data这个数据结构中的内容赋值,并将BC_TRANSACTIN命令和这个数据结构写入到mOut中。
- handle值用来标识目的端,注册服务的目的端就是ServiceManager。在创建ServiceManager的方法binder_become_context_manager方法一步步往下调用的时候,有一个方法binder_ioctl_set_ctx_mgr方法创建了ServiceManager实体。这个实体是binder_context_mgr_node。
- Parcel data中的重要成员变量:
- mDataSize:binder_transaction_data的数据大小
- mData:binder_transaction_data数据的起始地址
- mObjectsSize:记录flat_binder_object结构体个数
- mObjects:记录flat_binder_object结构体的数据偏移量
ICPTHreadState::waitForResponse
上面的方法是把addService的请求信息写到mOut中,下面是发送请求的部分实现:
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
...
if (mIn.dataAvail() == 0) continue;
cmd = mIn.readInt32();
switch (cmd) {
case BR_TRANSACTION_COMPLETE: ...
case BR_DEAD_REPLY: ...
case BR_FAILED_REPLY: ...
case BR_ACQUIRE_RESULT: ...
case BR_REPLY: ...
goto finish;
default:
err = executeCommand(cmd); //【见小节3.x】
if (err != NO_ERROR) goto finish;
break;
}
}
...
return err;
}
这个方法主要就是在发送请求,可以看到可根据命令去执行相应的内容。这里的发送请求有没有和binder设备进行交互呢?有一个方法talkWithDriver()意面意思就是和driver通信,还有一个方法executeCommand()。下面来看看这个方法。
ICPTHreadState::talkWithDivier
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
...
binder_write_read bwr;
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
if (doReceive && needRead) {
//接收数据缓冲区信息的填充。如果以后收到数据,就直接填在mIn中了。
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
//当读缓冲和写缓冲都为空,则直接返回
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
//通过ioctl不停的读写操作,跟Binder Driver进行通信
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
...
} while (err == -EINTR); //当被中断,则继续执行
...
return err;
}
看到这个方法我发现了三个很重要的信息,能够帮助我们梳理和divier交互的中间过程:
- binder_write_read
这是用来和binder设备交换数据的结构。这个结构在看binder驱动底层读写数据的时候就出现过,当时以为这个结构是属于内核空间的。在这里发现这个数据结构中有两块区域,一个是read_buffer,一个是write_buffer。BpBinder将从客户端拿来的数据写到这里。
- ioctl()
调用这个方法进行读写操作,和binder driver通信。
总结
这里注册服务主要分析的MediaService的服务注册,主要流程是:
- 首先初始化ProcessState对象和BpServiceManager对象。同时也会获得通信的BpBinder。
- 初始化MediaServicePlayer对象,并将这个服务通过BpServiceManager的业务层函数addService将要注册的服务加入进去。
- 此后BpServiceManager将请求的数据交给BpBinder处理。BpBinder调用transact方法,实则是交给了IPCTHreadState处理,也就是创建出了线程,线程也会创建自己本地存储空间去完成Parcel数据包mOut和mIn的数据交换工作。
- IPCTHreadState调用transact方法处理事务。将数据存入binder_transaction_data结构中,写入到mOut中。
- 调用talkWidthDriver()方法,和Binder Driver通信,用binder_write_read结构体和binder设备交换数据。
Binder Driver中的ServiceManager
从上面可以看到talkWithDriver开始和driver开始通信,调用了ioctl方法。
ioctl-->binder ioctl-->binder_ioctl_write_read
binder_ioctl_write_read
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
struct binder_proc *proc = filp->private_data;
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
//将用户空间bwr结构体拷贝到内核空间
copy_from_user(&bwr, ubuf, sizeof(bwr));
...
if (bwr.write_size > 0) {
//将数据放入目标进程
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
...
}
if (bwr.read_size > 0) {
//读取自己队列的数据
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait);
...
}
//将内核空间bwr结构体拷贝到用户空间
copy_to_user(ubuf, &bwr, sizeof(bwr));
...
}
这个方法很经典,在注释中已经把它分为了三大块。
- copy_from_user
将binder_write_read结构体拷贝到用户空间,这里面包含的是用户地址空间的数据,在我们最开始分析的时候就可以对接起来了。
- 两个if
write_size中存在数据就写入目标进程;read_size存在数据就从队列中读取进程。
- copy_to_user
若处理失败则需要将数据还原,在重新写回到用户地址空间。
binder_thread_write
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
uint32_t cmd;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
while (ptr < end && thread->return_error == BR_OK) {
//拷贝用户空间的cmd命令,此时为BC_TRANSACTION
if (get_user(cmd, (uint32_t __user *)ptr)) -EFAULT;
ptr += sizeof(uint32_t);
switch (cmd) {
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
//拷贝用户空间的binder_transaction_data
if (copy_from_user(&tr, ptr, sizeof(tr))) return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
break;
}
...
}
*consumed = ptr - buffer;
}
return 0;
}
可以看到BC_TRANSACT命令和BC_REPLY命令执行的方法是一样的。调用binder_transaction方法。
binder_transaction
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply){
struct binder_transaction *t;
struct binder_work *tcomplete;
...
if (reply) {
...
}else {
if (tr->target.handle) {
...
} else {
// handle=0则找到servicemanager实体
target_node = binder_context_mgr_node;
}
//target_proc为servicemanager进程
target_proc = target_node->proc;
}
if (target_thread) {
...
} else {
//找到servicemanager进程的todo队列
target_list = &target_proc->todo;
target_wait = &target_proc->wait;
}
t = kzalloc(sizeof(*t), GFP_KERNEL);
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
//非oneway的通信方式,把当前thread保存到transaction的from字段
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
else
t->from = NULL;
t->sender_euid = task_euid(proc->tsk);
t->to_proc = target_proc; //此次通信目标进程为servicemanager进程
t->to_thread = target_thread;
t->code = tr->code; //此次通信code = ADD_SERVICE_TRANSACTION
t->flags = tr->flags; // 此次通信flags = 0
t->priority = task_nice(current);
//从servicemanager进程中分配buffer
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
t->buffer->allow_user_free = 0;
t->buffer->transaction = t;
t->buffer->target_node = target_node;
if (target_node)
binder_inc_node(target_node, 1, 0, NULL); //引用计数加1
offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
//分别拷贝用户空间的binder_transaction_data中ptr.buffer和ptr.offsets到内核
copy_from_user(t->buffer->data,
(const void __user *)(uintptr_t)tr->data.ptr.buffer, tr->data_size);
copy_from_user(offp,
(const void __user *)(uintptr_t)tr->data.ptr.offsets, tr->offsets_size);
off_end = (void *)offp + tr->offsets_size;
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
off_min = *offp + sizeof(struct flat_binder_object);
switch (fp->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
struct binder_node *node = binder_get_node(proc, fp->binder);
if (node == NULL) {
//服务所在进程 创建binder_node实体【见4.3.2】
node = binder_new_node(proc, fp->binder, fp->cookie);
...
}
//servicemanager进程binder_ref
ref = binder_get_ref_for_node(target_proc, node);
...
//调整type为HANDLE类型
if (fp->type == BINDER_TYPE_BINDER)
fp->type = BINDER_TYPE_HANDLE;
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
fp->handle = ref->desc; //设置handle值
fp->cookie = 0;
binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
&thread->todo);
} break;
case :...
}
if (reply) {
..
} else if (!(t->flags & TF_ONE_WAY)) {
//BC_TRANSACTION 且 非oneway,则设置事务栈信息
t->need_reply = 1;
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
} else {
...
}
//将BINDER_WORK_TRANSACTION添加到目标队列,本次通信的目标队列为target_proc->todo
t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list);
//将BINDER_WORK_TRANSACTION_COMPLETE添加到当前线程的todo队列
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
list_add_tail(&tcomplete->entry, &thread->todo);
//唤醒等待队列,本次通信的目标队列为target_proc->wait
if (target_wait)
wake_up_interruptible(target_wait);
return;
}
这个方法主要就是在binder驱动中执行任务。它有两个参数我们应该都比较熟悉。
- binder_proc:这是在binder_open的时候创建的binder的一个数据类型,里面存放着binder的一些信息
- binder_transaction_data:这个在IPCthreadState的方法中见过,也是一种数据类型,存放了客户端传来的数据信息。
再梳理下这个方法的主要流程:
- target_node = binder_context_mgr_data找到ServiceManager实体
- 设置ServiceManager进程:target_proc
- 找到ServiceManager进程的todo队列和等待队列
- 为binder_transaction结构赋值:
- t->from = thread(传进来的binder_thread)
- t->to_proc = target_proc(通信目标进程为ServiceManager进程)
- t->buffer = binder_alloc_buf()(ServiceManager进程中分配buffer)
- 拷贝用户空间binder_transaction_data中的buffer和offsets到内核
- 在服务所在进程创建binder_node实体,ServiceManager进程创建binder_ref
- 向ServiceManager的binder_proc->todo添加BINDER_WORK_TRANSACTION事务
这是binder底层驱动执行事务的一系列过程
do_add_service
看完底层servicemanager进程是如何与binder driver进行通信的,再看看注册服务时的方法。这个方法是在分析ServiceManager启动的时候见过的。binder_loop-->binder_parse-->svcmgr_handler。
int do_add_service(struct binder_state *bs,
const uint16_t *s, size_t len,
uint32_t handle, uid_t uid, int allow_isolated,
pid_t spid)
{
struct svcinfo *si;
if (!handle || (len == 0) || (len > 127))
return -1;
//权限检查
if (!svc_can_register(s, len, spid)) {
return -1;
}
//服务检索
si = find_svc(s, len);
if (si) {
if (si->handle) {
svcinfo_death(bs, si); //服务已注册时,释放相应的服务
}
si->handle = handle;
} else {
si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
if (!si) { //内存不足,无法分配足够内存
return -1;
}
si->handle = handle;
si->len = len;
memcpy(si->name, s, (len + 1) * sizeof(uint16_t)); //内存拷贝服务信息
si->name[len] = '\0';
si->death.func = (void*) svcinfo_death;
si->death.ptr = si;
si->allow_isolated = allow_isolated;
si->next = svclist; // svclist保存所有已注册的服务
svclist = si;
}
//以BC_ACQUIRE命令,handle为目标的信息,通过ioctl发送给binder驱动
binder_acquire(bs, handle);
//以BC_REQUEST_DEATH_NOTIFICATION命令的信息,通过ioctl发送给binder驱动,主要用于清理内存等收尾工作。
binder_link_to_death(bs, handle, &si->death);
return 0;
}
这里先做权限检查,再进行服务检索,如果服务的名称有一样的就释放源服务添加新服务。将服务保存到svclist中。服务注册成功会调用binder_send_reply方法通过binder_write向binder驱动通信。
总结
在看这注册服务的时候我有点懵,原因在于native层注册服务后又跑到了binder驱动层。其实这是一个连带的过程,主要就是要将ServiceManager所在进程和binder进程联系在一起。
这里在梳理下全部流程:
- MediaPlayerService进程调用ioctl向binder发送数据。该过程为一个事务binder_transaction,执行当前操作的线程为binder_thread(thread1)。IPC数据内容包括:
- binder协议BC_TRANSACTION
- handle为0
- RPC代码为ADD_SERVICE
- RPC数据为”nedia.player“
- binder驱动收到该请求,生成BR_TRANSACTION命令,选择目标处理该请求的线程,即ServiceManager的binder_thread(thread2)。将整个binder_transaction数据插入到目标线程的TODO队列
- thread2线程收到任务后,将服务“media player”注册到服务目录中。注册完成后,生成IPC应答数据BC_REPLY
- binder驱动收到该binder应答请求,生成BR_REPLY命令。在MediaPlayerService接收到命令后,知道服务注册完成就可以正常使用了。
获取服务
获取服务还是从MediaServicePlayer的获取服务方法来看。
getMediaPlayerService
sp&
IMediaDeathNotifier::getMediaPlayerService()
{
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
sp sm = defaultServiceManager(); //获取ServiceManager
sp binder;
do {
//获取名为"media.player"的服务
binder = sm->getService(String16("media.player"));
if (binder != 0) {
break;
}
usleep(500000); // 0.5s
} while (true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier(); //创建死亡通知对象
}
//将死亡通知连接到binder
binder->linkToDeath(sDeathNotifier);
sMediaPlayerService = interface_cast(binder);
}
return sMediaPlayerService;
}
这里在获取服务的过程中,仍采用不断循环的方法。
BpSM.getService
virtual sp getService(const String16& name) const
{
unsigned n;
//循环5次,大概和android ANR时间为5s相关
for (n = 0; n < 5; n++){
sp svc = checkService(name); //【见2.3】
if (svc != NULL) return svc;
sleep(1);
}
return NULL;
}
BpSM.checkService
virtual sp checkService( const String16& name) const
{
Parcel data, reply;
//写入RPC头
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
//写入服务名
data.writeString16(name);
remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
return reply.readStrongBinder();
}
检索指定服务是否存在
在这之后又调用了和请求服务一样的程序。
总结
获取服务过程,就是向ServiceManager进行查询指定服务,当执行binder_transaction()时,会区分获取服务所属进程的情况。
参考文章
Binder源码分析系列
写给 Android 应用工程师的 Binder 原理剖析