一、 個人心得:
網上很多講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對象。