Binder通信和啓動流程詳解

所有源碼均基於android M

本篇從情景化角度出發分析Binder的通信邏輯。

其中會涉及到幾個概念
· media server - 系統多媒體服務
· media player server - 多媒體服務下的視頻服務
· Binder驅動

先總結一下本篇所會涉及到的概念和他們之間的關係。

media server 是安卓系統中用來管理各個多媒體服務的“服務的服務類”。media server管理着多個服務的啓動和加載,包括 AudioFlinger,Camera,MediaPlayerService,等等。

這些服務都需要通過media server註冊到系統的ServiceManager中。

ServiceManager是android系統用來管理服務的管理器。在android裏這些服務是以C/S架構存在的。所有的客戶端想是用服務端功能,都需要先向系統的ServiceManager申請調用這些服務,而不能夠直接使用服務的實際對象。

在C端使用S端的服務的過程中,C端所持有的是一個BinderProxy對象,它用BinderProxy,通過Binder驅動向S端發起調用,S端再通過Binder驅動把處理結果返回給C端。S端也持有一個Binder對象,和C端不同的是它不是proxy這種代理對象,它在Java層是我們寫AIDL時候經常見到的Stub,在C++或者JNI層,是BBinder的派生類。

我們先從Binder驅動的啓動開始。

Binder驅動的啓動

android系統本質上也是個Linux系統,它的啓動也依據 init.rc 的配置。在init.rc中,啓動binder驅動的部分在servicemanager,

service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

servicemanger是系統側的進程,我們看看它代碼的main入口。
Mashmallow/frameworks/native/cmds/servicemanager/service_manager.c

int main(int argc, char **argv)
{
    struct binder_state *bs;

    bs = binder_open(128*1024); // 打開binder驅動
    if (!bs) {
        ALOGE("failed to open binder driver\n");
        return -1;
    }

    if (binder_become_context_manager(bs)) { //設置fd爲當前binder上下文
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    selinux_enabled = is_selinux_enabled();
    sehandle = selinux_android_service_context_handle();
    selinux_status_open(true);

    if (selinux_enabled > 0) {
        if (sehandle == NULL) {
            ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
            abort();
        }

        if (getcon(&service_manager_context) != 0) {
            ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
            abort();
        }
    }

    union selinux_callback cb;
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    cb.func_log = selinux_log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

    binder_loop(bs, svcmgr_handler); //開始事件循環,svcmgr_handler很重要,是處理事件的主要入口

    return 0;
}

binder驅動的啓動流程比較簡單。首先它調用binder_open()去打開binder節點,我們知道在linux中一切皆是文件。
Mashmallow/frameworks/native/cmds/servicemanager/binder.c

struct binder_state *binder_open(size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs)); 
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }

    bs->fd = open("/dev/binder", O_RDWR);//打開binder驅動
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }

    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        fprintf(stderr,
                "binder: kernel driver version (%d) differs from user space version (%d)\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }

    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);//設定映射空間大小
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}

/dev/binder是所有binder設備的節點。binder框架的內存拷貝我們都知道只有一次,它是使用mmap在binder設備驅動上開闢了一個內存映射空間用來做內存拷貝的。因爲省去了 用戶空間-內核空間 的拷貝流程,所以本質上它能夠做到比傳統的IPC少一次內存拷貝。

但這裏有個細節需要注意,這裏的內存映射空間大小實際只有 128x 1024 字節,並不是我們所熟悉的1M。因爲這是在系統側的binder通信,這個內存空間實際上只有ServiceManager在使用,所以設計上只需要128K。

看到binder_loop之後就應該猜的到,這裏是個事件循環機制,在一個for裏面不停地等待事件的發生和處理。

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;
    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); //通過ioctl等待事件

        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }

        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); //解析數據結構並且調用 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()的時候第二個參數 func是一個函數指針,它所指向的是service_manager中的函數svcmgr_handler()。
loop的邏輯比較簡單,就是一般的事件驅動邏輯。

