Android5.0 vold-啟動(dòng)過程

轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://www.reibang.com/users/183339cdc7ae/latest_articles

概述

該篇文章主要講解vold是如何啟動(dòng)的喉脖,以及內(nèi)部組件間的關(guān)系

相關(guān)文章

Android5.0 vold-整體架構(gòu)
Android5.0 vold-注冊(cè)過程(上)
Android5.0 vold-注冊(cè)過程(下)

啟動(dòng)流程

vold整體架構(gòu)可以知道, vold是個(gè)native程序,并且有三部分組成,首先來看下這個(gè)程序的入口

程序入口

android系統(tǒng)的啟動(dòng)會(huì)先去解析init.rc文件,所以首先來看看init.rc

service vold /system/bin/vold
    class core
    socket vold stream 0660 root mount
    ioprio be 2

從init.rc (/system/core/rootdir/init.rc)里面可以看出薄扁,vold可執(zhí)行文件位置在手機(jī)系統(tǒng)的/system/bin/vold.
那/system/bin/vold這個(gè)可執(zhí)行程序是由哪些c文件編譯而成的,讓我們?cè)賮砜纯此膍k文件,位于/system/vold/Android.mk

LOCAL_SRC_FILES := \
    main.cpp \
    $(common_src_files)
LOCAL_MODULE := libvold

這樣就根據(jù)init.rc和Android.mk文件找到vold的入口是在源碼目錄system/vold/main.cpp里面

對(duì)象實(shí)例化

首先來看看main.cpp的main函數(shù)

int main() {
   ...   
  /* Create our singleton managers */   
  if (!(vm = VolumeManager::Instance())) {       
      SLOGE("Unable to create VolumeManager");     
      exit(1);   
  };
    
  if (!(nm = NetlinkManager::Instance())) {       
     SLOGE("Unable to create NetlinkManager");       
     exit(1);    
  };

  cl = new CommandListener();
  vm->setBroadcaster((SocketListener *) cl);    
  nm->setBroadcaster((SocketListener *) cl);
   
  if (vm->start()) {
       SLOGE("Unable to start VolumeManager (%s)", strerror(errno));       
       exit(1);   
  }
    
  if (process_config(vm)) {       
      SLOGE( "Error reading configuration (%s)... continuing anyways",strerror(errno));    
  }
  ...  
  if (nm->start()) {       
      SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
      exit(1);   
  }
  ... 
  if (cl->startListener()) {       
    SLOGE("Unable to start CommandListener (%s)", strerror(errno));       
    exit(1);   
  }
}

看Instance方法名就可以得知貌踏,vm和nm使用了單例模式實(shí)例化了對(duì)象,跳過他們窟勃,來看看new CommandListener做了些什么事情.
在new CommandListener的時(shí)候祖乳,這里會(huì)先調(diào)用父類FrameworkListener的構(gòu)造方法,然后在注冊(cè)需要監(jiān)聽的命令(registerCmd)秉氧,如果沒有注冊(cè)眷昆,systemserver發(fā)過來的命令是無法獲取的

CommandListener::CommandListener():FrameworkListener("vold", true) {    
    registerCmd(new DumpCmd());    
    ...  
    registerCmd(new FstrimCmd());
};

因?yàn)镕rameworkListener又是繼承自SocketListener,再來看看SocketListener的構(gòu)造方法

FrameworkListener::FrameworkListener(constchar*socketName, boolwithSeq) : SocketListener(socketName, true, withSeq) {    
       init(socketName, withSeq);}

SocketListener::SocketListener(constchar*socketName, boollisten{    
       init(socketName, -1, listen, false);
}

init函數(shù)并沒有做實(shí)際的事情,只是實(shí)例化了一些對(duì)象以備后面使用
接著往后看亚斋,查看源碼可以發(fā)現(xiàn)垦藏,vm->start()并沒有做什么事情,而是直接返回了0.
那我們來看看process_config做了什么事情

加載配置

process_config函數(shù)如下

#define FSTAB_PREFIX "/fstab."
static int process_config(VolumeManager *vm) {  
    ...    
    property_get("ro.hardware", propbuf, "");    
    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);    
    fstab = fs_mgr_read_fstab(fstab_filename);  
      ... 
    /* Loop through entries looking for ones that vold manages */    
    for(i = 0; i < fstab->num_entries; i++) {       
          if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {           
             DirectVolume *dv = NULL;           
               ...            
              vm->addVolume(dv);        
          }    
    }
}

