Android底層:通熟易懂分析binder:1.binder準(zhǔn)備工作

寫binder的初衷

提起binder氮采,應(yīng)該會有很多人說鹰椒,binder這都已經(jīng)多么老的技術(shù)了宝鼓,并且分析binder的文章是一搜一大堆轩性,你這完全沒必要寫binder方面的文章昂拖薄格遭!
我其實對于這種觀點不以為然,說下我的理由吧:

  1. 對自己看過的留瞳,學(xué)過的binder知識需要有一個總結(jié)拒迅,這個總結(jié)是非常必要的,不信大家可以想想她倘,若沒有總結(jié)璧微,以前學(xué)過的知識是不是已經(jīng)忘記了很多,沒有總結(jié)那你學(xué)過的知識就是零散的硬梁,不成系統(tǒng)前硫。
  2. 即時binder很老了,依然會有很多人不了解荧止,有可能你會說屹电,了解它有啥用。我想說binder是android基石中的基石跃巡,android中到處都可以看到binder的身影(AMS危号,WMS等等),若了解了binder素邪,你可以對android會有一個很深的認(rèn)識外莲,進程與進程之間的交互邏輯,進程與系統(tǒng)服務(wù)之間交互邏輯等等兔朦。
  3. Android系統(tǒng)架構(gòu)是分為app偷线,framework,native烘绽,linux kernel層淋昭,這四層之間到底是一個什么聯(lián)系,當(dāng)了解了binder(binder貫穿了這四層)后安接,您就會對這四層之間是怎么樣協(xié)同工作來保證系統(tǒng)正常運行的翔忽。

疑惑點

在學(xué)習(xí)binder的過程中或多或少都會遇到一些疑惑點英融,設(shè)計者為什么這樣來設(shè)計,我遇到了以下的疑惑點:

  1. 兩個進程(client和server進程)之間通信歇式,client進程拿到的是server進程的代理類(其實是一個handle)驶悟,那這個handle是在什么時候生成的,生成規(guī)則是啥材失?
  2. app進程是怎么與驅(qū)動層建立一一對應(yīng)關(guān)系的
  3. 驅(qū)動層中client是怎么樣傳遞數(shù)據(jù)并且喚醒server的
  4. client進程調(diào)用server進程方法時是怎么樣導(dǎo)致阻塞的
  5. 驅(qū)動層的 binder_proc, binder_thread和上層(非驅(qū)動層)的進程痕鳍,線程是一個什么關(guān)系。binder_node, binder_ref, binder_nodes, binder_refs作用是啥龙巨?
  6. app進程是在啥時候open driver(打開驅(qū)動層)的
  7. ServiceManager進程起什么作用笼呆,ServiceManager存儲的是系統(tǒng)服務(wù)的代理binder,還是真正的系統(tǒng)服務(wù)對象
  8. app進程與系統(tǒng)服務(wù)(如AMS)進行通信時旨别,是每次都要經(jīng)過ServiceManager去拿到系統(tǒng)服務(wù)的binder嗎诗赌?
  9. client進程與server進程通信是否要經(jīng)過ServiceManager進程

通過閱讀源碼以及參考大牛的文章(比如gityuan,老羅)等,基本上把上面的疑惑點都解決了秸弛,因此希望能在這個系列文章中把這些疑惑給解決铭若。同時也希望自己能從一個通熟易懂的角度來給大家分析binder,這也是我在文章的題目中要加 通熟易懂 的目的递览。
當(dāng)然如果想通過一篇文章把binder講解的很清楚是不可能的叼屠,因此這會是一個系列的文章。

android的源碼是9.0.0_r3/
binder_driver層的源碼需要單獨去google官網(wǎng)下載绞铃,不要選擇goldfish(模擬器)的版本镜雨,選擇其他的版本

驅(qū)動層代碼: binder.c

首先我們從binder的準(zhǔn)備工作開始這個系列文章。為啥要從準(zhǔn)備工作講起憎兽,因為我覺得追溯一個事物要從它的根源追起冷离。