到這裏binder驅動就啓動完畢,並且進入循環模式不停地等待和處理從C端傳過來的數據了。總的來說binder驅動做的事情包括
· 添加服務
· 查詢服務
· 獲取服務
這三個是主要功能,具體代碼邏輯在 service_manager 的 svcmgr_handler裏,也就是上面說的binder_loop()的第二個參數,

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    ...
}

bingder驅動啓動完畢後,就等待C端的ServiceManager和它通信了。到這裏還只是bidner框架的其中一個部分,得結合C端的邏輯才能明白整個binder的工作原理

MediaService的啓動

這裏說的 media service 是提供多媒體服務的S端,它裏面使用了ServiceManager的C端。在init.rc中,SM的C端的啓動比S端稍微慢一點。並且它是在mediaserver進程裏啓動的。

service media /system/bin/mediaserver
    class main
    user media
    group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
    ioprio rt 4

media sever是我們即將要啓動的多媒體服務,它的主入口在
Mashmallow/frameworks/av/media/mediaserver/main_mediaserver.cpp

int main(int argc __unused, char** argv)
{
    ...
    if (doLog && (childPid = fork()) != 0) { //從主進程fork出來子進程
        //主進程部分繼續做一些邏輯
    } else { //子進程部分
        // all other services
        if (doLog) {
            prctl(PR_SET_PDEATHSIG, SIGKILL);   // if parent media.log dies before me, kill me also
            setpgid(0, 0);                      // but if I die first, don't kill my parent
        }
        InitializeIcuOrDie();
        sp<ProcessState> proc(ProcessState::self()); //在本地進程中打開binder驅動
        sp<IServiceManager> sm = defaultServiceManager(); //啓動C端的SM
        ALOGI("ServiceManager: %p", sm.get());
        AudioFlinger::instantiate();
        MediaPlayerService::instantiate(); //啓動MediaPlayerService,添加到S端的SM中
        ResourceManagerService::instantiate();
        CameraService::instantiate();
        AudioPolicyService::instantiate();
        SoundTriggerHwService::instantiate();
        RadioService::instantiate();
        registerExtensions();
        ProcessState::self()->startThreadPool();//開始C端的事件循環
        IPCThreadState::self()->joinThreadPool();
    }
}

這個文件的代碼很簡單隻有一百多行。我們刪掉與fork出來的子進程邏輯無關的代碼之後就剩下這些。

這裏涉及到三個東西,ProcessState,ServiceManager,MediaPlayerService。

MediaPlayerService其實只是系統提供的多媒體服務的其中一個,類似的還有Camera,SurfaceFlinger等。還記得我們一開始說的,服務必須先添加到系統的SM裏作爲S端,C端再通過SM去獲取服務嗎。這裏的media player service就是一個S端的服務,並且對比其他服務來說,它的代碼和邏輯關係相對簡單,所以下面我們會用它來做例子。

需要先明確一個基本關係,既在這裏誰是C端誰是S端?

首先這裏有兩個東西,一個是ServiceManager,另外一個是MediaPlayerService。

_ServiceManager_是MediaPlayerService啓動過程中需要用到的C端SM,在這裏的任務是把服務添加到SM的S端中,它扮演的邏輯角色是SM的C端。而它的S端是在第一節裏我們分析的binder啓動那裏。

_MediaPlayService_在這裏扮演的角色是一個S端服務,它在等待SM的C端把它註冊到系統中。

明確這兩點後我們才能接着往下走,不然下面的很多邏輯都會想不明白。

ProcessState的啓動

ProcessState做了幾件事情,創建自己的實例,打開binder。
Mashmallow/frameworks/native/libs/binder/ProcessState.cpp

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState;
    return gProcess;
}

