Android5.0 vold-启动过程

转载请标明出处:http://www.jianshu.com/users/183339cdc7ae/latest_articles

概述

该篇文章主要讲解vold是如何启动的,以及内部组件间的关系

相关文章

Android5.0 vold-整体架构
Android5.0 vold-注册过程(上)
Android5.0 vold-注册过程(下)

启动流程

vold整体架构可以知道, vold是个native程序,并且有三部分组成,首先来看下这个程序的入口

程序入口

android系统的启动会先去解析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可执行文件位置在手机系统的/system/bin/vold.
那/system/bin/vold这个可执行程序是由哪些c文件编译而成的,让我们再来看看它的mk文件,位于/system/vold/Android.mk

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

这样就根据init.rc和Android.mk文件找到vold的入口是在源码目录system/vold/main.cpp里面

对象实例化

首先来看看main.cpp的main函数

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使用了单例模式实例化了对象,跳过他们,来看看new CommandListener做了些什么事情.
在new CommandListener的时候,这里会先调用父类FrameworkListener的构造方法,然后在注册需要监听的命令(registerCmd),如果没有注册,systemserver发过来的命令是无法获取的

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

因为FrameworkListener又是继承自SocketListener,再来看看SocketListener的构造方法

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

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

init函数并没有做实际的事情,只是实例化了一些对象以备后面使用
接着往后看,查看源码可以发现,vm->start()并没有做什么事情,而是直接返回了0.
那我们来看看process_config做了什么事情

加载配置

process_config函数如下

#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(注:这里可能根据不同的厂商会有不同的fstab文件)。
通过调用fs_mgr_read_fstab函数,得到一个fatab的结构体。然后遍历fstab的每一行,把每一个挂载分区实例化一个DirectVolume对象 然后加入到VolumeManager的管理中去。
来看看fstab长什么样子

# 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

开始监听

VolumeManager

经过前面一系列的操作,该实例化的对象已经实例化好了,需要做的准备工作也已经完成了,现在是万事俱备只欠东风了。
我们来看下开启整个系统运作的按钮cl->startListener, startListener的实现是在父类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中传进来的参数通过调用android_get_control_socket函数产生一个socket文件,并且调用listen方法监听,这里是基本的socket通信的写法。 然后启用了一个线程pthread_create,并且执行了threadStart方法,threadStart方法也只是执行了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的写端写入数据,然后读出相应的数据(eg 之前通过registerCmd注册的一些命令)做相应的操作这个方法很简单,当写入端有数据写入的时候,accept方法会得到写入的数据,封装为一个SocketClient对象然后调用onDataAvailable方法来处理得到的数据值得一提的是为了达到节省CPU的效果,这里使用了select方法,该方法会使线程阻塞住,直到read_fds有数据的时候才唤醒.

onDataAvailable的实现是在子类FrameworkListener中,最终会调用dispatchCommand方法
该方法又最终会调用之前注册的命令的runCommand方法这里
vold的VolumeManager大致流程)已经讲完了

NetlinkManager

NetlinkManager和VolumeManager十分相似,都只是做了一个监听的动作,我们来看看实现,从它的start方法看起还是使用了socket方法建立一个socket,bind一个地址,在实例化一个NetlinkHandler对象

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

查看代码可以发现NetlinkHandler继承自NetlinkHandler,而NetlinkHandler又是继承自SocketListener的。这和VolumeManager的结构十分相似

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

NetlinkHandler的start方法最终会调用到SocketListener的startListener方法
这个方法在之前已经做过介绍,这里不在讲解,主要作用就是监听socket端口,当有信息的时候调用onDataAvailable方法
这里会将mBuffer的值封装为一个NetlinkEvent对象,然后调用onEvent
onEvent最终会调用到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,分别调用每个volume对象的handleBlockEvent方法
前面已经讲过process_config方法会解析fstab.qcom文件,并调用VolumeManager的addVolume添加volume
每一个volume都是DirectVolume,所以最后调用到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会根据kernel传递过来的NetlinkEvent中的action做出相应的动作然后调用sendBroadcast通知上层kernel发出的信号

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) {
    ...
   }
}

流程图

一图胜千言,上文中有关键的代码流程,可以结合流程图来看才能有一个整体的概念


start_brief.jpeg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 1:InputChannel提供函数创建底层的Pipe对象 2: 1)客户端需要新建窗口 2)new ViewRo...
    自由人是工程师阅读 5,097评论 0 18
  • JAVA面试题 1、作用域public,private,protected,以及不写时的区别答:区别如下:作用域 ...
    JA尐白阅读 1,124评论 1 0
  • 离婚和吵架哪个对孩子伤害更大 干任何一个工作都有门槛,当父母甚至连面试都不用 01 这个主题,乍一看还特么真有点儿...
    白格姨妈阅读 469评论 1 2
  • 阳光伏过大厦的肩头 不见鸿雁往南方 有身瘦且低矮的青草 迎着风吹的方向 光与影分割未知终点的行路 相互目光倾尽,也...
    凉白唐阅读 624评论 26 22