本篇內(nèi)容

  1. binder準(zhǔn)備工作
  2. 系統(tǒng)調(diào)用
  3. 第一個準(zhǔn)備工作:open binder driver
  4. 第二個準(zhǔn)備工作:mmap(內(nèi)存映射)
  5. 進程與driver層通信
  6. 第三個準(zhǔn)備工作:啟動binder主線程,接收數(shù)據(jù)
  7. binder準(zhǔn)備工作何時被調(diào)用
  8. 總結(jié)

1.binder準(zhǔn)備工作

binder在能進行進程通信之前纯命,是需要做一些準(zhǔn)備工作的西剥,這些工作大致上有下面3個:
1. open binder driver
2. mmap
3. 啟動binder主線程,接收數(shù)據(jù)

在講準(zhǔn)備工作之前亿汞,先講下系統(tǒng)調(diào)用

2.系統(tǒng)調(diào)用

app進程或系統(tǒng)進程是屬于兩個不同的進程瞭空,進程之間是沒辦法通信的,因此需要一個中間者來參與疗我,這個中間者就是linux內(nèi)核咆畏,如下圖:


app1調(diào)用app2示例圖 (4).jpg

app進程或系統(tǒng)進程又被稱為用戶空間, linux內(nèi)核被稱為內(nèi)核空間吴裤,
用戶空間在調(diào)用內(nèi)核空間程序的過程被稱為系統(tǒng)調(diào)用(syscall)在進行系統(tǒng)調(diào)用時旧找,用戶空間會陷入內(nèi)核態(tài)。
陷入內(nèi)核態(tài)簡單理解就是:用戶空間的線程暫停執(zhí)行(處于中斷狀態(tài))麦牺,切換到內(nèi)核線程執(zhí)行內(nèi)核程序钮蛛,當(dāng)內(nèi)核程序執(zhí)行完畢后鞭缭,切換到用戶空間,用戶空間線程恢復(fù)執(zhí)行魏颓;反之若內(nèi)核程序處于阻塞狀態(tài)岭辣,則用戶空間的線程也一直處于中斷狀態(tài)。
binder用戶空間調(diào)用到內(nèi)核空間方法的對應(yīng)關(guān)系是:
open-> binder_open, ioctl->binder_ioctl甸饱,mmap->binder_mmap 等等
系統(tǒng)調(diào)用可以說是binder的核心原理沦童。
內(nèi)核空間是共享內(nèi)存的

3. 第一個準(zhǔn)備工作:open binder driver

第一個準(zhǔn)備工作為啥是open binder driver呢?我們從代碼上分析下都做了哪些事情叹话,分析完后自然就能知道了偷遗,對應(yīng)的類是ProcessState,這個類是一個單例的驼壶,因此一個進程中只存在一個實例鹦肿,調(diào)用打開驅(qū)動的代碼如下:

static int open_driver(const char *driver){
  // 打開binder driver的關(guān)鍵方法,最終會調(diào)用driver層的binder_open方法, 返回的fd很關(guān)鍵辅柴,后面與driver的交互都需要帶上它
  int fd = open(driver, O_RDWR | O_CLOEXEC);
  if (fd >= 0) {
    int vers = 0;
    // 獲取binder版本號
    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(%d) does not match user space protocol(%d)! ioctl() return value: %d",
            vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
        close(fd);
        fd = -1;
    }
    size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
    // 告訴driver層可以啟動的最大的線程數(shù)
    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 '%s' failed: %s\n", driver, strerror(errno));
  }
  return fd;
}

調(diào)用open方法后會返回一個fd值卓鹿,這個值很重要复局,后面的每次與driver通信都會用到它。
open方法最終會調(diào)到driver層的binder_open方法:

