binder機制(上篇)

來源:互聯網
上載者:User

標籤:工作

問題:binder到底是如何從代理對象找到其對應的binder實體的呢?要回答這個就必須要看Binder驅動如何工作的。

1.Binder裝置檔案的開啟

一個進程在使用Binder機制進行通訊之前,必須開啟裝置檔案/dev/binder來擷取一個檔案描述符,然後才能通過這個檔案描述符和Binder驅動進行互動,進程調用open函數開啟/dev/binder的時候,binder_open會被調用,實現如下:

static int binder_open(struct inode *nodp, struct file *filp){    struct binder_proc *proc;    . . . . . .    proc = kzalloc(sizeof(*proc), GFP_KERNEL);    get_task_struct(current);    proc->tsk = current;    . . . . . .    hlist_add_head(&proc->proc_node, &binder_procs);    proc->pid = current->group_leader->pid;    . . . . . .    filp->private_data = proc;    . . . . . .}


首先建立一個binder_proc結構體,接下來就進行proc的初始化工作,並將proc加入全域雙向鏈表binder_procs當中,Binder驅動程式將所有開啟裝置檔案/dev/binder的進程都加入到binder_procs當中,因此,通過遍曆這個鏈表就可以知道系統當前有多少進程在使用Binder處理序間通訊機制。filp->private_data=proc;將新建立的proc儲存在當前檔案的private_data變數中,參數filep指向一個開啟的檔案結構體。進程以後調用binder_ioctl與驅動程式互動式,會將struct file結構體傳過去,從file結構體的private_data欄位就可以得到proc。

1.1 binder_proc介紹
struct binder_proc{    struct hlist_node proc_node;    struct rb_root threads;    struct rb_root nodes;    struct rb_root refs_by_desc;    struct rb_root refs_by_node;    int pid;    . . . . . .    . . . . . .};

binder_proc用來描述正在使用binder機制的進程。上面也提到,每當進程開啟/dev/binder時,binder驅動就會為其建立binder_proc
結構體。每一個使用Binder進程通訊機制的進程都有一個binder線程池,用來出來通訊請求,這個Binder線程池是由Binder驅動程式來維護的。成員變數threads是紅/黑樹狀結構節點,它以線程ID作為關鍵字來組織一個進程中的binder線程池,進程可以調用ioctl函數將線程註冊到binder驅動程式當中。
一個進程內部包含了一系列的Binder實體物件和Binder引用對象,進程使用三個紅/黑樹狀結構描述。nodes組織binder實體物件,以binder實體物件的ptr作為關鍵字;refs_by_desc和refs_by_node用來組織Binder引用對象,前者以Binder引用對象的desc作為關鍵字,後者以node作為關鍵字。

有了binder_ref和binder_node知識,我們就可以解釋BpBinder到底如何和BBinder聯絡上的。OK我們已經可以更深入的說明Binder
控制代碼的作用了,比如進程1的BpBinder發起跨進程調用,向binder驅動傳入自己記錄的控制代碼值,binder驅動就會在進程1對應的binder_proc結構的引用樹種尋找和控制代碼值相等的binder_ref節點,一旦找到binder_ref節點,就可以通過該節點的node域找到對應的binder_node節點,這個目標binder_node當然是從屬進程2的binder_proc啦,不過不要緊,因為binder_ref和binder_node都處於binder驅動的地址空間中,所以是可以用指標直接指向的。目標binder_node節點的cookie域,記錄的其實是進程2中BBinder的地址,binder驅動只需把這個值反映給應用程式層,應用程式層就可以直接拿到BBinder了。這就是Binder完成精確打擊的大體過程。如:

2. BpBinder和IPCThreadState

從名字上看,IPCThreadState是“和跨進程通訊(IPC)相關的線程狀態”。那麼很顯然,一個具有多個線程的進程裡應該會有多個IPCThreadState對象了,只不過每個線程只需一個IPCThreadState對象而已。這有點兒“局部單例”的意思。所以,在實際的代碼中,IPCThreadState對象是存放線上程的局部儲存區(TLS)裡的。
IPCThreadState類中含有一個成員變數mProcess,它指向一個ProcessState對象,從名字上看“進程狀態”就知道它是進程相關,是進程範圍內唯一的。ProcessState負責初始化Binder裝置,即開啟裝置檔案/dev/binder,以及將裝置檔案對應到進程的地址空間。Binder線程中的每個線程都可以通過它來和Binder驅動建立串連。

