解决方案

android vold初始化及sd卡挂载流程(转载)

seo靠我 2023-09-25 04:05:42

1 总体介绍

在Android 中,当SD卡插入系统之后,系统会自动挂载。Vold 就是负责挂载SD卡的,vold 的全称是volume daemon。实际上是负责完成系统的CDROM,USB 大容量存SEO靠我储,MMC 卡等扩展存储的挂载任务自动完成的守护进程。它提供的主要特点是支持这些存储外设的热插拔。

1.1总体流程图

Ø         绿色箭头:表示插入SD卡后事件传递以及SD卡挂载

Ø        SEO靠我 红色箭头:表示挂载成功后的消息传递流程

Ø         黄色箭头:表示MountService发出挂载/卸载SD卡的命令

1.2总体类图

n         main.cpp,vold的入口函数,系统SEO靠我起来会只执行vold的可执行文件,调到这个main函数中。

n         NetlinkManager.cpp位于源码位置/system/vold/NetlinkManager.cpp。该类的主要SEO靠我通过引用NetlinkHandler类中的onEvent()方法来接收来自内核的事件消息,NetlinkHandler位于/system/vold/NetlinkHandler.cpp。

n      SEO靠我   VolumeManager:位于源码位置/system/vold/VolumeManager.cpp。该类的主要作用是接收经过NetlinkManager处理过后的事件消息。

n         SEO靠我DirectVolume:位于/system/vold/DirectVolume.cpp。该类的是一个工具类,主要负责对传入的事件进行进一步的处理,block事件又可以分为:Add,Removed,CSEO靠我hange,Noaction这四种。

n             Volume:Volume.cpp位于/system/vold/Volume.cpp,该类是负责SD卡挂载的主要类。Volume.cppSEO靠我主要负责检查SD卡格式,以及对复合要求的SD卡进行挂载,并通过Socket将消息SD卡挂载的消息传递给NativeDaemonConnector。

总的讲,vold程序需要分层三部分,第一部分为NetlSEO靠我inkManager,管理接受来自kernel的UEvent消息,第二部分为VolumeManager,主要负责处理来自NetlinkManager的消息和来自java层的消息,之后真正的挂载卸载动作SEO靠我就需要volume负责了。

2 初始化流程

2.1 时序图

2.2 代码分析

在Android 系统启动的时候,init进程会去解析init.rc文件,在该文件中,有如下代码:

service vold /sySEO靠我stem/bin/vold

    class core

    socket vold stream 0660 root mount

    ioprio be 2

定义了一个vold的service,去执行vold程序,并创建了SEO靠我一个名字为vold的socket,init进程解析完后就去执行vold程序,创建与java层通信的Socket。

在Android 源码/system/vold路径下的main.cpp,这个就是voldSEO靠我程序的入口,我们看看起main函数,代码如下:

int main() {  VolumeManager *vm;  CommandListener *cl;  NetlinkManager *nm;  SEO靠我if (!(vm = VolumeManager::Instance())) {//创建VolumeManager实例  };  if (!(nm = NetlinkManager::InstanceSEO靠我())) {//创建NelinkManager实例  };  cl = new CommandListener(); //创建与java层socket通信的接口  vm->setBroadcasterSEO靠我((SocketListener *) cl);  nm->setBroadcaster((SocketListener *) cl);  if (vm->start()) { //什么都没做  } SEO靠我 if (process_config(vm)) {//初始化fstab  SLOGE("Error reading configuration (%s)... continuing anyways"SEO靠我, strerror(errno));  }  if (nm->start()) {//开始监听kernel上报的vold消息   }   ……  if (cl->stSEO靠我artListener()) {//开始监听来自java层的socket消息  SLOGE("Unable to start CommandListener (%s)", strerror(errnoSEO靠我));  exit(1);  }  while(1) {  sleep(1000);  }  exit(0);   }

首先,在main函数中,需要创建VolumeManager和NetSEO靠我linkManager的实例,里面就做了一些初始化的动作,这里就不多说了。

接着,则是初始化vold与java层的socket通信接口。创建了的CommandListener实例。在上面的类图关系中,我SEO靠我们知道,CommandListener继承于FrameworkListener,而FrameworkListener有继承于SocketListener。先看看CommandListener的初始化代SEO靠我码:

CommandListener::CommandListener() :                   FrameworkListener("vold") {      registerCmSEO靠我d(new DumpCmd());      registerCmd(new VolumeCmd()); //处理volume事件      registerCmd(new AsecCmd());  SEO靠我    registerCmd(new ObbCmd());      registerCmd(new StorageCmd());      registerCmd(new XwarpCmd());SEO靠我      registerCmd(new CryptfsCmd());  }

在上面代码中我们看到,先以“vold”为参数构造FrameworkListener类,完成之后,则调用FrameworkLSEO靠我istener类中的registerCmd()方法,注册一些处理方法类,而对于sd卡挂载的事件,我们先关注VolumeCmd类,它是FrameworkListener的内部类,用于处理Volume事件SEO靠我。接下来,看FrameworkListener的构造函数:

FrameworkListener::FrameworkListener(const char *socketName) :         SEO靠我                     SocketListener (socketName, true) {      mCommands = new FrameworkCommandCollecSEO靠我tion();  }

以之前传进来的“vold”参数构造SocketListener类,然后在FrameworkListener构造函数中,创建FrameworkCommandCollection的实例SEO靠我,其实它就是一个容器,用于存储之前调用的registerCmd()注册的处理方法类。下面就看SocketListener的构造函数:

SocketListener::SocketListener(conSEO靠我st char *socketName, bool listen) {      mListen = listen;      mSocketName = socketName; /将vold字符串存SEO靠我储在mSocketName变量中      mSock = -1;      pthread_mutex_init(&mClientsLock, NULL);      mClients = new SEO靠我SocketClientCollection(); //创建socket客户端容器  }

其实很简单,就是做了一些变量的初始化工作,用mSocketName变量存储“vold”字符串,这个vold是很有SEO靠我讲究的,因为是init.rc定义的vold Service中,就创建了一个名字为vold的socket端口,后面将通过“vold”获取到该 socket端口。

到此,CommandListener的初始SEO靠我化就完成了的。

我们回到main函数中,创建了CommandListener实例之后,然后调用VolumeManger的setBroadcaster方法,将CommandListener的实例存储在mBSEO靠我roadcaster变量中,代码如下:

void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }

其实NetlinkManager也做了SEO靠我同样的设置,但我还没发现它有什么用,所以就不再关注了。

接下来就开始调用了main.cpp的process_config()方法了,在介绍之前,我必须先介绍下vold.fstab配置文件,这个配置文件就SEO靠我是在process_config()中被解析的,而vold.fstab配置文件,就是用于描述vold的挂载动作的,其配置例子如下:

dev_mount        sdcard         /mnSEO靠我t/sdcard         auto     /devices/platform/goldfish_mmc.0

挂载命令            标签           挂载点          SEO靠我    子分区个数               挂载路径

我们就以上面例子来说明,意思就是将/devices/platform/goldfish_mmc.0挂载到/mnt/sdcard中,/deviceSEO靠我s/platform/goldfish_mmc.0可以认为是kernel上报上来的路径。子分区个数如果为auto则表示只有1个子分区,也可以为任何不为0的整数。如果vold.fstab解析无误,VolSEO靠我ueManager将创建DirectVolume。

好了,下面可以看 process_config()方法了,代码如下:

[cpp] view plaincopy

static int process_conSEO靠我fig(VolumeManager *vm) {      FILE *fp;      int n = 0;      char line[255];      if (!(fp = fopen("SEO靠我/etc/vold.fstab", "r"))) {          return -1;      }      while(fgets(line, sizeof(line), fp)) {   SEO靠我       const char *delim = " \t";          char *save_ptr;          char *type, *label, *mount_pointSEO靠我, *mount_flags, *sysfs_path;          int flags;          n++;          line[strlen(line)-1] = \0;  SEO靠我        if (line[0] == # || line[0] == \0)              continue;          if (!(type = strtok_r(linSEO靠我e, delim, &save_ptr))) {              goto out_syntax;          }          if (!(label = strtok_r(NUSEO靠我LL, delim, &save_ptr))) {              goto out_syntax;          }          if (!(mount_point = strtSEO靠我ok_r(NULL, delim, &save_ptr))) {              goto out_syntax;          }          if (!strcmp(type,SEO靠我 "dev_mount")) {              DirectVolume *dv = NULL;              char *part;              if (!(pSEO靠我art = strtok_r(NULL, delim, &save_ptr))) {                  goto out_syntax;              }         SEO靠我     if (strcmp(part, "auto") && atoi(part) == 0) {                  goto out_syntax;              }SEO靠我              if (!strcmp(part, "auto")) {//如果解析没有错,那么就将创建DirectVolume                  dv = new DirSEO靠我ectVolume(vm, label, mount_point, -1);              } else {                  dv = new DirectVolume(SEO靠我vm, label, mount_point, atoi(part));              }              while ((sysfs_path = strtok_r(NULL,SEO靠我 delim, &save_ptr))) {                  if (*sysfs_path != /) {                      break;         SEO靠我         }                  if (dv->addPath(sysfs_path)) {                      goto out_fail;      SEO靠我            }              }              if (sysfs_path)                  flags = parse_mount_flagsSEO靠我(sysfs_path);              else                  flags = 0;              dv->setFlags(flags);       SEO靠我       vm->addVolume(dv); //将创建的DirectVolume添加到VolumeManager中。          } else if (!strcmp(type, "maSEO靠我p_mount")) {          } else {          }      }      fclose(fp);      return 0;  }

该方法,通过一个wihle方法,逐SEO靠我行进行解析,如果认为合理,那么将拿到的信息用于创建DirectVolume实例,然后调用VolumeManager的addVolume方法,存储在mVolumes变量中。

好了,下面就开始看注册监听keSEO靠我rnel的sockect端口了。就是NetLinkManager的start方法,代码如下:

int NetlinkManager::start() {

struct sockaddr_nl nladdrSEO靠我;

    int sz = 64 * 1024;

    int on = 1;

    memset(&nladdr, 0, sizeof(nladdr));

    nladdr.nl_family = AF_NETLINK;

nladSEO靠我dr.nl_pid = getpid();

    nladdr.nl_groups = 0xffffffff;

if ((mSock = socket(PF_NETLINK,//创建socket,返回文件描述符SEO靠我

                        SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {

SLOGE("Unable to create uevent socket: %s", strerror(errnoSEO靠我));

        return -1;

    }

    if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {

SLOGE("UnableSEO靠我 to set uevent socket SO_RECBUFFORCE option: %s", strerror(errno));

        return -1;

    }

if (setsockopt(mSock, SEO靠我SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {

SLOGE("Unable to set uevent socket SO_PASSCRED optioSEO靠我n: %s", strerror(errno));

        return -1;

    }

if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0SEO靠我) {

        SLOGE("Unable to bind uevent socket: %s", strerror(errno));

        return -1;

    }

mHandler = new NetlinkHandlSEO靠我er(mSock);

    if (mHandler->start()) {

        return -1;

    }

    return 0;

}

其实就是调用socket()创建socket端口,返回描述符,经过一些设置,然后就描述符作SEO靠我为参数,创建的NetlinkHandler实例,然后就直接调用起start方法。看NetLinkHandler构造函数:

NetlinkHandler::NetlinkHandler(int listeSEO靠我nerSocket) :

                NetlinkListener(listenerSocket) {

}

构造函数里什么都没做,NetlinkHandler继承于NetlinkListener,然后讲socket端SEO靠我口的描述符传进去。

NetlinkListener::NetlinkListener(int socket) :

                            SocketListener(socket, false) {

mFormat = NETLSEO靠我INK_FORMAT_ASCII;

}

又是这么几句代码,NetlinkListener也是继承于SocketListener,所以还将socket描述符传进去,再次创建了SocketListener的实SEO靠我例,所以,在vold系统中,有两个SocketListener的实例。看其构造函数,这里的构造函数与之前的是不一样的,代码如下:

SocketListener::SocketListener(int sSEO靠我ocketFd, bool listen) {

    mListen = listen;

    mSocketName = NULL;

    mSock = socketFd;

pthread_mutex_init(&mCliSEO靠我entsLock, NULL);

    mClients = new SocketClientCollection();

}

其实,与上面的构造函数,还是差不多的,只是传进来的参数不一样而已,之前的是一个“volSEO靠我d”字符串,而这里是一个socket的描述符。

好了,构造函数创建好了,那么接着看NetlinkHandler->start()方法:

int NetlinkHandler::start() {

returSEO靠我n this->startListener();//指到了socketListener中了

}

这里的startListener方法是SocketListener中的,代码如下:

int SocketLisSEO靠我tener::startListener() {

    if (!mSocketName && mSock == -1) {

        return -1;

    } else if (mSocketName) {

if ((mSSEO靠我ock = android_get_control_socket(mSocketName)) < 0) {

            return -1;

        }

    }

if (mListen && listen(mSock, 4) < 0SEO靠我) {

        return -1;

    } else if (!mListen)

mClients->push_back(new SocketClient(mSock, false));//创建socket客户端,并SEO靠我添加到mClients容器中。

    if (pipe(mCtrlPipe)) {

        return -1;

    }

if (pthread_create(&mThread, NULL, SocketListener::tSEO靠我hreadStart, this)) {//创建新线程

        return -1;

    }

    return 0;

}

此时的条件下,mSocketName=null,mSock!=0,继续往下看,创建了SocketClieSEO靠我nt实例,并添加到mClients容器中,用于接收客户端发过来的消息。

接着创建新的一个线程,用于读取socket客户端发过来的消息,线程执行的方法如下:

void *SocketListener::thSEO靠我readStart(void *obj) {

    SocketListener *me = reinterpret_cast<SocketListener *>(obj);

me->runListener()SEO靠我;

    pthread_exit(NULL);

    return NULL;

}

看runListener()方法:

void SocketListener::runListener() {

SocketClientCoSEO靠我llection *pendingList = new SocketClientCollection();

    while(1) {

        ……

for (it = mClients->begin(); it != SEO靠我mClients->end(); ++it) {

            int fd = (*it)->getSocket();

            if (FD_ISSET(fd, &read_fds)) {

pendingList->push_SEO靠我back(*it);

            }

        }

        pthread_mutex_unlock(&mClientsLock);

        while (!pendingList->empty()) {//客户端有消息

it = pendingLSEO靠我ist->begin();

            SocketClient* c = *it;

            pendingList->erase(it);

if (!onDataAvailable(c) && mListen) {//  处SEO靠我理消息

            }

        }

    }

    delete pendingList;

}

在该方法中,一个while循环,不断读取socket消息,如果发现有socket消息,那么就调用方法onDataAvailable处理,该方法是在NSEO靠我etlinkListener方法实现的,其代码如下:

bool NetlinkListener::onDataAvailable(SocketClient *cli)

{

int socket = cli-SEO靠我>getSocket();

    ssize_t count;

count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_recv(socket, mBuffer, SEO靠我sizeof(mBuffer)));

    if (count < 0) {

        SLOGE("recvmsg failed (%s)", strerror(errno));

        return false;

    }

NetlinSEO靠我kEvent *evt = new NetlinkEvent();

    if (!evt->decode(mBuffer, count, mFormat)) {

SLOGE("Error decoding NSEO靠我etlinkEvent");

    } else {

        onEvent(evt);//在NetlinkHandler被实现

    }

    return true;

就是经过了处理,跳转到了NetlinkHandler的onEveSEO靠我nt()方法处理。好了,注册kernel监听就到此先搞一段落了。

回到了main函数中,最后,看到调用了CommandListener->startListener(),其实就是调用了SocketLisSEO靠我tener中的startListener方法。代码就不再次贴出来了,同样也是创建了一个新的线程读取socket消息,只是,发现有消息后,调用的是FrameworkListener中的onDataAvaSEO靠我ilable方法处理。

好了,到此,vold的初始化已经完成了。下面看看sd的mount流程吧。

3 SD卡mount流程

3.1时序图

3.2 流程图

3.3 代码分析

经过前面的介绍,我们知道了,在NetliSEO靠我nkHandler的onEvent方法中,收到了kernel的消息。其代码如下:

void NetlinkHandler::onEvent(NetlinkEvent *evt) {

VolumeManagSEO靠我er *vm = VolumeManager::Instance();

    const char *subsys = evt->getSubsystem();

    if (!subsys) {

        return;

    }

ifSEO靠我 (!strcmp(subsys, "block")) {

        vm->handleBlockEvent(evt);//进一步处理

    }

}

这里就只处理block消息了,看看VolumeManager的handlSEO靠我eBlockEvent方法吧:

void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {

const char *devpath = evt->fSEO靠我indParam("DEVPATH");

    VolumeCollection::iterator it;

    bool hit = false;

for (it = mVolumes->begin(); it !SEO靠我= mVolumes->end(); ++it) {

        if (!(*it)->handleBlockEvent(evt)) {//到DirectVolume处理

            hit = true;

            break;

        }

    }

}

SEO靠我里的for循环遍历mVolumes,它其实是DirectVolume实例列表,在解析vold.fstab中,创建的DirectVolume实例并添加到mVolumes列表中。然后再调用DirectVoSEO靠我lume的handleBlockEvent方法尝试处理该消息,看是否能匹配,起代码如下:

int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {

cSEO靠我onst char *dp = evt->findParam("DEVPATH");

    PathCollection::iterator  it;

for (it = mPaths->begin(); itSEO靠我 != mPaths->end(); ++it) {//遍历vold.fstab定义的路径

if (!strncmp(dp, *it, strlen(*it))) {//kernel上报上来的路径与voSEO靠我ld.fstab中定义的匹配

            int action = evt->getAction();

            const char *devtype = evt->findParam("DEVTYPE");

if (actiSEO靠我on == NetlinkEvent::NlActionAdd) {

                int major = atoi(evt->findParam("MAJOR"));

int minor = atoi(evt->fiSEO靠我ndParam("MINOR"));

                char nodepath[255];

                snprintf(nodepath,

                         sizeof(nodepath), "/dev/block/vold/%d:%d",

majSEO靠我or, minor);

                if (createDeviceNode(nodepath, major, minor)) {

                }

                if (!strcmp(devtype, "disk")) {//插入设备消息

haSEO靠我ndleDiskAdded(dp, evt);//上报一个物理分区

                } else {

                    handlePartitionAdded(dp, evt);//上报一个逻辑分区

                }

} else if (action SEO靠我== NetlinkEvent::NlActionRemove) {//拔出设备消息

                if (!strcmp(devtype, "disk")) {

handleDiskRemoved(dp, evt);SEO靠我

                } else {

                    handlePartitionRemoved(dp, evt);

                }

} else if (action == NetlinkEvent::NlActionChange) {//设备状态改SEO靠我变消息

                if (!strcmp(devtype, "disk")) {

                    handleDiskChanged(dp, evt);

                } else {

handlePartitionChanged(dp, evt)SEO靠我;

                }

            } else {

                    SLOGW("Ignoring non add/remove/change event");

            }

            return 0;

        }

    }

    errno = ENODEV;

    return -1;

}

KernelSEO靠我上报上来的消息中,有一个路径的消息,将与vold.fstab中定义的路径进行匹配,如果匹配,那么说明这个消息是有效的,那么就继续处理。

那么,kernel上报的消息也分为三类,分别是设备插入、拔出、状态SEO靠我改变。我们这里就先关注插入的消息吧。

那么,插入的消息,又分是物理分区还是一个逻辑分区。假如插入一个sd卡,它只有一个分区,那么上报的就是Disk消息。假如插入一个sd卡,该卡有内部又被分成多个分区,那SEO靠我么就先上报的是一个Dist消息,用于描述这个sd卡,后面还会上报多个消息,每个消息对应sd卡中的一个分区,也就是partition消息。

在这里,我们关注Dist消息吧,看看handleDiskAddeSEO靠我d()方法,代码如下;

void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {

mDiskMajor = SEO靠我atoi(evt->findParam("MAJOR"));

    mDiskMinor = atoi(evt->findParam("MINOR"));

const char *tmp = evt->findSEO靠我Param("NPARTS");

    if (tmp) {

        mDiskNumParts = atoi(tmp);//如果上报的是只有一个分区的sd,该变量为0

    } else {

mDiskNumParts = 1SEO靠我;

    }

       mPartsEventCnt = 0;

    char msg[255];

    int partmask = 0;

    int i;

    for (i = 1; i <= mDiskNumParts; i++) {

partSEO靠我mask |= (1 << i);

    }

    mPendingPartMap = partmask;

    if (mDiskNumParts == 0) {

setState(Volume::State_Idle);/SEO靠我/设置初始状态

    } else {

        setState(Volume::State_Pending);

    }

snprintf(msg, sizeof(msg), "Volume %s %s disk insertSEO靠我ed (%d:%d)",

             getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);//构造消息

mVm->getBroadcaster()->sendBrSEO靠我oadcast(ResponseCode::VolumeDiskInserted,

                                             msg, false);//发socket消息到java层

}

如果是Disk消息,那么上报的sd卡只有一个分区,所以上面SEO靠我的mDiskNumParts=0。看下面,调用snprintf()构造msg消息,然后调用mVm->getBroadcaster()->sendBroadcast发送到java层。其实mVm->getSEO靠我Broadcaster()就是放回CommandListener的实例变量,sendBroadcast就是在SocketListener中,代码如下:

void SocketListener::sendSEO靠我Broadcast(const char *msg) {

    pthread_mutex_lock(&mClientsLock);

    SocketClientCollection::iterator i;

forSEO靠我 (i = mClients->begin(); i != mClients->end(); ++i) {

        if ((*i)->sendMsg(msg)) {//发送socket消息

        }

    }

pthread_SEO靠我mutex_unlock(&mClientsLock);

}

Ok,看到了吧,这里就发送了一个VolumeDiskInserted的消息到java层。但如果是系统改启动的话,kernel早早就发来了消息,SEO靠我但是java层还没起来呢。所以,等到mountService起来之后,就收到了socket消息了。

我们直接看mountService的onEvent()方法吧代码如下:

public boolean oSEO靠我nEvent(int code, String raw, String[] cooked) {

   …….

if (code == VoldResponseCode.VolumeDiskInserted) {SEO靠我

   new Thread() {

    public void run() {

           try {

             int rc;

if ((rc = doMountVolume(path)) != StorageResultCode.OpeSEO靠我rationSucceeded) {

                      Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));

                            }

} catch (Exception SEO靠我ex) {

                      }

                    }

                }.start();

            } else if (code == VoldResponseCode.VolumeDiskRemoved) {

             }

}

这里我们只看onEvent的处理VoldRespoSEO靠我nseCode.VolumeDiskInserted消息,我们看到,对于VolumeDiskInserted消息,mountService立刻调用了方法doMountVolume(path),其实就是SEO靠我通过socket对vold发送了一个条mount的命令。

所以对与java层来讲,可以发mount、unmount消息到vold中。那么现在,就看vold处理吧。

前面也介绍过,java层发送的sockeSEO靠我t消息,vold层在SocketListener中读取到,然后会在FrameworkListener的onDataAvailable()方法中处理,代码如下:

bool FrameworkListeneSEO靠我r::onDataAvailable(SocketClient *c) {

    char buffer[255];

    int len;

len = TEMP_FAILURE_RETRY(read(c->getSoSEO靠我cket(), buffer, sizeof(buffer)));

    if (len < 0) {

        return false;

    } else if (!len)

        return false;

int offset SEO靠我= 0;

    int i;

    for (i = 0; i < len; i++) {

        if (buffer[i] == \0) {

dispatchCommand(c, buffer + offset);//开始派SEO靠我发消息

            offset = i + 1;

        }

    }

    return true;

}

调用dispatchCommand()派发消息了,代码如下:

void FrameworkListener::dispatchCommaSEO靠我nd(SocketClient *cli, char *data) {

    ……

    for (i = mCommands->begin(); i != mCommands->end(); ++i) {

FrameSEO靠我workCommand *c = *i;

        if (!strcmp(argv[0], c->getCommand())) {

            if (c->runCommand(cli, argc, argv)) {

SLOSEO靠我GW("Handler %s error (%s)", c->getCommand(), strerror(errno));

            }

    }

}

一堆的处理,代码也就不贴出来了,直接看关键的部分吧。记得在CommanSEO靠我dListener的构造函数中吗,里面调用了FrameworkListener的registerCmd()方法,注册了一些处理方法类,其实就是添加到了mCommands容器中了,这里当然需要遍历咯,找SEO靠我到其合适的处理方法类,然后调用其runComand()方法,看看其代码吧:

int CommandListener::VolumeCmd::runCommand(SocketClient *cli,

inSEO靠我t argc, char **argv) {

    dumpArgs(argc, argv, -1);

    …….

    VolumeManager *vm = VolumeManager::Instance();

int SEO靠我rc = 0;

    if (!strcmp(argv[1], "list")) {

        return vm->listVolumes(cli);

} else if (!strcmp(argv[1], "debugSEO靠我")) {

    } else if (!strcmp(argv[1], "mount")) {//处理mount消息

        rc = vm->mountVolume(argv[2]);

} else if (!strSEO靠我cmp(argv[1], "unmount")) {

        rc = vm->unmountVolume(argv[2], force, revert);

} else if (!strcmp(argv[1],SEO靠我 "format")) {  //处理格式化消息

        rc = vm->formatVolume(argv[2]);

} else if (!strcmp(argv[1], "share")) { //处理挂SEO靠我载到pc消息

        rc = vm->shareVolume(argv[2], argv[3]);

    } else if (!strcmp(argv[1], "unshare")) {

rc = vm->unshaSEO靠我reVolume(argv[2], argv[3]);

    } else if (!strcmp(argv[1], "shared")) {

        bool enabled = false;

if (vm->sharSEO靠我eEnabled(argv[2], argv[3], &enabled)) {

    }

    return 0;

}

在这里处理Volume消息,我们就只看mount消息吧,就调用了VolumeManager的mounSEO靠我tVolume方法,代码如下:

int VolumeManager::mountVolume(const char *label) {

Volume *v = lookupVolume(label);//SEO靠我找到该挂载点的Volume的实例

    if (!v) {

        return -1;

    }

    return v->mountVol();//去挂载啦

}

到Volume的mountVol()中挂载,代码如下:

int VolumSEO靠我e::mountVol() {

  …..

}

这个方法代码量比较大,就不贴出来了,但是完成mount的动作就是在该方法中,然后呢,Volume中还包含了其他的功能方法,比如unmount、share、unshSEO靠我are。

好了,花了一个下午的时间整理出来,vold的初始化即sd卡的挂载流程就讲解到这吧,我这里讲流程的比较多,很多细节问题也没有讲,其实我写的文档,还是比较注册流程,消息是怎么传递的,至于细节,用到SEO靠我的时候,再详细看!

“SEO靠我”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与 我们联系删除或处理,客服邮箱:html5sh@163.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同 其观点或证实其内容的真实性。

网站备案号:浙ICP备17034767号-2