static int binder_open(struct inode *nodp, struct file *filp){
      // 聲明binder_proc傍念,后續(xù)對它分配空間歪架,初始化等操作
  struct binder_proc *proc;
     省略代碼 ...
  proc = kzalloc(sizeof(*proc), GFP_KERNEL);
  if (proc == NULL)
    return -ENOMEM;
  get_task_struct(current);
  proc->tsk = current;
  proc->vma_vm_mm = current->mm;
  INIT_LIST_HEAD(&proc->todo);
  init_waitqueue_head(&proc->wait);
  省略代碼 ...
  hlist_add_head(&proc->proc_node, &binder_procs);
  proc->pid = current->group_leader->pid;
  INIT_LIST_HEAD(&proc->delivered_death);
  filp->private_data = proc;
      省略代碼 ...
  return 0;
}

open binder driver這一個過程股冗,做了幾件很重要的事情:

1.在driver層對binder_proc(binder_proc記錄了很多的信息,后續(xù)會著中介紹它)進行了初始化和蚪,對binder_proc中的todo隊列等信息做了初始化止状。
2.把上層的pid(進程id)記錄在了binder_proc中
3.把binder_proc賦值給了filp->private_data,filp是和返回給上層的fd值是存在一定關(guān)系的攒霹,通過fd是可以找到filp怯疤,進而找到當(dāng)前進程的binder_proc。
4.ProcessState把返回的fd存在內(nèi)存中催束,以便與driver層進行通信
5.獲取driver層的版本信息(ioctl獲取)進行對比
6.通知driver層上層最多啟動幾個binder線程(ioctl通知)

open binder driver后集峦,上層的進程在driver層就有了記錄,后續(xù)上層進程就可以與driver層通信了(通信基本都是通過ioctl進行的)抠刺。

4. 第二個準(zhǔn)備工作:mmap(內(nèi)存映射)

ProcessState::ProcessState(const char *driver){
  省略代碼...
  // 打開驅(qū)動成功后
  if (mDriverFD >= 0) {
    // 進行內(nèi)存映射
    // mmap the binder, providing a chunk of virtual address space to receive transactions.
    mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
    if (mVMStart == MAP_FAILED) {
        // *sigh*
        ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
        close(mDriverFD);
        mDriverFD = -1;
        mDriverName.clear();
    }
  }
  省略代碼...
}

在ProcessState中塔淤,打開binder驅(qū)動后,緊接著做mmap工作速妖。
對應(yīng)driver層的方法是: binder_mmap

binder在性能方面能優(yōu)于其他進程通信的其中一個因素是進程通信中的數(shù)據(jù)只拷貝一次高蜂,拷貝一次的重要原因就是mmap(內(nèi)存映射),關(guān)于mmap的介紹網(wǎng)上有好多的介紹罕容,這邊就不贅述了备恤。

5.進程與driver層通信

在講啟動binder主線程稿饰,接收數(shù)據(jù)內(nèi)容之前,需要把一個基礎(chǔ)的一直提到的進程與driver層通信內(nèi)容講清楚烘跺,這樣非常有利于我們理解后面的內(nèi)容湘纵。

不像socket通信,client和server端在連接建立后是可以互相發(fā)送消息的滤淳,binder進程與driver層通信原理是系統(tǒng)調(diào)用(syscall)梧喷,通信的發(fā)起方只能是上層的進程。

5.1發(fā)送數(shù)據(jù)給driver層

主要是通過ioctl方法傳遞數(shù)據(jù)脖咐,調(diào)用過程是ioctl-->binder_ioctl(driver層方法)铺敌,

  ioctl(fd, cmd,數(shù)據(jù))

ioctl的參數(shù)如上屁擅,fd不用說了就是打開binder驅(qū)動返回的值(用于查找當(dāng)前進程在driver層的binder_proc)偿凭,cmd的值有BINDER_WRITE_READ,BINDER_SET_MAX_THREADS等等派歌,數(shù)據(jù)對應(yīng)的結(jié)構(gòu)體有binder_write_read等弯囊。

5.2driver層返回數(shù)據(jù)

有些調(diào)用是需要driver層返回數(shù)據(jù)的,比如cmd為BINDER_WRITE_READ的調(diào)用胶果,driver層把需要返回的數(shù)據(jù)調(diào)用copy_to_user方法copy 用戶空間中匾嘱,當(dāng)binder_ioctl方法執(zhí)行完畢后,切換到用戶空間線程早抠,這時候從binder_write_read.read_buffer中取數(shù)據(jù)霎烙。