self()很簡短,看的出來這裏是個單例模式。第一次創建的時候是空的,然後 new 一個出來,第二次就直接返回第一次創建的對象了。創建對象調用了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)
{
    if (mDriverFD >= 0) {
        // XXX Ideally, there should be a specific define for whether we
        // have mmap (or whether we could possibly have the kernel module
        // availabla).
#if !defined(HAVE_WIN32_IPC)
        // 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); //內存映射
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
#else
        mDriverFD = -1;
#endif
    }

    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}

構造函數裏比較重要的是open_driver調用。它打開了binder驅動,返回一個fd,然後就可以用這個fd做內存映射了。在這裏的內存映射大小是 BINDER_VM_SIZE,

#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))

它的大小剛好是1M-8K。爲什麼-8K有興趣的可以自行了解。這裏不是重點。

open_binder()代碼,

static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR);//打開binder驅動
    if (fd >= 0) {
        fcntl(fd, F_SETFD, FD_CLOEXEC);
        int vers = 0;
        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 = DEFAULT_MAX_BINDER_THREADS;
        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;//返回文件描述符
}

注意這裏只做了打開驅動一件事,並且把文件描述符返回。也許有會疑惑,這裏沒見到預想的事件循環?沒錯,這裏只做打開驅動一件事,事件循環在mediaserver啓動的最後一步纔會開始。在這個階段,所有的多媒體服務都還沒有加入到系統SM中,因此還不需要事件循環來接受消息。

defaultServiceManager()

讓我們回到 main_mediaserver 中。在ProcessState::self()之後是defaultServiceManager()。它在IServiceManager.cpp裏,
Mashmallow/frameworks/native/libs/binder/IServiceManager.cpp

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
    
    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL)); //獲取BpBinder
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }
    
    return gDefaultServiceManager;
}

OK,這裏又是一個單例。但這裏就稍微有點複雜了。

ProcessState::self()->getContextObject(NULL)

上一步我們實例化了ProcessState,這裏我們還是調用 self(),拿到的也還是那個單例。然後去獲取上下文對象,傳入的是NULL也就是0.我們看看它做了什麼

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle); //查找entry

    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }

            b = new BpBinder(handle);  //創建BpBinder對象
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

傳進來的NULL也就是0對吧,首先它會去lookup一遍,拿到一個entry之後,看看entry裏保存的e->binder是不是空的。空的就創建一個。然後把它返回。
這裏創建的binder是BpBinder,參數handle也很特殊是0。

讓我們回到IServiceManager中。從上面的分析知道從ProcessState裏拿到的是一個 BpBinder。至於sp是個什麼玩意不用太在意,IBinder表示返回的BpBinder是IBinder的派生。

問題來了,BpBinder怎麼轉成sp< IServiceManager >的?

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這個玩意。展開這一段會有點複雜,但它裏面涉及到後面要用到的remote對象,沒辦法繞開它理解。

interface_cast類似於java中的代理模式,或者類型強轉。轉換的類型必須是實現了同一套接口的對象。這裏做的轉換是BpBinder -> BpServiceManager。

個人覺得這裏看成代理模式會好理解一些

interface_cast是一個內聯函數,
Mashmallow/frameworks/native/include/binder/IInterface.h

template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}

它定義了一個模板 INTERFACE,展開後調用的是 INTERFACE:asInterface()。

INTERFACE:asInterface()又是個什麼高科技?其實它是一個宏,也在 IInterface.h 中定義。它涉及到兩個宏,DECLARE_META_INTERFACE 和 IMPLEMENT_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();                                            \


#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() { }                                   \

IMPLEMENT是對DECLEAR的實現。DECLEAR定義了一系列api。
回到 IServiceManager.h中,它使用DECLEAR做了函數聲明,