通過getprop命令可以知道ro.hardware的值為qcom,所以這里fstab_filename為fstab.qcom(注:這里可能根據(jù)不同的廠商會(huì)有不同的fstab文件)伞访。
通過調(diào)用fs_mgr_read_fstab函數(shù)掂骏,得到一個(gè)fatab的結(jié)構(gòu)體。然后遍歷fstab的每一行厚掷,把每一個(gè)掛載分區(qū)實(shí)例化一個(gè)DirectVolume對(duì)象 然后加入到VolumeManager的管理中去弟灼。
來看看fstab長(zhǎng)什么樣子

# Android fstab file.
#<src>                                                  <mnt_point>         <type>    <mnt_flags and options>                              <fs_mgr_flags>
# The filesystem that contains the filesystem checker binary (typically /system) cannot
# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK
/dev/block/mtdblock0                                    /system             ext4      ro,barrier=1                                         wait
/dev/block/mtdblock1                                    /data               ext4      noatime,nosuid,nodev,barrier=1,nomblk_io_submit      wait,check
/dev/block/mtdblock2                                    /cache              ext4      noatime,nosuid,nodev  wait,check
/devices/platform/goldfish_mmc.0                        auto                vfat      defaults                                             voldmanaged=sdcard:auto

開始監(jiān)聽

VolumeManager

經(jīng)過前面一系列的操作,該實(shí)例化的對(duì)象已經(jīng)實(shí)例化好了冒黑,需要做的準(zhǔn)備工作也已經(jīng)完成了田绑,現(xiàn)在是萬事俱備只欠東風(fēng)了。
我們來看下開啟整個(gè)系統(tǒng)運(yùn)作的按鈕cl->startListener, startListener的實(shí)現(xiàn)是在父類SocketListener里面