5.3 binder_write_read

上面講完了上層進程與driver層的通信方式,為什么要說binder_write_read呢蕊连,因為binder_write_read是binder通信過程中使用最頻繁的結(jié)構(gòu)體悬垃,進程之間通信都用的是它,咱們先有一個初步的了解甘苍,后面還會在詳細介紹尝蠕,看下它的定義

  struct binder_write_read {
    //write開頭和傳遞給driver層的數(shù)據(jù)有關(guān)系
    binder_size_t write_size;
    binder_size_t write_consumed;
    //write_buffer傳遞給driver層數(shù)據(jù)
    binder_uintptr_t write_buffer;
    //read開頭和driver層返回的數(shù)據(jù)有關(guān)系
    binder_size_t read_size;
    binder_size_t read_consumed;
    //read_buffer driver層返回的數(shù)據(jù)
    binder_uintptr_t read_buffer;
 };

那我們從代碼流程上來看下是怎么樣在上層進程與driver層傳遞binder_write_read的,我們只是先簡單看下這一流程载庭,在后面部分還會著中細說這一流程細節(jié)趟佃。這個流程主要是在IPCThreadState這個類中

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

writeTransactionData方法先把進程之間交互的關(guān)鍵數(shù)據(jù)寫入binder_transaction_data這個結(jié)構(gòu)體中,在把binder_transaction_data寫入mOut昧捷,mOut的數(shù)據(jù)最終寫入binder_write_read的write_buffer中闲昭,最終調(diào)用:

status_t IPCThreadState::talkWithDriver(bool doReceive)

talkWithDriver方法從方法名就能看出是與driver層進行通信的,doReceive為true代表等待driver層返回數(shù)據(jù)靡挥,否則不等待序矩。數(shù)據(jù)最終寫入binder_write_read中,該方法中最終調(diào)用下面方法:

 ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)

ioctl最終調(diào)用到driver層的binder_ioctl方法

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

binder_ioctl方法中因為當(dāng)前的cmd是BINDER_WRITE_READ跋破,因此會執(zhí)行binder_ioctl_write_read方法

static int binder_ioctl_write_read(struct file *filp,
            unsigned int cmd, unsigned long arg,
            struct binder_thread *thread)

binder_ioctl_write_read方法中簸淀,binder_thread_write方法會讀取上層傳遞的數(shù)據(jù)瓶蝴,binder_thread_read方法會根據(jù)情況返回給上層數(shù)據(jù)。

binder_thread_write非常的重要租幕,比如進程通信完成通知driver層做收尾工作舷手,回復(fù)數(shù)據(jù)給對方進程,以及driver層通知上層啟動binder線程等等劲绪。先暫時簡單的介紹下這方面內(nèi)容男窟,以便于我們能更好的理解后面的內(nèi)容,在后面時我們還會詳細講解相關(guān)的內(nèi)容贾富。

6. 第三個準(zhǔn)備工作:啟動binder主線程歉眷,接收數(shù)據(jù)

6.1為什么啟動binder主線程

為什么要啟動binder主線程,拿socket通信來說颤枪,socket的client和server兩端要想進行通信汗捡,server端必須先啟動以等待client端連接進而通信。同理binder進程之間通信分為client進程和server進程的畏纲,那server進程需要監(jiān)聽client進程傳遞過來的數(shù)據(jù)扇住,那這個監(jiān)聽行為就需要放在一個線程中,那這個線程就是binder主線程盗胀,binder主線程就是一個接收者。

6.2 啟動binder主線程流程分析

那我們就從代碼上分析下读整,分析的時候會用到剛剛提到的 進程與driver層通信binder_write_read內(nèi)容咱娶,

void ProcessState::startThreadPool()
{
  AutoMutex _l(mLock);
  if (!mThreadPoolStarted) {
      mThreadPoolStarted = true;
      // true代表啟動binder主線程
      spawnPooledThread(true);
  }
}

再來看下spawnPooledThread方法