class IServiceManager : public IInterface
{
public:
    DECLARE_META_INTERFACE(ServiceManager);

這裏把宏展開,在宏展開後就有了這麼個函數聲明,

static android::sp<ServiceManager> asInterface(                       \
        const android::sp<android::IBinder>& obj);    

android::是一個命名域,關於命名域可以回頭自己再去查是個什麼概念。
這裏的關鍵是 asInterface() 是個聲明。那麼它的實現在哪裏?

答案在 IServiceManager.cpp 中,

IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");

我們對IMPLEMENT進行宏展開,就變成

android::sp<IServiceManager> IServiceManager::asInterface(                \
        const android::sp<android::IBinder>& obj)                   \
{                                                                   \
    android::sp<IServiceManager> intr;                                 \
    if (obj != NULL) {                                              \
        intr = static_cast<IServiceManager*>(                          \
            obj->queryLocalInterface(                               \
                    I##INTERFACE::descriptor).get());               \
        if (intr == NULL) {                                         \
            intr = new BpServiceManager(obj); //真正返回的東西      \
        }                                                           \
    }                                                               \
    return intr;                                                    \
}  

所以最後得到的是一個 BpServiceManager 對象。而傳入的obj則是BpBinder。

MediaPlayerService::instantiate();

如果你還沒暈的話,繼續往下看。如果已經暈了,建議把 ProcessState::self()defaultServiceManager() 這兩部分再看一次。如果沒有C/C++功底的話,這兩部分得花不少時間反覆看才能看明白。

在經過上面兩部分的邏輯之後,mediaserver進程就打開了binder驅動,並且獲得了一個BpServiceManager對象。

Bp前綴表示的是BinderProxy,它代表着一個Binder對象在C端的代理。與之相對的有BnXXX,前綴Bn代表的是BinderNative,代表一個在S端的服務。

相對應的,MediaPlayerService也有Bp和Bn。
/Mashmallow/frameworks/av/media/libmediaplayerservice/MediaPlayerService.h

class MediaPlayerService : public BnMediaPlayerService

我們在 mediaserver 所要啓動的MediaPlayerService實際是個 BnMediaPlayerService 派生類。那麼BpXXX在哪裏呢?熟悉Proxy的設計的話就知道,不管是在哪種語言哪種平臺下,Proxy都希望是對用戶透明的,也可以說用戶看不見代理。那麼BpXXX一定不會在 .h 頭文件裏對不?

再往前猜一步,media player service 的S端叫MediaPlayerService,那它的C端是不是應該有個對應的叫 IMediaPlayerService?
Mashmallow/frameworks/av/media/libmedia/IMediaPlayerService.cpp

class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
{
public:
    BpMediaPlayerService(const sp<IBinder>& impl)
        : BpInterface<IMediaPlayerService>(impl)
    {
    }

果然在這裏有個BpMediaPlayerService,並且在頭文件IMediaPlayerService.h是沒有聲明的。

OK,回到 MediaPlayerService::instantiate(),這時候S端的MediaPlayerService準備啓動,
Mashmallow/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}

它調用了 defaultServiceManager,然後把實例化的 MediaPlayerService addService進去。

addService的實現在
Mashmallow/frameworks/native/libs/binder/IServiceManager.cpp
還記得 defaultServiceManager 拿到的 BpBinder 通過 interface_cast 變成了什麼玩意嗎?

BpServiceManager是這個東西。 addService的實現也就在它的類實現裏面,

virtual status_t addService(const String16& name, const sp<IBinder>& service,
        bool allowIsolated)
{
    Parcel data, reply;
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
    data.writeString16(name);
    data.writeStrongBinder(service);
    data.writeInt32(allowIsolated ? 1 : 0);
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);//調用binder驅動傳遞消息
    return err == NO_ERROR ? reply.readExceptionCode() : err;
}

關鍵在於 remote()->transact,這裏是通過Binder驅動真正進行消息傳遞的地方。傳遞的方向是,
BpServiceManager -> BnServiceManager
新的問題出現了
remote() 返回的是啥?
BnServiceManager 在哪?

OK,第一個問題,remote()返回什麼。
BpServiceManager是從BpInterface派生的,

class BpServiceManager : public BpInterface<IServiceManager>

那麼BpInterface的聲明我們看看,

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
                                BpInterface(const sp<IBinder>& remote);//這裏的remote

protected:
    virtual IBinder*            onAsBinder();
};

是不是看到熟悉的remote了。

再回想BpServiceManager構造的時候傳入的參數是啥? BpBinder對不對?
所以這裏 remote()返回的其實是 BpBinder對象,也就是Binder驅動在C端的一個代理。

OK,下一個問題,BnServiceManager 在哪?
很不幸,雖然在 IServiceManager.cpp 裏確實有 BnServiceManager 的實現,但這裏的 BnServiceManager 實際上並不是這個東西,而是我們最開始的時候講的 Binder驅動的啓動 裏的 servicemanager 進程裏的binder。

明確這兩點後回到 remote()->transact。現在知道是BpBinder的 transact() 使用了Binder驅動進行消息通信,
Mashmallow/frameworks/native/libs/binder/BpBinder.cpp

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags); //發出消息的地方
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

