Android中的binder機制分析二:以精簡的語句來提煉binder

一、 個人心得:
網上很多講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不能直接通信:
AB進程都認爲自己在獨享3GB的內存空間
但是,大於等於虛擬地址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對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章