void ProcessState::spawnPooledThread(bool isMain)
{
  // binder主線程啟動后執(zhí)行
  if (mThreadPoolStarted) {
      // binder線程的名字膘侮,格式: binder:pid_x(x代表第幾個線程)
      String8 name = makeBinderThreadName();
      ALOGV("Spawning new pooled thread, name=%s\n", name.string());
      // new一個線程isMain是否是binder主線程
      sp<Thread> t = new PoolThread(isMain);
      t->run(name.string());
  }
}

看下PoolThread類

class PoolThread : public Thread
{
public:
    explicit PoolThread(bool isMain)
        : mIsMain(isMain)
    {
    }

protected:
    virtual bool threadLoop()
    {
        // 線程啟動后昧诱,執(zhí)行IPCThreadState的方法
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }

    const bool mIsMain;
};

啟動線程后盏档,最終調(diào)用IPCThreadState::self()->joinThreadPool(mIsMain)方法,上面的流程主要是在ProcessState類中,現(xiàn)在切換到IPCThreadState類中

void IPCThreadState::joinThreadPool(bool isMain)
{
  // mOut的數(shù)據(jù)會發(fā)送給driver層道川,BC_ENTER_LOOPER:代表是binder主線程,
  //BC_REGISTER_LOOPER:代表是driver底發(fā)命令啟動的binder線程
  mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

  status_t result;
  do {
      processPendingDerefs();
      // 等待driver層傳遞的數(shù)據(jù),進行處理,沒數(shù)據(jù)則處于等待狀態(tài)
      // now get the next command to be processed, waiting if necessary
      result = getAndExecuteCommand();

      省略代碼...

      // 若當(dāng)前線程不再用于了并且是非主線程,則之間退出
      // 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);

  // binder線程退出了装诡,需要通知drive層渔伯,以便做一些處理
  mOut.writeInt32(BC_EXIT_LOOPER);
  // 傳false代表通知driver層后玄叠,立馬返回舀寓,不等待driver層數(shù)據(jù)
  talkWithDriver(false);
}

joinThreadPool()方法,若isMain為true代表binder主線程,則不會退出育谬,基本會隨著整個進程一直存在(除非發(fā)生錯誤)膛檀。在線程退出時咖刃,也是需要通知driver層的。著中看下getAndExecuteCommand()這個方法

status_t IPCThreadState::getAndExecuteCommand()
{
  status_t result;
  int32_t cmd;
  // 發(fā)送數(shù)據(jù)給driver層古拴,并且等待返回數(shù)據(jù)
  result = talkWithDriver();
  if (result >= NO_ERROR) {
      size_t IN = mIn.dataAvail();
      if (IN < sizeof(int32_t)) return result;
      cmd = mIn.readInt32();
      
      省略代碼...

      //拿到cmd進行處理
      result = executeCommand(cmd);

      省略代碼...
  }

  return result;
}

getAndExecuteCommand()方法會調(diào)用talkWithDriver()來發(fā)送數(shù)據(jù)給driver層,并且等待driver層的返回數(shù)據(jù)油额,調(diào)用executeCommand(cmd)方法會處理返回的cmd,咱們暫時不討論這個過程芹扭,看下talkWithDriver()方法:

// doReceive默認(rèn)值為true
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
  if (mProcess->mDriverFD <= 0) {
      return -EBADF;
  }
  // 聲明一個binder_write_read
  binder_write_read bwr;

  // Is the read buffer empty?
  const bool needRead = mIn.dataPosition() >= mIn.dataSize();

  // We don't want to write anything if we are still reading
  // from data left in the input buffer and the caller
  // has requested to read the next data.
  const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

  bwr.write_size = outAvail;
  bwr.write_buffer = (uintptr_t)mOut.data();

  // doReceive && needRead都為true,代表會等待driver層的返回數(shù)據(jù)份汗,沒返回之前處于阻塞狀態(tài)
  // This is what we'll read.
  if (doReceive && needRead) {
      bwr.read_size = mIn.dataCapacity();
      // 返回數(shù)據(jù)最終從mIn中讀取
      bwr.read_buffer = (uintptr_t)mIn.data();
  } else {
      bwr.read_size = 0;
      bwr.read_buffer = 0;
  }

  省略代碼...

  bwr.write_consumed = 0;
  bwr.read_consumed = 0;
  status_t err;
  do {
      省略代碼...
      // 與driver層通信
      if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
          err = NO_ERROR;
      else
          err = -errno;

      省略代碼...
  } while (err == -EINTR);

  省略代碼...

  if (err >= NO_ERROR) {
      if (bwr.write_consumed > 0) {
          if (bwr.write_consumed < mOut.dataSize())
              mOut.remove(0, bwr.write_consumed);
          else {
              mOut.setDataSize(0);
              processPostWriteDerefs();
          }
      }
      // 是否有返回數(shù)據(jù)
      if (bwr.read_consumed > 0) {
          mIn.setDataSize(bwr.read_consumed);
          mIn.setDataPosition(0);
      }
      
      省略代碼...

      return NO_ERROR;
  }

  return err;
}