int SocketListener::startListener(intbacklog) {   
     ... 
    if ((mSock = android_get_control_socket(mSocketName)) < 0) {    
          ...    
          if (mListen && listen(mSock, backlog) < 0) {   
               ...    
               if(pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {   
                        ...
               }
         }
   }
}

這里mSocketName的值為vold抡爹,也就是前面FrameworkListener中傳進(jìn)來的參數(shù)通過調(diào)用android_get_control_socket函數(shù)產(chǎn)生一個(gè)socket文件掩驱,并且調(diào)用listen方法監(jiān)聽,這里是基本的socket通信的寫法冬竟。 然后啟用了一個(gè)線程pthread_create欧穴,并且執(zhí)行了threadStart方法,threadStart方法也只是執(zhí)行了runListener方法而已

void SocketListener::runListener() {    
    while(1) {
          ...        
          int fd = (*it)->getSocket();       
          ...
          if((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0 {            
                ...            
               sleep(1);           
                 continue;        
          } else if (!rc)        ...          
               c = accept(mSock, &addr, &alen);        
               ...
               mClients->push_back(newSocketClient(c, true,mUseCmdNum));       
         }        
                ...          
                SocketClient* c = *it;       
                ...
                if(!onDataAvailable(c)) {
                    release(c, false);          
                }
}

這里就是等待socket的寫端寫入數(shù)據(jù)泵殴,然后讀出相應(yīng)的數(shù)據(jù)(eg 之前通過registerCmd注冊(cè)的一些命令)做相應(yīng)的操作這個(gè)方法很簡(jiǎn)單涮帘,當(dāng)寫入端有數(shù)據(jù)寫入的時(shí)候,accept方法會(huì)得到寫入的數(shù)據(jù)笑诅,封裝為一個(gè)SocketClient對(duì)象然后調(diào)用onDataAvailable方法來處理得到的數(shù)據(jù)值得一提的是為了達(dá)到節(jié)省CPU的效果调缨,這里使用了select方法,該方法會(huì)使線程阻塞住吆你,直到read_fds有數(shù)據(jù)的時(shí)候才喚醒.

onDataAvailable的實(shí)現(xiàn)是在子類FrameworkListener中弦叶,最終會(huì)調(diào)用dispatchCommand方法
該方法又最終會(huì)調(diào)用之前注冊(cè)的命令的runCommand方法這里
vold的VolumeManager大致流程)已經(jīng)講完了

NetlinkManager

NetlinkManager和VolumeManager十分相似,都只是做了一個(gè)監(jiān)聽的動(dòng)作妇多,我們來看看實(shí)現(xiàn)伤哺,從它的start方法看起還是使用了socket方法建立一個(gè)socket,bind一個(gè)地址,在實(shí)例化一個(gè)NetlinkHandler對(duì)象

int NetlinkManager::start() {    
      ... 
       if((mSock = socket(PF_NETLINK, SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0){    
        ...    
          if (setsockopt (mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {    ...    
          if (setsockopt (mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {    ...    
          if (bind (mSock, (structsockaddr *) &nladdr, sizeof(nladdr)) < 0) {   
               ...
              mHandler = new NetlinkHandler(mSock);   
               if(mHandler->start()) {   
               ...
}

查看代碼可以發(fā)現(xiàn)NetlinkHandler繼承自NetlinkHandler砌梆,而NetlinkHandler又是繼承自SocketListener的默责。這和VolumeManager的結(jié)構(gòu)十分相似

NetlinkHandler::NetlinkHandler(intlistenerSocket) : NetlinkListener(listenerSocket){
}
NetlinkListener::NetlinkListener(intsocket) : SocketListener(socket, false) {    
      mFormat = NETLINK_FORMAT_ASCII;
}

NetlinkHandler的start方法最終會(huì)調(diào)用到SocketListener的startListener方法
這個(gè)方法在之前已經(jīng)做過介紹,這里不在講解咸包,主要作用就是監(jiān)聽socket端口桃序,當(dāng)有信息的時(shí)候調(diào)用onDataAvailable方法
這里會(huì)將mBuffer的值封裝為一個(gè)NetlinkEvent對(duì)象,然后調(diào)用onEvent
onEvent最終會(huì)調(diào)用到VolumeManager的handleBlockEvent方法

bool NetlinkListener::onDataAvailable(SocketClient *cli){   
     ...
     count =TEMP_FAILURE_RETRY(
                      uevent_kernel_multicast_uid_recv(  socket, mBuffer, sizeof(mBuffer), &uid));    
    ...    
    NetlinkEvent *evt = newNetlinkEvent();    
    if (evt->decode(mBuffer, count, mFormat)){       
           onEvent(evt);   
     }   
     ...
}

voidNetlinkHandler::onEvent(NetlinkEvent *evt) {   
     VolumeManager *vm = VolumeManager::Instance();    
      ...   
     vm->handleBlockEvent(evt);
}

我們來看看VolumeManager的handleBlockEvent方法
這里遍歷了mVolumes烂瘫,分別調(diào)用每個(gè)volume對(duì)象的handleBlockEvent方法
前面已經(jīng)講過process_config方法會(huì)解析fstab.qcom文件媒熊,并調(diào)用VolumeManager的addVolume添加volume
每一個(gè)volume都是DirectVolume奇适,所以最后調(diào)用到DirectVolume的handleBlockEvent方法

static int process_config (VolumeManager *vm){ 
     ...  
     DirectVolume *dv = NULL;  
     ...  
     vm->addVolume(dv);
}
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {   
     ... 
     for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {       
          if(!(*it)->handleBlockEvent(evt)) {   
              ..
          }
      }
}

handleBlockEvent會(huì)根據(jù)kernel傳遞過來的NetlinkEvent中的action做出相應(yīng)的動(dòng)作然后調(diào)用sendBroadcast通知上層kernel發(fā)出的信號(hào)

int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
    ...
    int action = evt->getAction();
    ...
    if (action == NetlinkEvent::NlActionAdd) {
          ...
    if (!strcmp(devtype, "disk")) {
        handleDiskAdded(dp, evt);
    } else {
        handlePartitionAdded(dp, evt);
    }s
    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,msg, false);
      ...
    } else if (action == NetlinkEvent::NlActionRemove) {
        ...
    }else  if (action == NetlinkEvent::NlActionChange) {
    ...
   }
}

流程圖

一圖勝千言,上文中有關(guān)鍵的代碼流程芦鳍,可以結(jié)合流程圖來看才能有一個(gè)整體的概念


start_brief.jpeg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嚷往,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子柠衅,更是在濱河造成了極大的恐慌皮仁,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菲宴,死亡現(xiàn)場(chǎng)離奇詭異贷祈,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)喝峦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門势誊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谣蠢,你說我怎么就攤上這事粟耻。” “怎么了眉踱?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵挤忙,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我勋锤,道長(zhǎng)饭玲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任叁执,我火速辦了婚禮,結(jié)果婚禮上矮冬,老公的妹妹穿的比我還像新娘谈宛。我一直安慰自己,他們只是感情好胎署,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布吆录。 她就那樣靜靜地躺著,像睡著了一般琼牧。 火紅的嫁衣襯著肌膚如雪恢筝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天巨坊,我揣著相機(jī)與錄音撬槽,去河邊找鬼。 笑死趾撵,一個(gè)胖子當(dāng)著我的面吹牛侄柔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼暂题,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼移剪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起薪者,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤纵苛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后言津,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赶站,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年纺念,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贝椿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陷谱,死狀恐怖烙博,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情烟逊,我是刑警寧澤渣窜,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站宪躯,受9級(jí)特大地震影響乔宿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜访雪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一详瑞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧臣缀,春花似錦坝橡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至脂倦,卻和暖如春番宁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赖阻。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工蝶押, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人政供。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓播聪,卻偏偏與公主長(zhǎng)得像朽基,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子离陶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容