這裏出現的IPCThreadState是個什麼玩意?

IPCThreadState

從名字上來看可以猜到 IPCThreadState 是一個用來進行跨進程通信(IPC)的線程。而且它並不是我們第一次見到,在ProcessState::self()->getContextObject(NULL)中,創建BpBinder的時候已經調用過一次 IPCThreadState::self()了。
可想而知它也是個單例。調用transact是爲了向Binder的S端發消息,

Mashmallow/frameworks/native/libs/binder/IPCThreadState.cpp

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_LOG_TRANSACTIONS() {
        TextOutput::Bundle _b(alog);
        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
            << handle << " / code " << TypeCode(code) << ": "
            << indent << data << dedent << endl;
    }
    
    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);//數據寫入到mOut
    }
    
    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }
    
    if ((flags & TF_ONE_WAY) == 0) {
        #if 0
        if (code == 4) { // relayout
            ALOGI(">>>>>> CALLING transaction 4");
        } else {
            ALOGI(">>>>>> CALLING transaction %d", code);
        }
        #endif
        if (reply) {
            err = waitForResponse(reply); //mOut寫入到 /dev/binder 內存映射
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        #if 0
        if (code == 4) { // relayout
            ALOGI("<<<<<< RETURNING transaction 4");
        } else {
            ALOGI("<<<<<< RETURNING transaction %d", code);
        }
        #endif
        
        IF_LOG_TRANSACTIONS() {
            TextOutput::Bundle _b(alog);
            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
                << handle << ": ";
            if (reply) alog << indent << *reply << dedent << endl;
            else alog << "(none requested)" << endl;
        }
    } else {
        err = waitForResponse(NULL, NULL);
    }
    
    return err;
}

代碼裏有兩處可疑的地方,分別是

err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
err = waitForResponse(reply);

writeTransactionData看似發送消息,但實際上它只做了數據處理,

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;

    tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;
    
    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
        *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
        tr.offsets_size = 0;
        tr.data.ptr.offsets = 0;
    } else {
        return (mLastError = err);
    }
    
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));
    
    return NO_ERROR;
}

看到沒,這裏一點跟驅動交互的事情都沒有。最終只是把要發送的數據寫入到mOut這個buffer裏。

waitForResponse做了什麼呢,

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break; //和驅動交互
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        
        cmd = (uint32_t)mIn.readInt32();
        
        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }

        switch (cmd) {
        ...
            goto finish;

        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }
    
    return err;
}

這裏省略了一些代碼。它一上來就跟驅動交互,通過talkWithDriver,把mOut裏的東西往內存映射裏寫。然後等待通信完成,把返回的數據讀到 mIn 裏。talkWithDriver的代碼雖然多但並不複雜,這裏就不展開分析了。接收方是誰呢?上面已經說了,是 servicemanager 進程。

細心的會發現,binder的IPC過程,似乎是個同步過程?是的沒錯,它就是個同步過程。

ProcessState::self()->startThreadPool();

到上面IPCThreadState的transact這裏,其實整個流程還沒結束。實際上到transact這裏只是把MediaPlayerService啓動起來,並且註冊到servicemanager裏。此時MediaPlayerService還沒準備好接受消息。
這句話啥意思?