talkWithDriver()方法中最終會調(diào)用ioctl與driver層通信寄猩,發(fā)送BC_ENTER_LOOPER給driver層,在講 進程與driver層通信泊柬,binder_write_read 時講到最終會走到driver層的** binder_ioctl_write_read**方法,binder_thread_write方法讀數(shù)上層的數(shù)據(jù)(暫時不介紹)荤堪,現(xiàn)在著中看下binder_thread_read方法

static int binder_thread_read(struct binder_proc *proc,
              struct binder_thread *thread,
              binder_uintptr_t binder_buffer, size_t size,
              binder_size_t *consumed, int non_block)
{

    省略代碼...

  retry:
    // wait_for_proc_work 為true,標(biāo)明當(dāng)前內(nèi)核線程沒有要處理的事務(wù)肮塞,因此即將進入等待狀態(tài)
    wait_for_proc_work = thread->transaction_stack == NULL &&
          list_empty(&thread->todo);

    省略代碼...

    if (wait_for_proc_work) {
      // 當(dāng)前內(nèi)核線程進入等待狀態(tài)恋谭,等待新事務(wù)加入被喚醒
      if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
            BINDER_LOOPER_STATE_ENTERED))) {
        binder_user_error("%d:%d ERROR: Thread waiting for process work before calling BC_REGISTER_LOOPER or BC_ENTER_LOOPER (state %x)\n",
          proc->pid, thread->pid, thread->looper);
        wait_event_interruptible(binder_user_error_wait,
              binder_stop_on_user_error < 2);
      }
      binder_set_nice(proc->default_priority);
      if (non_block) {
        if (!binder_has_proc_work(proc, thread))
          ret = -EAGAIN;
      } else
        ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    } else {
       省略代碼...
    }

    省略代碼...
    // 說明被喚醒了
    thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

    if (ret)
      return ret;

    while (1) {
      // 下面省略的代碼取出事務(wù)開始執(zhí)行
      省略代碼...
    }

  done:

    // 下面省略的代碼根據(jù)條件來通知上層進程是否開啟新的binder線程
    省略代碼...
    return 0;

}
binder_thread_read方法攘乒,若當(dāng)前內(nèi)核線程沒有事務(wù)要處理贤牛,則內(nèi)核線程進入等待狀態(tài),因此會導(dǎo)致上層進程對應(yīng)的binder線程也進入等待狀態(tài)则酝,若有事務(wù)了則內(nèi)核線程會被喚醒殉簸,緊接著處理事務(wù)(這部分內(nèi)容我們會在后面介紹)。

