一、 个人心得:
网上很多讲binder的书籍和博客,喜欢一上来就从智能指针开始,我个人觉得,对于binder机制的研究,没有必要去深入钻研智能指针,把binder中所有的sp和wp都当成普通的指针就行了,其次,另一个就是一上来开始分析源码, 然后大家就会被各种名词给混淆:
BnService, BpService, BBinder, Service Manager, binder driver, ProcessState,IPCThreadState等等,这些概念再混入到各种文件例如
IxxxService.h,IxxxService.cpp,总之,我刚开始是被完全晕了。。。
所以,我觉得一个不错的方式是先给出一个最简单的demo,至少,这个demo让大家知道,binder是基于C/S的通信架构,脑子里面有一个client端和service端的概念,那么去分析代码,会有一个大致的方向。
binder相关源码路径(Android4.4):
frameworks\native\include\binder
frameworks\native\libs\binder\
二、 用精简的话总结binder:
1. binder机制的核心是基于mmap的binder driver的运转;
这里的binder driver指的是Linux kernel中的binder驱动,说这个之前会抛出一个用户态和内核态的概念,理论上,不管是native层运行的程序也好,java层运行的程序也罢,都是在用户态运行的,以32位系统为例,每个程序(进程)都认为自己独享了3个G的用户空间,所以,两个进程A和B不能直接通信:
但是,大于等于虚拟地址0XC000000之后的虚拟地址对于每个进程而言,都是一样的,唯一的缺陷就是用户态的程序不能直接访问这段内核地址,必须要以特定的方式,最常用的就是copy_from_user和copy_to_user。
而binder的实现,则是直接在Linux kernel中虚拟成了一个设备,借助操作驱动文件的方式操作binder,并且,binder没有完全使用copy_from_user和copy_to_user这种效率不高的字符操作函数,而是直接使用了更高效的mmap来实现用户态与内核态的互访;
好吧,说了这么说,好像又绕又废话,总之,就是不管我们上面java层,native层怎么折腾,怎么封装,最终,都是在binder driver中实现的;
2. Android系统只有一个service manager,并且,它是所有service的大管家;
天地浑沌如鸡子。盘古生在其中。万八千岁。天地开辟。阳清为天。阴浊为地。
好像扯得有点远,插入这句话是想说我们的service manager(SM)如同盘古一样在天地初始(系统启动)的时候,用一把斧头开天辟地(第一个打开binder驱动),世界才拥有了千秋万物(Android才拥有了各种service)。
虽然盘古是虚构的,可是我们的SM可是有实体的哦(一个可执行文件),就在这里:
frameworks\native\cmds\servicemanager\service_manager.c(Android4.4)
frameworks\native\cmds\servicemanager\binder.c
我们来具体分析一下这个代码。
int main(int argc, char **argv)
{
struct binder_state *bs;
/* svcmgr就是一个handle,值为0 */
void *svcmgr = BINDER_SERVICE_MANAGER;
/* 1.打开binder驱动 */
bs = binder_open(128*1024);
/* 2.把自己变成大管家,统领所有service */
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
/* 3.开启循环,开始管事了 */
binder_loop(bs, svcmgr_handler);
return 0;
}
main函数中首先调用binder_open去驱动打开binder:
struct binder_state *binder_open(unsigned mapsize)
{
****省略部分代码****
bs->fd = open("/dev/binder", O_RDWR);
if (bs->fd < 0) {
fprintf(stderr,"binder: cannot open device (%s)\n",
strerror(errno));
goto fail_open;
}
bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
****省略部分代码****
}
可以看到这个函数其实就干了两件事,一个是打开设备文件,另一个是mmap出一段可以直接在用户态能够操作的内核态内存,大小是128k;
回到main函数 :
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
因为是按照驱动模式操作binder,自然ioctl是必不可少的了,这里直接告诉驱动把此进程设置成大管家;
最后看看大管家的循环:
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
unsigned readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(unsigned));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (unsigned) readbuf;
/* 不停地调用驱动中的ioctl */
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
/* 解析指令 */
res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
这个loop循环就很亲民化了,binder_write_read 是与驱动中binder通信的数据格式,不停地驱动ioctl看是否有指令,如果有的话就去解析;
好了,Android中的“盘古”SM代码就分析完了,它的使用自然是要与启动脚本init.rc联系在一起;
out\target\product\xxx\root
这里面有一段如下代码:
service servicemanager /system/bin/servicemanager
class core
user system
group system
3. client端和service端各有一个唯一ProcessState对象来协助BpxxxService和BnxxxService;
我们从之前的demo大概明白了这些:BpxxxService是client的代理服务,BnxxxService连接服务端与binder驱动的管道,程序实际中,client并不知道有这个叫BpxxxService的东西存在,以为自己是直接调用BnxxxService来获取所需的,那么,这个BpxxxService是怎么出现的呢?
我们先来看service端的main函数:
int main(int argc, char** argv)
{
/* 1. 打开binder driver并且进行内核地址的mmap */
sp<ProcessState> proc(ProcessState::self());
/* 2.获取Android的SM并将我们的服务添加进去 */
sp<IServiceManager> sm = defaultServiceManager();
sm->addService(String16(MyService::getServiceName()), new MyService());
/* 3.启动子线程,一般的server都不需要再启动这样一个子线程来协助主线程工作 */
ProcessState::self()->startThreadPool();
/* 4.loop循环,从binder driver中获取指令,并由Bn的transact转为onTransact
* 最终回调到Bn端,进而执行xxxService里面的代码 */
IPCThreadState::self()->joinThreadPool();
return 0;
}
分析第一步,看一下ProcessState::self():
sp<ProcessState> ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) {
return gProcess;
}
gProcess = new ProcessState;
return gProcess;
}
这里面是使用的单例设计模式,gProcess是一个全局变量,所以每个进程调用此函数只会有一个ProcessState对象,继续看下ProcessState构造函数:
ProcessState::ProcessState()
: mDriverFD(open_driver())
, mVMStart(MAP_FAILED)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
if (mDriverFD >= 0) {
......
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
......
}
截取关键代码如上,参数列表中mDriverFD是binder驱动的文件描述符,是通过
open_driver()来获取的,这个函数是不是很熟悉,跟SM应用程序里面打开binder操作如出一辙,跟进这个函数:
static int open_driver()
{
int fd = open("/dev/binder", O_RDWR);
if (fd >= 0) {
fcntl(fd, F_SETFD, FD_CLOEXEC);
int vers;
status_t result = ioctl(fd, BINDER_VERSION, &vers);
if (result == -1) {
ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
close(fd);
fd = -1;
}
if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
ALOGE("Binder driver protocol does not match user space protocol!");
close(fd);
fd = -1;
}
size_t maxThreads = 15;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
if (result == -1) {
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
} else {
ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
}
return fd;
}
很简单,打开binder驱动,调用ioctl查询binder版本和设置最大子线程数(15);
继续回到构造函数,接下来就是调用mmap让系统在内核里面找一段内存,能让用户态直接访问的,内存大小为
/* 1M-8k */
#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
返回的即这段内存的始地址;
第二步,调用defaultServiceManager获取SM,并添加我们的服务:
sp<IServiceManager> defaultServiceManager()
{
if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
{
AutoMutex _l(gDefaultServiceManagerLock);
while (gDefaultServiceManager == NULL) {
gDefaultServiceManager = interface_cast<IServiceManager>(
ProcessState::self()->getContextObject(NULL));
if (gDefaultServiceManager == NULL)
sleep(1);
}
}
return gDefaultServiceManager;
}
这个也是单例设计模式,我们看一下interface_cast是什么:
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
这是一个模板类,返回的是上面的一个语句,那么这个语句跟我们的binder有什么关系?答案在供client端调用的IMyService.h和IMyService.cpp的两个关键宏上面:
IMyService.h有这个语句:
/* 固定写法:此处必须继承自IInterface类 */
class IMyService : public IInterface
{
public:
DECLARE_META_INTERFACE(MyService);
virtual int setNum(int a) = 0;
virtual int getNum() = 0;
};
DECLARE_META_INTERFACE是什么:
#define DECLARE_META_INTERFACE(INTERFACE) \
static const android::String16 descriptor; \
static android::sp<I##INTERFACE> asInterface( \
const android::sp<android::IBinder>& obj); \
virtual const android::String16& getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##INTERFACE();
看得眼花,把参数带进来替换:
/* 定义descriptor */
static const android::String16 descriptor;
/* 声明一个asInterface方法,很重要,预警 */
static android::sp<IMyService> asInterface(
const android::sp<android::IBinder>& obj);
/* 就是一个getter方法,没啥好说的 */
virtual const android::String16& getInterfaceDescriptor() const;
/* 跟进我们传进来的参数,自动帮我们声明构造与析构,很贴心嘛 */
IMYSERVICE();
virtual ~IMYSERVICE();
c/c++中“##”表示宏参数连接,上述代码带进来,实际上就三个事:
a.descriptor()和getInterfaceDescriptor()是一对的,获取描述符;
b.声明构造和析构;
c.声明了一个asInterface函数;
看来,在这个头文件中声明这个宏很方便啊,一句话帮我们完善了很多内容~
继续看IMyService.cpp中下面这句话:
/* 接口:这里面会去new BpxxxSerivce*/
IMPLEMENT_META_INTERFACE(MyService, "jztech.binder.IMyService");
跟踪一下,还是在IInterface.h中,这个略微复杂,但是跟头文件中的声明是对应的,这里是实现:
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
const android::String16 I##INTERFACE::descriptor(NAME); \
const android::String16& \
I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
} \
android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
const android::sp<android::IBinder>& obj) \
{ \
android::sp<I##INTERFACE> intr; \
if (obj != NULL) { \
intr = static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
I##INTERFACE::descriptor).get()); \
if (intr == NULL) { \
intr = new Bp##INTERFACE(obj); \
} \
} \
return intr; \
} \
I##INTERFACE::I##INTERFACE() { } \
I##INTERFACE::~I##INTERFACE() { } \
依旧看的头疼,但是还是要带入参数
/* 这两个函数是头文件中实现 */
const android::String16
IMYSERVICE::descriptor("jztech.binder.IMyService"); \
const android::String16&
IMYSERVICE::getInterfaceDescriptor()const {
return IMYSERVICE::descriptor;
}
/* asInterface的实现 */
android::sp<IMYSERVICE> IMYSERVICE::asInterface(
const android::sp<android::IBinder>& obj)
{ \
android::sp<IMYSERVICE> intr; \
if (obj != NULL) { \
intr = static_cast<IMYSERVICE*>( \
obj->queryLocalInterface( \
IMYSERVICE::descriptor).get()); \
if (intr == NULL) {
/* 实例化了BpxxxService */
intr = new BpMYSERVICE(obj); \
} \
} \
return intr; \
} \
/* 构造和析构什么事都不用做 */
IMYSERVICE::IMYSERVICE() { } \
IMYSERVICE::~IMYSERVICE() { }
我们重点关注的就是asInterface的实现,天啊,在这里面居然实例化了BpxxxService,再回想一下,IxxxService是client端直接调用的,简直就是在眼皮子底下实例化的,难怪说对client端,Bp是透明的,这两个宏真的是好一招瞒天过海。
我们往上回两级捋捋,现在我们在分析service端代码,然后分析到了一个return语句,返回的是asInterface函数,通过分析宏定义,我们知道了核心是创建了用于client的代码Bp,嗯…似乎有什么不对?等一下,我们现在不是在说service端吗?怎么莫名其妙地变成了client端,还去分析Bp啥时候创建的?其实,你如能这么想那就对了,这就是binder的C/S美妙架构,我们现在想往SM申请添加一个服务端,对于SM而言,我们不正是一个client?所以,从整个Android系统来看,就是SM,service,client三者之间的剪不清,理还乱的关系;
还有点东西需要提一下,到这里,我们获得的,就是SM的binder实例了,看一下返回类型:
sp<IServiceManager> sm = defaultServiceManager();
这个IServiceManager模式,似乎很熟悉,但是又说不上来,其实,他就是SM的接口,等价于我们这个MyService的接口,你点进去看IServiceManager类,有四个纯虚函数:
virtual sp<IBinder> getService( const String16& name) const = 0;
virtual sp<IBinder> checkService( const String16& name) const = 0;
virtual status_t addService( const String16& name,
const sp<IBinder>& service,
bool allowIsolated = false) = 0;
virtual Vector<String16> listServices() = 0;
我们的demo就只有两个纯虚函数嘛,所以,我想说,SM本身就是一个标准的binder例子,这也是为什么很多书籍和博客喜欢以SM来入手讲解binder的原因;
回到main函数的第三步,这里会去创建一个子线程,用于协助主线程来处理该进程注册的所有服务,这条语句可以省略,其本身也是继承自Thread类,就不详细分析了;
继续分析第四步:
/* 4.loop循环,从binder driver中获取指令,并由Bn的transact转为onTransact
* 最终回调到Bn端,进而执行xxxService里面的代码 */
IPCThreadState::self()->joinThreadPool();
跟踪IPCThreadState::self()
IPCThreadState* IPCThreadState::self()
{
if (gHaveTLS) {
restart:
const pthread_key_t k = gTLS;
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
/* 重点关注这里 */
return new IPCThreadState;
}
if (gShutdown) return NULL;
pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS) {
if (pthread_key_create(&gTLS, threadDestructor) != 0) {
pthread_mutex_unlock(&gTLSMutex);
return NULL;
}
gHaveTLS = true;
}
pthread_mutex_unlock(&gTLSMutex);
goto restart;
}
这里面重点关注实例化IPCThreadState,先看一下这个构造:
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mMyThreadId(androidGetTid()),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
前面已经实例化过ProcessState,所以,这里直接就能获取,mIn和mOut是用于打包Parcel数据的,这个Parcel之前似乎没有提过,是binder通信中一种封装好的用于用户态和binder驱动交互的数据格式;
构造比较简单,我们再去看一下引用的函数joinThreadPool:
void IPCThreadState::joinThreadPool(bool isMain)
{
......
do {
......
/* 查询client端是否有指令 */
result = getAndExecuteCommand();
......
}
} while (result != -ECONNREFUSED && result != -EBADF);
......
}
整个函数被我精简了,只需要关注这个点:
既然service端要不停查询client端给过来的指令,那么,线程中的while循环自然是少不了了,分析一下getAndExecuteCommand:
status_t IPCThreadState::getAndExecuteCommand()
{
status_t result;
int32_t cmd;
/* 1. 与binder驱动交互之前的准备 */
result = talkWithDriver();
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) return result;
cmd = mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing top-level Command: "
<< getReturnString(cmd) << endl;
}
/* 2.执行cmd */
result = executeCommand(cmd);
......
}
return result;
}
talkWithDriver这个函数名取得有点名不副实,并没有与binder driver进行实质性的操作,那么executeCommand呢?
.......
case BR_TRANSACTION:
.......
else {
error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
if (error < NO_ERROR) reply.setError(error);
}
......
我们在进行client端往service端调用的时候,执行的cmd是BR_TRANSACTION,这里有一个the_context_object,看一下出处:
sp<BBinder> the_context_object;
void setTheContextObject(sp<BBinder> obj)
{
the_context_object = obj;
}
它其实就是BBinder,这个BBinder又是什么鬼?
我们需要捋一下继承关系:
xxxService->BnxxxService->BnInterface->BBinder
前面我们已经分析了,service端目前已经把MyService的服务添加到SM中了,与此同时,service端的线程也不停转起来了,并且从driver驱动里面拿到了client发过来的消息,所以,我们再看一下BBinder中transact干了什么:
status_t BBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
data.setDataPosition(0);
status_t err = NO_ERROR;
switch (code) {
case PING_TRANSACTION:
reply->writeInt32(pingBinder());
break;
default:
err = onTransact(code, data, reply, flags);
break;
}
if (reply != NULL) {
reply->setDataPosition(0);
}
return err;
}
它其实去调用了onTransact,这个函数我们又眼熟了,这不就是我们在IMyService.cpp中里面实现的
status_t BnMyService::onTransact (uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
int ret = -1;
switch (code) {
case SET_NUM:
{
int num = -1;
ALOGD("BnMyService::onTransact SET_NUM ");
num = data.readInt32();
ret = setNum(num);
reply->writeInt32(ret);
return NO_ERROR;
}
case GET_NUM:
{
ALOGD("BnMyService::onTransact GET_NUM ");
ret = getNum();
reply->writeInt32(ret);
return NO_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
这里就调用的MyService.cpp中的具体函数实现了;
总结一下binder的service端:
1.创建service进程唯一的ProcessState;
2. 以自己为client端,获取SM的binder实例,添加服务;
3. 创建一个IPCThreadState实例,进入线程循环;
4. 假定收到client端消息,由继承关系调用Bn的onTransact函数,并去调用xxxService中的函数实现,将结果写入binder驱动中返回给client;
client代码也是通过调用defaultServiceManager从而去创建client进程唯一的ProcessState实例,所以service端和client端都只有一个唯一的ProcessState对象。