通過上面的分析可以知道,啓動起來的MPS是個S端的服務,那麼它是要提供服務給C端的。既然要提供服務,那麼它就要有監聽事件的能力對吧。也就是說,必然有個類似 event loop的邏輯。那麼這個邏輯在哪裏呢?

回到最開始的 main_mediaserver 啓動流程,

sp<ProcessState> proc(ProcessState::self()); 
sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get());
MediaPlayerService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();

這裏還有兩行代碼沒有分析,關鍵的event loop就在這裏。

ProcessState::self()->startThreadPool() 會啓動一個線程用於監聽事件,也就是所謂的event loop。看看它的實現,

void ProcessState::startThreadPool()
{
    AutoMutex _l(mLock);
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }
}

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        ALOGV("Spawning new pooled thread, name=%s\n", name.string());
        sp<Thread> t = new PoolThread(isMain); //創建PoolThread
        t->run(name.string()); //啓動線程
    }
}

調用鏈最後會創建一個 PoolThread 對象,然後調用它的 run 方法。PoolThread是Threads的派生類,android源碼裏的Threads有很多實現,這裏是從
Mashmallow/system/core/libutils/Threads.cpp派生的,
省略一些代碼後它的run方法如下,

status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    Mutex::Autolock _l(mLock);

    if (mRunning) {
        // thread already started
        return INVALID_OPERATION;
    }

    // reset status and exitPending to their default value, so we can
    // try again after an error happened (either below, or in readyToRun())
    mStatus = NO_ERROR;
    mExitPending = false;
    mThread = thread_id_t(-1);

    // hold a strong reference on ourself
    mHoldSelf = this;

    mRunning = true;

    bool res;
    if (mCanCallJava) {
        res = createThreadEtc(_threadLoop, // _threadLoop
                this, name, priority, stack, &mThread);
    } else {
        res = androidCreateRawThreadEtc(_threadLoop, // _threadLoop
                this, name, priority, stack, &mThread);
    }

它調用了一個叫 _threadLoop的東西,我們直接忽略其他代碼看最終調用,

int Thread::_threadLoop(void* user)
{
    ...

    do {
        ...
        } else {
            result = self->threadLoop();
        }

看到了嗎,它會回去調用派生類的 threadLoop() 方法,也就是在PoolThread中的threadLoop().

protected:
    virtual bool threadLoop()
    {
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }
    
    const bool mIsMain;
};

而threadLoop() 又會去調用 joinThreadPool()。

void IPCThreadState::joinThreadPool(bool isMain)
{
    LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());

    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
    
    // This thread may have been spawned by a thread that was in the background
    // scheduling group, so first we will make sure it is in the foreground
    // one to avoid performing an initial transaction in the background.
    set_sched_policy(mMyThreadId, SP_FOREGROUND);
        
    status_t result;
    do {
        processPendingDerefs();
        // now get the next command to be processed, waiting if necessary
        result = getAndExecuteCommand(); //執行C端的命令

        if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
            ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
                  mProcess->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);

    LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",
        (void*)pthread_self(), getpid(), (void*)result);
    
    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}

到 do{…}while() 就真相大白了,終於可以看到類似 event loop 的邏輯。此時 media player service 才真正具有了S端的能力,這裏的event loop會等待消息進來,然後通過getAndExecuteCommand把C端的命令派發給S端服務去執行。

最後

整一個binder通信流程基本到這裏就清晰了。但還有個問題沒解決,main_mediaserver在startThreadPool之後又有一行 joinThreadPool,那是幹嘛?
在查閱了很多資料後我也沒有明白這個設計。它實際做的事情不過是開另外個線程等事件進來,而網上有開發者在屏蔽了這行代碼後也能正常執行所有流程,所以這個可能是谷歌的容錯設計?萬一掛了一個線程還有一個輔助線程麼?如果有人明白的話歡迎解疑。

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