到此為止啟動binder主線程的流程基本分析完成沽讹,我們來總結(jié)下:
1.首先會在當(dāng)前進程啟動binder主線程般卑,主線程生命周期和進程是一致的。
2.進程之間畢竟是隔離的爽雄,即時啟動了binder主線程蝠检,主線程在沒有binder驅(qū)動的協(xié)調(diào)下也不會收到別的進程的數(shù)據(jù),因此binder主線程啟動完畢后會通知driver層盲链,driver層的對應(yīng)內(nèi)核線程進入等待狀態(tài)蝇率,進而導(dǎo)致上層的binder主線程進入等待狀態(tài)。
3.driver層的內(nèi)核線程收到事務(wù)后刽沾,處理后把相關(guān)的數(shù)據(jù)返回給上層進程中的對應(yīng)binder線程本慕。
4.binder線程分為主線程和普通線程,普通線程執(zhí)行完畢后一般會銷毀

7. binder準(zhǔn)備工作何時被調(diào)用

至此binder的準(zhǔn)備工作已經(jīng)完畢了侧漓,但是還有一個很重要的工作沒做锅尘,那就是binder的準(zhǔn)備工作什么時候被調(diào)用,具體調(diào)用是在app_main.cpp文件中的AppRuntime類中

virtual void onZygoteInit()
{
    sp<ProcessState> proc = ProcessState::self();
    ALOGV("App process: starting thread pool.\n");
    proc->startThreadPool();
}

onZygoteInit方法中布蔗,ProcessState::self() ProcessState構(gòu)造函數(shù)開始初始化藤违,初始化時會open binder drivermmap纵揍,這些工作做完后執(zhí)行
proc->startThreadPool()顿乒,這個方法會啟動binder主線程,接收數(shù)據(jù)泽谨。

AppRuntime的onZygoteInit方法是在app進程zygote之后立馬就調(diào)用的璧榄,因此每個app進程只要被zygote出來后特漩,都會立馬把binder的準(zhǔn)備工作做好。

8. 總結(jié)

下面我用一張圖來總結(jié)下本篇的內(nèi)容


binder.jpg

open binder driver 后上層進程就可以與driver層通信了骨杂,啟動binder主線程 當(dāng)前的進程就可以作為binder進程通信的server端了涂身,可以接收client進程的數(shù)據(jù)了。

不管是server進程還是client進程搓蚪,在driver層都有對應(yīng)的內(nèi)核線程處于中斷狀態(tài)蛤售,在等待事務(wù)(binder_transcation后面會著中介紹),為什么client進程也需要一個等待的內(nèi)核線程呢妒潭?這是因為client在進行進程通信時悴能,只要傳遞給別的進程的數(shù)據(jù)中包含binder(此binder是BBinder,非BpBinder代理類)對象時雳灾,client進程隨時都會變?yōu)閎inder服務(wù)的提供者搜骡。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市佑女,隨后出現(xiàn)的幾起案子记靡,更是在濱河造成了極大的恐慌,老刑警劉巖团驱,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摸吠,死亡現(xiàn)場離奇詭異,居然都是意外死亡嚎花,警方通過查閱死者的電腦和手機寸痢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來紊选,“玉大人啼止,你說我怎么就攤上這事”眨” “怎么了献烦?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卖词。 經(jīng)常有香客問我巩那,道長,這世上最難降的妖魔是什么此蜈? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任即横,我火速辦了婚禮,結(jié)果婚禮上裆赵,老公的妹妹穿的比我還像新娘东囚。我一直安慰自己,他們只是感情好战授,可當(dāng)我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布页藻。 她就那樣靜靜地躺著抛蚁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惕橙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天钉跷,我揣著相機與錄音弥鹦,去河邊找鬼。 笑死爷辙,一個胖子當(dāng)著我的面吹牛彬坏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播膝晾,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼栓始,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了血当?” 一聲冷哼從身側(cè)響起幻赚,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎臊旭,沒想到半個月后落恼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡离熏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年佳谦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滋戳。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡钻蔑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奸鸯,到底是詐尸還是另有隱情咪笑,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布娄涩,位于F島的核電站蒲肋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钝满。R本人自食惡果不足惜兜粘,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望弯蚜。 院中可真熱鬧孔轴,春花似錦、人聲如沸碎捺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晋柱,卻和暖如春优构,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雁竞。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工钦椭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碑诉。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓彪腔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親进栽。 傳聞我的和親對象是個殘疾皇子德挣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,689評論 2 354