2.1 BpBinder的transact函數
每當我們利用BpBinder的transact函數發起一次跨進程事物時,其內部都是調用IPCThreadState對象的transact,如下:
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;}

當然進程中的BpBinder可能被多個線程使用,所以發起傳輸的IPCThreadState對象可能並不是同一個對象,不過沒關係,因為這些ICPThreadState對象可能最終使用同一個ProcessState對象。

2.1.1

調用IPCThreadState的transact

status_t IPCThreadState::transact(int32_t handle,                                  uint32_t code, const Parcel& data,                                  Parcel* reply, uint32_t flags){    . . . . . .        // 把data資料整理進內部的mOut包中        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);    . . . . . .    if ((flags & TF_ONE_WAY) == 0)    {        . . . . . .        if (reply)        {            err = waitForResponse(reply);        }        else        {            Parcel fakeReply;            err = waitForResponse(&fakeReply);        }        . . . . . .    }    else    {        err = waitForResponse(NULL, NULL);    }    return err;}

IPCThreadState::transact()會先調用writeTransactionData()函數將data資料整理進內部的mOut包中,這個函數的代碼如下:

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.handle = handle;    tr.code = code;    tr.flags = binderFlags;    tr.cookie = 0;    tr.sender_pid = 0;    tr.sender_euid = 0;    . . . . . .        tr.data_size = data.ipcDataSize();        tr.data.ptr.buffer = data.ipcData();        tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);        tr.data.ptr.offsets = data.ipcObjects();    . . . . . .    mOut.writeInt32(cmd);    mOut.write(&tr, sizeof(tr));    return NO_ERROR;}

真正完成跨進程事務的是waitForResponse函數,代碼如下:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult){    int32_t cmd;    int32_t err;    while (1)    {        // talkWithDriver()內部會完成跨進程事務        if ((err = talkWithDriver()) < NO_ERROR)            break;        // 事務的回複資訊被記錄在mIn中,所以需要進一步分析這個回複        . . . . . .        cmd = mIn.readInt32();        . . . . . .        switch (cmd)        {        case BR_TRANSACTION_COMPLETE:            if (!reply && !acquireResult) goto finish;            break;        case BR_DEAD_REPLY:            err = DEAD_OBJECT;            goto finish;        case BR_FAILED_REPLY:            err = FAILED_TRANSACTION;            goto finish;        . . . . . .        . . . . . .        default:            // 注意這個executeCommand()噢,它會處理BR_TRANSACTION的。            err = executeCommand(cmd);            if (err != NO_ERROR) goto finish;            break;        }
2.1.2 talkWithDriver

waitForResponse是通過talkWithDriver來和Binder驅動打交道的,最終會調用ioctl函數,因為ioctl函數在傳遞BINDER_WRITE_READ意義時,使用輸入緩衝區和輸出緩衝區,所以IPCThreadState有Parcel類型的成員變數,mIn、mOut,將mOut內容發出去,發送後的回複寫入mIn。
talkWithDriver代碼如下:

status_t IPCThreadState::talkWithDriver(bool doReceive){    . . . . . .    binder_write_read bwr;    . . . . . .    bwr.write_size = outAvail;    bwr.write_buffer = (long unsigned int)mOut.data();    . . . . . .        bwr.read_size = mIn.dataCapacity();        bwr.read_buffer = (long unsigned int)mIn.data();    . . . . . .    . . . . . .    do    {        . . . . . .        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)            err = NO_ERROR;        . . . . . .    } while (err == -EINTR);    . . . . . .    . . . . . .    return err;}

mIn、mOut會封裝成binder_write_read結構,然後在傳給ioctl函數。最關鍵的當然是ioctl了,此時使用的檔案描述符就是ProcessState中記錄的mDriverFD,說明是想binder驅動傳遞語義,至此應用程式通過BpBinder向遠端傳輸的過程就清楚了,資料傳送到Binder驅動,就看binder驅動做哪些動作,留到中篇解析。

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

binder機制(上篇)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.