binder系统进程间的通信,其都是在驱动binder中实现的,也就是说,无论你的应用程序多么的复杂,最终还是通过ioctl这些标准的接口使用驱动程序,我们可以根据open,ioctl等等的调用过程分析C++程序的内部机制,和他的传输过程。
现在我们回顾一下应用程序是怎么和binder驱动打交道了,以test_server为例,他使用驱动程序的过程: 1.open:mmp 2.addservice:ioctl 3.while:read-data,parse-data,process data,reply:ioctl。
我们知道一个test_server进程可能是多线程的,如下图所示: 子线程是可以共享主线程的资源的,我们在主线程中使用open,然后代用mmap。基于c++面向对象的思想,即使调用mmap,其也是通过对象来完成的,打开源代码test_server.cpp,可以看到:
/* 打开驱动, mmap */
sp proc(ProcessState::self());
其通过ProcessState进行mmap,注意ProcessState为一个单列模式,每个进程只有一个ProcessState对象,这个对象怎么创建呢?后面可以知道,其是通过ProcessState::self创建唯一的对象。
上图中的圈都代表循环,主线程循环以及子线程循环都是通过ioctl读取或者发送数据,同理,基于面向对象的实现,其循环过程,也由一个对象进行处理IPCThreadState,其同样是单列模式,每一个线程对应唯一的一个IPCThreadState对象。在上图中存在3个线程,这共有3个IPCThreadState对象。其通过IPCThreadState::self得到对象。
到这里,我们发现了两个类,分别为ProcessState与IPCThreadState,只要我们对着两个类进行详细的分析,我们就能掌握C++在binder系统中的数据传递过程。首先我们来看看ProcessState。
ProcessState之前提到,open打开驱动的函数,是在ProcessState(ProcessState.cpp文件)执行的:
sp ProcessState::self()
gProcess = new ProcessState;
从上面我们可以知道,全局只有一个ProcessState对象。我们查看ProcessState的构造函数:
ProcessState::ProcessState()
: mDriverFD(open_driver())
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
......
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
在上面我们可以看到mDriverFD(open_driver()),其不用多想,open的操作肯定是在其内部了:
static int open_driver()
int fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
return fd;
可以知道最终fd赋值给了mDriverFD。从其中还能找到mmap函数。之前我们提到, 每个线程都存在一个IPCThreadState对象,其在调用ioctl时需要fd,可以知道IPCThreadState中肯定存在一个成员,其指向ProcessState中的mDriverFD。
现在我们查看IPCThreadState
IPCThreadState打开test_server.cpp:
/* 循环体 */
ProcessState::self()->startThreadPool();//创建一个子线程
IPCThreadState::self()->joinThreadPool();//主线程
我们先来看看IPCThreadState,他是如何为每个线程只创建一个对象(IPCThreadState.cpp):
IPCThreadState* IPCThreadState::self()
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
return new IPCThreadState;
int key_create_value = pthread_key_create(&gTLS, threadDestructor)
在这里插讲一个知识点,线程特有数据(Thread specific Data)。如下图,main函数执行: 然后创建了3个线程,对每个线程创建一个IPCThreadState对象,意思是说,每个对象的他们之间是不一样的。他们存在于线程的局部空间之中,那么我们怎么实现这一点呢?
在Linux之中,pthread_key_create(),其创建一个key,既然有key那么肯定存在val。那么每个线程的val都不相同,可以调用pthread_setspecific对这个key设置一个键值。后续可以通过pthread_getspecific获得键值。
现在回过头查看return new IPCThreadState,其构造函数如下:
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mMyThreadId(gettid()),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
/*给每个线程设置一个唯一的键值,当后执行PCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
其键值不为空,则st不为空,会直接返回,不在执行new IPCThreadState创建新对象,这样就能保证每个线程只有一个IPCThreadState对象*/
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
常上面我们可以看到,其初始化列表中存在mProcess(ProcessState::self()),等价于mProcess=ProcessState::self()。通过前面我们知道每个ProcessState存在mDriverFD。下面我们回到test_server.cpp:
IPCThreadState::self()->joinThreadPool();
其为一个循环,这个循环所做的事情我们已经非常熟悉了,无非就是通过ioctl读写数据,然后把处理结构返回给test_client应用程序:
void IPCThreadState::joinThreadPool(bool isMain)
do {
processPendingDerefs();
// now get the next command to be processed, waiting if necessary
result = getAndExecuteCommand();
if (result mDriverFD, result);
abort();
}
// Let this thread exit the thread pool if it is no longer
// needed and it is not the main process thread.
if(result == TIMED_OUT && !isMain) {
break;
}
} while (result != -ECONNREFUSED && result != -EBADF);
可以知道其中存在dowhile循环,首先分析其中的getAndExecuteCommand。
status_t IPCThreadState::getAndExecuteCommand()
/*接收数据*/
result = talkWithDriver();
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
/*处理数据*/
result = executeCommand(cmd);
到这里IPCThreadState::self()->joinThreadPool();//主线程基本分析完成,下面我么来看看:
ProcessState::self()->startThreadPool();//创建一个子线程
进入startThreadPool函数:
void ProcessState::startThreadPool()
spawnPooledThread(true);
/*sp t = new PoolThread(isMain)派生于Thread,可以知道其为一个线程*/
sp t = new PoolThread(isMain);
/*其相当于执行PoolThread中的threadLoop函数*/
t->run(name.string());
上面提过t->run(name.string())相当于执行PoolThread中的threadLoop函数,那么我们看看threadLoop:
virtual bool threadLoop()
IPCThreadState::self()->joinThreadPool(mIsMain);
可以看到执行和主线程一样的代码,但是这句代码,是在子线程之中执行的,然后进入一个循环。即子线程之中,存在两个循环。
下面我们分析addService的过程,以及IPCThreadState::self()->joinThreadPool()接收到数据之后是如何处理的。
那么我们以下共3个问题需要解决 1.addService是如何添加服务的 2.server如何分辨client想使用那一个服务 3.怎么调用HelloService所提供的函数。
1.addService是如何添加服务的: 对于不同的服务,构造flat_binder_object结构体,里面的.binder/.cookie对应的服务不一样,其值也不一样。在IServiceManager.cpp文件中:
virtual status_t addService(const String16& name, const sp& service,bool allowIsolated)
/*service = sp& service*/
data.writeStrongBinder(service);
/*这里val = service = new BnhelloService*/
return flatten_binder(ProcessState::self(), val, this);
flat_binder_object obj;
/*local =this = new BnhelloService*/
IBinder *local = binder->localBinder();
obj.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast(local->getWeakRefs());
/*new BnhelloService*/
obj.cookie = reinterpret_cast(local);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
2.server如何分辨client想使用那一个服务 server收到数据里含有flat_binder_object结构体,他可以根据.binder/.cookie分辨client想使用哪一个服务。把.cookie转换为Bnxxx对象,然后调用他的函数,下面我们跟踪一下源码(在IPCThreadState循环中,接收到数据之后会执行):
status_t IPCThreadState::getAndExecuteCommand()
result = executeCommand(cmd);
case BR_TRANSACTION:
/*根据cookie构造了一个BBinder对象指针,调用其中的transact函数*/
error = reinterpret_cast(tr.cookie)->transact(tr.code, buffer,&reply, tr.flags);
其上的transact实现如下(Binder.cpp):
status_t BBinder::transact(
/*其会根据code值,调用不同的函数。*/
err = onTransact(code, data, reply, flags);