Android -- Vold机制简要分析


Android -- Vold机制简要分析

Vold是用于管理和控制Android外部存储介质的后台进程,这里说的管控,主要包括SD卡的插拔、挂载/卸载和格式化等;它是Android平台外部存储系统的管控枢纽。
Vold的整个控制模块主要由三个类模块构成:NetlinkManager、VolumeManager和CommandListener,它们的功能划分大概是:


  1. NetlinkManager:用于从kernel中获取SD卡插拔的Uevnet消息
  2. VolumeManager:管理模块,对NetlinkManager转发的消息做一些处理,并通过CommandListener发送给framework(MountService.java);接着framework会通过套接字下发命令,指引VolumeManager对存储设备做下一步的操作,如挂载/卸载等
  3. CommandListener:通过socket,实现MountService.java与Vold之间的消息交换


NetLink是Linux下用户进程和kernel进行信息交互的一种机制,借助这种机制,用户进程(如Vold/Netd)可以接收来自kernel的一些消息,同时也可以向kernel发送一些控制命令。NetlinkManager就是基于此设计的。Uevent也跟Linux系统有关,它与Linux 的设备文件系统有一定关系;这里,我们可以简单的认为,Uevent就是一个字符串,它描述了外部存储设备插入/拔出、挂载/卸载的状态信息。Vold通过Netlink机制,可以得到这些信息,并进行外部存储设备的管理、控制。

由上述介绍,我们可以得到如下的Vold框架图描述:


有了Vold的架构描述,接下来就开始分析Vold进程的整体流程及实现了。

一、Vold进程的声明与创建

Vold进程的声明与创建过程跟zygote一样,在init.rc中声明,在init进程创建:
  1. service vold /system/bin/vold \  
  2.         --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \  
  3.         --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0  
  4.     class core  
  5.     socket vold stream 0660 root mount  
  6.     socket cryptd stream 0660 root mount  
  7.     ioprio be 2  
在创建Vold进程时,会为它创建两个socket,用于与framework进行信息交互。其他的细节,参考之前zygote进程创建的介绍。

二、进入Vold主程序

Vold的主程序在/system/vold目录中,直接看main.cpp::main()函数:
  1. int main(int argc, char** argv) {  
  2.     setenv("ANDROID_LOG_TAGS""*:v", 1);  
  3.     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));  
  4.   
  5.     LOG(INFO) << "Vold 3.0 (the awakening) firing up";  
  6.   
  7.     LOG(VERBOSE) << "Detected support for:"  
  8.             << (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")  
  9.             << (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")  
  10.             << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "");  
  11.   
  12.     VolumeManager *vm;  
  13.     CommandListener *cl;  
  14.     CryptCommandListener *ccl;  
  15.     NetlinkManager *nm;  
  16.   
  17.     parse_args(argc, argv);  
  18.   
  19.     sehandle = selinux_android_file_context_handle();  
  20.     if (sehandle) {  
  21.         selinux_android_set_sehandle(sehandle);  
  22.     }  
  23.   
  24.     // Quickly throw a CLOEXEC on the socket we just inherited from init  
  25.     fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);//拿到init进程创建的名为vold的socket句柄,并为它设置FD_CLOEXEC标志位  
  26.     fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);//同上  
  27.   
  28.     mkdir("/dev/block/vold", 0755);//创建/dev/block/vold目录,存放所有subdisk和sdcard的挂载点信息  
  29.   
  30.     /* For when cryptfs checks and mounts an encrypted filesystem */  
  31.     klog_set_level(6);  
  32.   
  33.     /* Create our singleton managers */  
  34.     //1、创建VolumeManager  
  35.     if (!(vm = VolumeManager::Instance())) {  
  36.         LOG(ERROR) << "Unable to create VolumeManager";  
  37.         exit(1);  
  38.     }  
  39.       
  40.     //2、创建NetlinkManager  
  41.     if (!(nm = NetlinkManager::Instance())) {  
  42.         LOG(ERROR) << "Unable to create NetlinkManager";  
  43.         exit(1);  
  44.     }  
  45.   
  46.     if (property_get_bool("vold.debug"false)) {  
  47.         vm->setDebug(true);  
  48.     }  
  49.       
  50.     //3、创建CommandListener、CryptCommandListener  
  51.     cl = new CommandListener();  
  52.     ccl = new CryptCommandListener();  
  53.     vm->setBroadcaster((SocketListener *) cl);  
  54.     nm->setBroadcaster((SocketListener *) cl);  
  55.   
  56.     //4、启动VolumeManager  
  57.     if (vm->start()) {  
  58.         PLOG(ERROR) << "Unable to start VolumeManager";  
  59.         exit(1);  
  60.     }  
  61.   
  62.     if (process_config(vm)) {  
  63.         PLOG(ERROR) << "Error reading configuration... continuing anyways";  
  64.     }  
  65.   
  66.     //6、启动NetlinkManager,处理来自kernel的usb/sdcard插拔消息  
  67.     if (nm->start()) {  
  68.         PLOG(ERROR) << "Unable to start NetlinkManager";  
  69.         exit(1);  
  70.     }  
  71.     //7、应用层往/sys/block目录下的uevent文件写"add\n"指令,触发kernel向上发送Uevent消息,获取设备的当前信息  
  72.     coldboot("/sys/block");  
  73. //    coldboot("/sys/class/switch");  
  74.   
  75.     /* 
  76.      * Now that we're up, we can respond to commands 
  77.      */  
  78.     //8、启动CommandListener  
  79.     if (cl->startListener()) {  
  80.         PLOG(ERROR) << "Unable to start CommandListener";  
  81.         exit(1);  
  82.     }  
  83.       
  84.     //9、启动CryptCommandListener  
  85.     if (ccl->startListener()) {  
  86.         PLOG(ERROR) << "Unable to start CryptCommandListener";  
  87.         exit(1);  
  88.     }  
  89.   
  90.     // Eventually we'll become the monitoring thread  
  91.     while(1) {  
  92.         sleep(1000);  
  93.     }  
  94.   
  95.     LOG(ERROR) << "Vold exiting";  
  96.     exit(0);  
  97. }  
从代码中的注释可知,Vold主要创了三个对象:NetlinkManager、VolumeManager和CommandListener。根据Vold的架构图,现分别对它们的创建及启动过程进行分析。

(1)、NetlinkManager

主要的处理过程:
  1. nm = NetlinkManager::Instance()
  2. nm->setBroadcaster((SocketListener *) cl)
  3. nm->start()
现按步骤进行分析。

1、NetlinkManager::Instance():
  1. NetlinkManager *NetlinkManager::Instance() {  
  2.     if (!sInstance)  
  3.         sInstance = new NetlinkManager();  
  4.     return sInstance;  
  5. }  
  6.   
  7. NetlinkManager::NetlinkManager() {  
  8.     mBroadcaster = NULL; //type:SocketListener*,用来进行socket通信  
  9. }  
这里使用了单例模式来构建NetlinkManager对象,构造函数中只是简单地初始化了成员变量。
2、NetlinkManager::setBroadcaster():
  1. cl = new CommandListener();  
  2. nm->setBroadcaster((SocketListener *) cl);  
  3. void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }  
setBroadcaster()函数也很简单,为mBroadcast进行赋值。
3、NetlinkManager::start():
  1. int NetlinkManager::start() {  
  2.     struct sockaddr_nl nladdr;  
  3.     int sz = 64 * 1024;  
  4.     int on = 1;  
  5.   
  6.     memset(&nladdr, 0, sizeof(nladdr));  
  7.     nladdr.nl_family = AF_NETLINK;  
  8.     nladdr.nl_pid = getpid();  
  9.     nladdr.nl_groups = 0xffffffff;  
  10.   
  11.     //创建地址族为PF_NETLINK的socket,与Kernel进行通信;也可以为AF_NETLINK.参照Linux Netlink机制资料  
  12.     if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,  
  13.             NETLINK_KOBJECT_UEVENT)) < 0) {//NETLINK_KOBJECT_UEVENT协议:Kernel messages to userspace  
  14.         SLOGE("Unable to create uevent socket: %s", strerror(errno));  
  15.         return -1;  
  16.     }  
  17.   
  18.     //设置套接字  
  19.     if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {  
  20.         SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));  
  21.         goto out;  
  22.     }  
  23.   
  24.     if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {  
  25.         SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));  
  26.         goto out;  
  27.     }  
  28.   
  29.     //为套接字绑定地址  
  30.     if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {  
  31.         SLOGE("Unable to bind uevent socket: %s", strerror(errno));  
  32.         goto out;  
  33.     }  
  34.   
  35.     mHandler = new NetlinkHandler(mSock);//mHandler、mSock都是成员变量.mHandler对象主要保存了套接字的文件描述符,供后续使用  
  36.     if (mHandler->start()) {//startListener通过父类方法,在mSock上监听连接请求  
  37.         SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));  
  38.         goto out;  
  39.     }  
  40.   
  41.     return 0;  
  42.   
  43. out:  
  44.     close(mSock);  
  45.     return -1;  
  46. }  
start()方法中创建了一个句柄值为mSock的套接字,用来和kernel通信;而实际具体的socket信息交互是由NetlinkHandler处理的。
NetlinkHandler的实现有一套继承机制,其实际继承关系如图所示:


按照继承关系,分析它的构建过程:
  1. mHandler = new NetlinkHandler(mSock);  
  2. NetlinkHandler::NetlinkHandler(int listenerSocket) :  
  3.                 NetlinkListener(listenerSocket) {  
  4. }  
  1. /* temporary version until we can get Motorola to update their 
  2.  * ril.so.  Their prebuilt ril.so is using this private class 
  3.  * so changing the NetlinkListener() constructor breaks their ril. 
  4.  */  
  5. NetlinkListener::NetlinkListener(int socket) :  
  6.                             SocketListener(socket, false) {  
  7.     mFormat = NETLINK_FORMAT_ASCII;  
  8. }  
  1. SocketListener::SocketListener(const char *socketName, bool listen) {  
  2.     init(socketName, -1, listen, false);  
  3. }  
  4. void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {  
  5.     mListen = listen;//是否是监听端,这里为false  
  6.     mSocketName = socketName;//保存socket的名字  
  7.     mSock = socketFd;//保存socket的句柄值,与Kernel通信  
  8.     mUseCmdNum = useCmdNum;  
  9.     pthread_mutex_init(&mClientsLock, NULL);  
  10.     mClients = new SocketClientCollection();//集合对象,保存类型SocketClient为的变量;保存了socket通信中的客户端对象  
  11. }  
再看NetlinkHandler::start()方法:
  1. int NetlinkHandler::start() {  
  2.     return this->startListener();  
  3. }  
实际调用的是SocketListener::startListener():
  1. int SocketListener::startListener() {  
  2.     return startListener(4);  
  3. }  
  4.   
  5. int SocketListener::startListener(int backlog) {  
  6.   
  7.     if (!mSocketName && mSock == -1) {  
  8.         SLOGE("Failed to start unbound listener");  
  9.         errno = EINVAL;  
  10.         return -1;  
  11.     } else if (mSocketName) {  
  12.         if ((mSock = android_get_control_socket(mSocketName)) < 0) {  
  13.             SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
  14.                  mSocketName, strerror(errno));  
  15.             return -1;  
  16.         }  
  17.         SLOGV("got mSock = %d for %s", mSock, mSocketName);  
  18.         fcntl(mSock, F_SETFD, FD_CLOEXEC);  
  19.     }  
  20.   
  21.     if (mListen && listen(mSock, backlog) < 0) {//如果mListen为true,则监听该socket;表明此socket应该是服务端  
  22.         SLOGE("Unable to listen on socket (%s)", strerror(errno));  
  23.         return -1;  
  24.     } else if (!mListen)//实际传入的mListen值为false,走此分支  
  25.         mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));//创建一个SocketClient对象,并保存到集合中  
  26.   
  27.     if (pipe(mCtrlPipe)) {  
  28.         SLOGE("pipe failed (%s)", strerror(errno));  
  29.         return -1;  
  30.     }  
  31.   
  32.     if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {//创建一个线程,在其中调用threadStart(),根据mListen值,等待接收来自Kernel的Uevent消息  
  33.         SLOGE("pthread_create (%s)", strerror(errno));  
  34.         return -1;  
  35.     }  
  36.   
  37.     return 0;  
  38. }  
创建一个SocketListener对象,并加入mClients中;随后,创建一个线程,并调用SocketListener::threadStart():
  1. void *SocketListener::threadStart(void *obj) {  
  2.     SocketListener *me = reinterpret_cast<SocketListener *>(obj);  
  3.   
  4.     me->runListener();  
  5.     pthread_exit(NULL);  
  6.     return NULL;  
  7. }  
  8.   
  9. void SocketListener::runListener() {  
  10.   
  11.     SocketClientCollection pendingList;  
  12.   
  13.     while(1) {  
  14.         SocketClientCollection::iterator it;  
  15.         fd_set read_fds;  
  16.         int rc = 0;  
  17.         int max = -1;  
  18.   
  19.         FD_ZERO(&read_fds);  
  20.   
  21.         if (mListen) {//如果我们是服务端,则将该socket的套接字加入到可读监控队列中  
  22.             max = mSock;  
  23.             FD_SET(mSock, &read_fds);  
  24.         }  
  25.   
  26.         FD_SET(mCtrlPipe[0], &read_fds);  
  27.         if (mCtrlPipe[0] > max)  
  28.             max = mCtrlPipe[0];  
  29.   
  30.         pthread_mutex_lock(&mClientsLock);  
  31.         for (it = mClients->begin(); it != mClients->end(); ++it) {  
  32.             // NB: calling out to an other object with mClientsLock held (safe)  
  33.             int fd = (*it)->getSocket();  
  34.             FD_SET(fd, &read_fds);  
  35.             if (fd > max) {  
  36.                 max = fd;  
  37.             }  
  38.         }  
  39.         pthread_mutex_unlock(&mClientsLock);  
  40.         SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);  
  41.         if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {//如果监测到read_fds集合中有socket可读  
  42.             if (errno == EINTR)  
  43.                 continue;  
  44.             SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);  
  45.             sleep(1);  
  46.             continue;  
  47.         } else if (!rc)  
  48.             continue;  
  49.   
  50.         if (FD_ISSET(mCtrlPipe[0], &read_fds)) {  
  51.             char c = CtrlPipe_Shutdown;  
  52.             TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));  
  53.             if (c == CtrlPipe_Shutdown) {  
  54.                 break;  
  55.             }  
  56.             continue;  
  57.         }  
  58.         if (mListen && FD_ISSET(mSock, &read_fds)) {//监听端客户端连接请求  
  59.             struct sockaddr addr;  
  60.             socklen_t alen;  
  61.             int c;  
  62.   
  63.             do {  
  64.                 alen = sizeof(addr);  
  65.                 c = accept(mSock, &addr, &alen);//接受client的连接请求,c是代表client套接字的文件描述符  
  66.                 SLOGV("%s got %d from accept", mSocketName, c);  
  67.             } while (c < 0 && errno == EINTR);  
  68.             if (c < 0) {  
  69.                 SLOGE("accept failed (%s)", strerror(errno));  
  70.                 sleep(1);  
  71.                 continue;  
  72.             }  
  73.             fcntl(c, F_SETFD, FD_CLOEXEC);  
  74.             pthread_mutex_lock(&mClientsLock);  
  75.             mClients->push_back(new SocketClient(c, true, mUseCmdNum));//根据c,创建一个SocketListener对象,并加入到队列中  
  76.             pthread_mutex_unlock(&mClientsLock);  
  77.         }  
  78.   
  79.         /* Add all active clients to the pending list first */  
  80.         pendingList.clear();  
  81.         pthread_mutex_lock(&mClientsLock);  
  82.         for (it = mClients->begin(); it != mClients->end(); ++it) {  
  83.             SocketClient* c = *it;  
  84.             // NB: calling out to an other object with mClientsLock held (safe)  
  85.             int fd = c->getSocket();  
  86.             if (FD_ISSET(fd, &read_fds)) {//遍历所有保存的客户端;如果该socket可读,将该套接字加入到队列中  
  87.                 pendingList.push_back(c);  
  88.                 c->incRef();  
  89.             }  
  90.         }  
  91.         pthread_mutex_unlock(&mClientsLock);  
  92.   
  93.         /* Process the pending list, since it is owned by the thread, 
  94.          * there is no need to lock it */  
  95.         while (!pendingList.empty()) {  
  96.             /* Pop the first item from the list */  
  97.             it = pendingList.begin();  
  98.             SocketClient* c = *it;  
  99.             pendingList.erase(it);  
  100.             /* Process it, if false is returned, remove from list */  
  101.             if (!onDataAvailable(c)) {//客户端收到数据,调用NetlinkListener::onDataAvailable()处理  
  102.                 release(c, false);//数据处理失败,则释放socket资源  
  103.             }  
  104.             c->decRef();  
  105.         }  
  106.     }  
  107. }  
我们初始化NetlinkListener时传入的mListener值为false;上述代码中,会遍历所有保存的客户端socket,如果收到数据,则进行处理。

调用NetlinkListener::onDataAvailable():
  1. bool NetlinkListener::onDataAvailable(SocketClient *cli)  
  2. {  
  3.     int socket = cli->getSocket();  
  4.     ssize_t count;  
  5.     uid_t uid = -1;  
  6.   
  7.     bool require_group = true;  
  8.     if (mFormat == NETLINK_FORMAT_BINARY_UNICAST) {  
  9.         require_group = false;  
  10.     }  
  11.   
  12.     count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,  
  13.             mBuffer, sizeof(mBuffer), require_group, &uid));//从kernel获取Uevent消息,保存到mBuffer中  
  14.     if (count < 0) {  
  15.         if (uid > 0)  
  16.             LOG_EVENT_INT(65537, uid);  
  17.         SLOGE("recvmsg failed (%s)", strerror(errno));  
  18.         return false;//读取失败,则返回false,上层调用则会关闭socket资源  
  19.     }  
  20.   
  21.     NetlinkEvent *evt = new NetlinkEvent();//事件的代码封装  
  22.     if (evt->decode(mBuffer, count, mFormat)) {解析Uevent数据,填充到NetlinkEvent对象中  
  23.         onEvent(evt);//NetlinkHandler::onEvent()  
  24.     } else if (mFormat != NETLINK_FORMAT_BINARY) {  
  25.         // Don't complain if parseBinaryNetlinkMessage returns false. That can  
  26.         // just mean that the buffer contained no messages we're interested in.  
  27.         SLOGE("Error decoding NetlinkEvent");  
  28.     }  
  29.   
  30.     delete evt;  
  31.     return true;  
  32. }  
先通过socket获取到Uevent数据,再解析并将信息封装到NetlinkEvent对象中。NetlinkHandler::onEvent()分发处理该对象:
  1. void NetlinkHandler::onEvent(NetlinkEvent *evt) {  
  2.     VolumeManager *vm = VolumeManager::Instance();  
  3.     const char *subsys = evt->getSubsystem();  
  4.   
  5.     if (!subsys) {//如果事件和外部存储设备无关,则不处理  
  6.         SLOGW("No subsystem found in netlink event");  
  7.         return;  
  8.     }  
  9.   
  10.     if (!strcmp(subsys, "block")) {//如果Uevent是block子系统  
  11.         vm->handleBlockEvent(evt);//进入VolumeManager中处理;此处和VolumeManager进行交互  
  12.     }  
  13. }  
如果事件是和外部存储有关,则调用VolumeManager::handleBlockEvent()处理该事件;这里,就看到了NetlinkManager和VolumeManager之间进行数据流动的处理了。

(2)、VolumeManager

Vold使用VolumeManager的过程和NetlinkManager类似,也是三步:
  1. vm= VolumeManager::Instance()
  2. vm->setBroadcaster((SocketListener *) cl)
  3. vm->start()
1、2步与NetlinkManager的处理类似:
  1. //单例模式  
  2. VolumeManager *VolumeManager::Instance() {  
  3.     if (!sInstance)  
  4.         sInstance = new VolumeManager();  
  5.     return sInstance;  
  6. }  
  7.   
  8. VolumeManager::VolumeManager() {  
  9.     mDebug = false;  
  10.     mActiveContainers = new AsecIdCollection();  
  11.     mBroadcaster = NULL;  
  12.     mUmsSharingCount = 0;  
  13.     mSavedDirtyRatio = -1;  
  14.     // set dirty ratio to 0 when UMS is active  
  15.     mUmsDirtyRatio = 0;  
  16. }  
  17.  void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }  
直接看VolumeManager::start()的处理:
  1. int VolumeManager::start() {  
  2.     // Always start from a clean slate by unmounting everything in  
  3.     // directories that we own, in case we crashed.  
  4.     unmountAll();//在处理外部设备事件之前,先重置所有状态  
  5.   
  6.     // Assume that we always have an emulated volume on internal  
  7.     // storage; the framework will decide if it should be mounted.  
  8.     CHECK(mInternalEmulated == nullptr);  
  9.     mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(  
  10.             new android::vold::EmulatedVolume("/data/media"));  
  11.     mInternalEmulated->create();//预先设定/data/media,由framework决定是否mount;EmulatedVolume和VolumeBase之间是继承关系,代表不同类型的Volume  
  12.   
  13.     return 0;  
  14. }  
再直接看NetlinkManager和VolumeManager之间信息处理的调用过程:
  1. void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {  
  2.     std::lock_guard<std::mutex> lock(mLock);  
  3.   
  4.     if (mDebug) {  
  5.         LOG(VERBOSE) << "----------------";  
  6.         LOG(VERBOSE) << "handleBlockEvent with action " << (int) evt->getAction();  
  7.         evt->dump();  
  8.     }  
  9.   
  10.     std::string eventPath(evt->findParam("DEVPATH"));//设备路径  
  11.     std::string devType(evt->findParam("DEVTYPE"));//设备类型  
  12.   
  13.     if (devType != "disk"return;  
  14.   
  15.     //主次设备号,两者可以描述一个具体设备  
  16.     int major = atoi(evt->findParam("MAJOR"));  
  17.     int minor = atoi(evt->findParam("MINOR"));  
  18.     dev_t device = makedev(major, minor);//根据主次设备号创建设备  
  19.   
  20.     switch (evt->getAction()) {  
  21.     case NetlinkEvent::Action::kAdd: {  
  22.         for (auto source : mDiskSources) {  
  23.             if (source->matches(eventPath)) {  
  24.                 // For now, assume that MMC devices are SD, and that  
  25.                 // everything else is USB  
  26.                 int flags = source->getFlags();  
  27.                 if (major == kMajorBlockMmc) {  
  28.                     flags |= android::vold::Disk::Flags::kSd;  
  29.                 } else {  
  30.                     flags |= android::vold::Disk::Flags::kUsb;  
  31.                 }  
  32.   
  33.                 auto disk = new android::vold::Disk(eventPath, device,  
  34.                         source->getNickname(), flags);//将信息封装成Disk对象,表示一个检测到的物理设备  
  35.                 disk->create();//Disk::create()  
  36.                 mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));//加进集合  
  37.                 break;  
  38.             }  
  39.         }  
  40.         break;  
  41.     }  
  42.     case NetlinkEvent::Action::kChange: {  
  43.         LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";  
  44.         for (auto disk : mDisks) {  
  45.             if (disk->getDevice() == device) {  
  46.                 disk->readMetadata();  
  47.                 disk->readPartitions();  
  48.             }  
  49.         }  
  50.         break;  
  51.     }  
  52.     case NetlinkEvent::Action::kRemove: {  
  53.         auto i = mDisks.begin();  
  54.         while (i != mDisks.end()) {  
  55.             if ((*i)->getDevice() == device) {  
  56.                 (*i)->destroy();  
  57.                 i = mDisks.erase(i);  
  58.             } else {  
  59.                 ++i;  
  60.             }  
  61.         }  
  62.         break;  
  63.     }  
  64.     default: {  
  65.         LOG(WARNING) << "Unexpected block event action " << (int) evt->getAction();  
  66.         break;  
  67.     }  
  68.     }  
  69. }  
向上层发送各种类型的消息都是通过notifyEvent()函数处理的,其实际就是通过socket来发送的。
同时,Vold的主函数中还有一个重要的函数调用process_config():
  1. static int process_config(VolumeManager *vm) {  
  2.     std::string path(android::vold::DefaultFstabPath()); //获取到vold.fstab文件路径  
  3.     fstab = fs_mgr_read_fstab(path.c_str());//解析.fstab文件,并返回封装的fstab对象  
  4.     if (!fstab) {  
  5.         PLOG(ERROR) << "Failed to open default fstab " << path;  
  6.         return -1;  
  7.     }  
  8.   
  9.     /* Loop through entries looking for ones that vold manages */  
  10.     bool has_adoptable = false;  
  11.     for (int i = 0; i < fstab->num_entries; i++) {  
  12.         if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {  
  13.             if (fs_mgr_is_nonremovable(&fstab->recs[i])) {  
  14.                 LOG(WARNING) << "nonremovable no longer supported; ignoring volume";  
  15.                 continue;  
  16.             }  
  17.   
  18.             std::string sysPattern(fstab->recs[i].blk_device);  
  19.             std::string nickname(fstab->recs[i].label);  
  20.             int flags = 0;  
  21.   
  22.             if (fs_mgr_is_encryptable(&fstab->recs[i])) {  
  23.                 flags |= android::vold::Disk::Flags::kAdoptable;  
  24.                 has_adoptable = true;  
  25.             }  
  26.             if (fs_mgr_is_noemulatedsd(&fstab->recs[i])  
  27.                     || property_get_bool("vold.debug.default_primary"false)) {  
  28.                 flags |= android::vold::Disk::Flags::kDefaultPrimary;//is Primary?  
  29.             }  
  30.   
  31.             vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(  
  32.                     new VolumeManager::DiskSource(sysPattern, nickname, flags)));//添加一个VolumeManager::DiskSource对象,保存了一些信息  
  33.         }  
  34.     }  
  35.     property_set("vold.has_adoptable", has_adoptable ? "1" : "0");  
  36.     return 0;  
  37. }  
  1. //解析.fstab配置文件,并返回一个fstab结构对象  
  2. struct fstab *fs_mgr_read_fstab(const char *fstab_path)  
  3. {  
  4.     FILE *fstab_file;  
  5.     int cnt, entries;  
  6.     ssize_t len;  
  7.     size_t alloc_len = 0;  
  8.     char *line = NULL;  
  9.     const char *delim = " \t";  
  10.     char *save_ptr, *p;  
  11.     struct fstab *fstab = NULL;  
  12.     struct fs_mgr_flag_values flag_vals;  
  13. #define FS_OPTIONS_LEN 1024  
  14.     char tmp_fs_options[FS_OPTIONS_LEN];  
  15.   
  16.     fstab_file = fopen(fstab_path, "r");  
  17.     if (!fstab_file) {  
  18.         ERROR("Cannot open file %s\n", fstab_path);  
  19.         return 0;  
  20.     }  
  21.   
  22.     entries = 0;  
  23.     while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {  
  24.         /* if the last character is a newline, shorten the string by 1 byte */  
  25.         if (line[len - 1] == '\n') {  
  26.             line[len - 1] = '\0';  
  27.         }  
  28.         /* Skip any leading whitespace */  
  29.         p = line;  
  30.         while (isspace(*p)) {  
  31.             p++;  
  32.         }  
  33.         /* ignore comments or empty lines */  
  34.         if (*p == '#' || *p == '\0')  
  35.             continue;  
  36.         entries++;  
  37.     }  
  38.   
  39.     if (!entries) {  
  40.         ERROR("No entries found in fstab\n");  
  41.         goto err;  
  42.     }  
  43.   
  44.     /* Allocate and init the fstab structure */  
  45.     fstab = calloc(1, sizeof(struct fstab));  
  46.     fstab->num_entries = entries;  
  47.     fstab->fstab_filename = strdup(fstab_path);  
  48.     fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));  
  49.   
  50.     fseek(fstab_file, 0, SEEK_SET);  
  51.   
  52.     cnt = 0;  
  53.     //解析fstab中每行的内容,并进行封装  
  54.     while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {  
  55.         /* if the last character is a newline, shorten the string by 1 byte */  
  56.         if (line[len - 1] == '\n') {  
  57.             line[len - 1] = '\0';  
  58.         }  
  59.   
  60.         /* Skip any leading whitespace */  
  61.         p = line;  
  62.         while (isspace(*p)) {  
  63.             p++;  
  64.         }  
  65.         /* ignore comments or empty lines */  
  66.         if (*p == '#' || *p == '\0')  
  67.             continue;  
  68.   
  69.         /* If a non-comment entry is greater than the size we allocated, give an 
  70.          * error and quit.  This can happen in the unlikely case the file changes 
  71.          * between the two reads. 
  72.          */  
  73.         if (cnt >= entries) {  
  74.             ERROR("Tried to process more entries than counted\n");  
  75.             break;  
  76.         }  
  77.   
  78.         if (!(p = strtok_r(line, delim, &save_ptr))) {  
  79.             ERROR("Error parsing mount source\n");  
  80.             goto err;  
  81.         }  
  82.         fstab->recs[cnt].blk_device = strdup(p);  
  83.   
  84.         if (!(p = strtok_r(NULL, delim, &save_ptr))) {  
  85.             ERROR("Error parsing mount_point\n");  
  86.             goto err;  
  87.         }  
  88.         fstab->recs[cnt].mount_point = strdup(p);//mount的位置  
  89.   
  90.         if (!(p = strtok_r(NULL, delim, &save_ptr))) {  
  91.             ERROR("Error parsing fs_type\n");  
  92.             goto err;  
  93.         }  
  94.         fstab->recs[cnt].fs_type = strdup(p);  
  95.   
  96.         if (!(p = strtok_r(NULL, delim, &save_ptr))) {  
  97.             ERROR("Error parsing mount_flags\n");  
  98.             goto err;  
  99.         }  
  100.         tmp_fs_options[0] = '\0';  
  101.         fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,  
  102.                                        tmp_fs_options, FS_OPTIONS_LEN);  
  103.   
  104.         /* fs_options are optional */  
  105.         if (tmp_fs_options[0]) {  
  106.             fstab->recs[cnt].fs_options = strdup(tmp_fs_options);  
  107.         } else {  
  108.             fstab->recs[cnt].fs_options = NULL;  
  109.         }  
  110.   
  111.         if (!(p = strtok_r(NULL, delim, &save_ptr))) {  
  112.             ERROR("Error parsing fs_mgr_options\n");  
  113.             goto err;  
  114.         }  
  115.         fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,  
  116.                                                     &flag_vals, NULL, 0);  
  117.         fstab->recs[cnt].key_loc = flag_vals.key_loc;  
  118.         fstab->recs[cnt].verity_loc = flag_vals.verity_loc;  
  119.         fstab->recs[cnt].length = flag_vals.part_length;  
  120.         fstab->recs[cnt].label = flag_vals.label;  
  121.         fstab->recs[cnt].partnum = flag_vals.partnum;  
  122.         fstab->recs[cnt].swap_prio = flag_vals.swap_prio;  
  123.         fstab->recs[cnt].zram_size = flag_vals.zram_size;  
  124.         cnt++;  
  125.     }  
  126.     fclose(fstab_file);  
  127.     free(line);  
  128.     return fstab;  
  129.   
  130. err:  
  131.     fclose(fstab_file);  
  132.     free(line);  
  133.     if (fstab)  
  134.         fs_mgr_free_fstab(fstab);  
  135.     return NULL;  
  136. }  
fstab文件是Linux下配置分区的一个文件,这部分后续补充......总之,就是解析fstab文件后,会根据配置信息创建DiskSource对象,加入到VolumeManager::mDiskSource中。

(3 )、CommandListener

VolumeManager要想向MountService发送消息,就要借助CommandListener。CommandListener有一个较为复杂的继承关系:


CommandListener的创建过程跟NetlinkManager类似:
  1. CommandListener::CommandListener() :  
  2.                  FrameworkListener("vold"true) {//vold是socket名称,init.rc文件中声明的一个socket资源,用于和framework通信  
  3.     registerCmd(new DumpCmd());//注册不同的命令对象,保存到mCommands成员中;同时,创建Cmd对象时,会保存一个字符串标识(一般是上层下发命令中的第一个字符串),用于后续区分不同的命令  
  4.     registerCmd(new VolumeCmd());//标识:volume  
  5.     registerCmd(new AsecCmd());//标识:asec  
  6.     registerCmd(new ObbCmd());//标识:obb  
  7.     registerCmd(new StorageCmd());//标识:storage  
  8.     registerCmd(new FstrimCmd());//标识:fstrim  
  9. }  
  1. FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :  
  2.                             SocketListener(socketName, true, withSeq) {  
  3.     init(socketName, withSeq);  
  4. }  
  1. SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {  
  2.     init(socketName, -1, listen, useCmdNum);  
  3. }  
  4.   
  5. void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {  
  6.     mListen = listen;//是否是监听端,与前面不同,这里为true  
  7.     mSocketName = socketName;//保存socket的名字"vold",与MountService通信  
  8.     mSock = socketFd;//保存socket的句柄值  
  9.     mUseCmdNum = useCmdNum;  
  10.     pthread_mutex_init(&mClientsLock, NULL);  
  11.     mClients = new SocketClientCollection();//集合对象  
  12. }  
下面直接看CommandListener->startListener():
  1. int SocketListener::startListener() {  
  2.     return startListener(4);  
  3. }  
  4.   
  5. int SocketListener::startListener(int backlog) {  
  6.   
  7.     if (!mSocketName && mSock == -1) {  
  8.         SLOGE("Failed to start unbound listener");  
  9.         errno = EINVAL;  
  10.         return -1;  
  11.     } else if (mSocketName) {  
  12.         if ((mSock = android_get_control_socket(mSocketName)) < 0) {//名为"vold"的socket的句柄值  
  13.             SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
  14.                  mSocketName, strerror(errno));  
  15.             return -1;  
  16.         }  
  17.         SLOGV("got mSock = %d for %s", mSock, mSocketName);  
  18.         fcntl(mSock, F_SETFD, FD_CLOEXEC);  
  19.     }  
  20.   
  21.     if (mListen && listen(mSock, backlog) < 0) {//mListener为true,则监听该socket  
  22.         SLOGE("Unable to listen on socket (%s)", strerror(errno));  
  23.         return -1;  
  24.     } else if (!mListen)//mListener为false,走此分支  
  25.         mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));  
  26.   
  27.     if (pipe(mCtrlPipe)) {  
  28.         SLOGE("pipe failed (%s)", strerror(errno));  
  29.         return -1;  
  30.     }  
  31.   
  32.     if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {//创建一个线程,在其中调用threadStart(),并在mSock代表的套接字上等待客户端的连接请求  
  33.         SLOGE("pthread_create (%s)", strerror(errno));  
  34.         return -1;  
  35.     }  
  36.   
  37.     return 0;  
  38. }  
在CommandListener监听流程中,mListene为true;表示这一端是监听侧,等待Client的连接请求。这种场景下,MountService就是这里描述的客户端。MountService在创建过程,会通过创建NativeDaemonConnector对象,去连接名为"vold"的socket,这样两者就可以通信了。

SocketListener::threadStart():
  1. void *SocketListener::threadStart(void *obj) {  
  2.     SocketListener *me = reinterpret_cast<SocketListener *>(obj);  
  3.   
  4.     me->runListener();  
  5.     pthread_exit(NULL);  
  6.     return NULL;  
  7. }  
  8.   
  9. void SocketListener::runListener() {  
  10.   
  11.     SocketClientCollection pendingList;  
  12.   
  13.     while(1) {  
  14.         SocketClientCollection::iterator it;  
  15.         fd_set read_fds;  
  16.         int rc = 0;  
  17.         int max = -1;  
  18.   
  19.         FD_ZERO(&read_fds);  
  20.   
  21.         if (mListen) {//如果我们是服务端,则将该socket的套接字加入到可读监控队列中  
  22.             max = mSock;  
  23.             FD_SET(mSock, &read_fds);  
  24.         }  
  25.   
  26.         FD_SET(mCtrlPipe[0], &read_fds);  
  27.         if (mCtrlPipe[0] > max)  
  28.             max = mCtrlPipe[0];  
  29.   
  30.         pthread_mutex_lock(&mClientsLock);  
  31.         for (it = mClients->begin(); it != mClients->end(); ++it) {  
  32.             // NB: calling out to an other object with mClientsLock held (safe)  
  33.             int fd = (*it)->getSocket();  
  34.             FD_SET(fd, &read_fds);  
  35.             if (fd > max) {  
  36.                 max = fd;  
  37.             }  
  38.         }  
  39.         pthread_mutex_unlock(&mClientsLock);  
  40.         SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);  
  41.         if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {//如果集合read_fds中有socket可读  
  42.             if (errno == EINTR)监测  
  43.                 continue;  
  44.             SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);  
  45.             sleep(1);  
  46.             continue;  
  47.         } else if (!rc)  
  48.             continue;  
  49.   
  50.         if (FD_ISSET(mCtrlPipe[0], &read_fds)) {  
  51.             char c = CtrlPipe_Shutdown;  
  52.             TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));  
  53.             if (c == CtrlPipe_Shutdown) {  
  54.                 break;  
  55.             }  
  56.             continue;  
  57.         }  
  58.         if (mListen && FD_ISSET(mSock, &read_fds)) {//mListener值实际为true;服务端,等待客户端连接请求  
  59.             struct sockaddr addr;  
  60.             socklen_t alen;  
  61.             int c;  
  62.   
  63.             do {  
  64.                 alen = sizeof(addr);  
  65.                 c = accept(mSock, &addr, &alen);//接受MountService发起的socket连接请求,  
  66.                 SLOGV("%s got %d from accept", mSocketName, c);  
  67.             } while (c < 0 && errno == EINTR);  
  68.             if (c < 0) {  
  69.                 SLOGE("accept failed (%s)", strerror(errno));  
  70.                 sleep(1);  
  71.                 continue;  
  72.             }  
  73.             fcntl(c, F_SETFD, FD_CLOEXEC);  
  74.             pthread_mutex_lock(&mClientsLock);  
  75.             mClients->push_back(new SocketClient(c, true, mUseCmdNum));//根据c,创建一个SocketListener对象,并加入到集合中  
  76.             pthread_mutex_unlock(&mClientsLock);  
  77.         }  
  78.   
  79.         /* Add all active clients to the pending list first */  
  80.         pendingList.clear();  
  81.         pthread_mutex_lock(&mClientsLock);  
  82.         for (it = mClients->begin(); it != mClients->end(); ++it) {  
  83.             SocketClient* c = *it;  
  84.             // NB: calling out to an other object with mClientsLock held (safe)  
  85.             int fd = c->getSocket();  
  86.             if (FD_ISSET(fd, &read_fds)) {//遍历保存的所有客户端socket,如果对应的socket可读,则将该套接字加入到队列中  
  87.                 pendingList.push_back(c);  
  88.                 c->incRef();  
  89.             }  
  90.         }  
  91.         pthread_mutex_unlock(&mClientsLock);  
  92.   
  93.         /* Process the pending list, since it is owned by the thread, 
  94.          * there is no need to lock it */  
  95.         while (!pendingList.empty()) {  
  96.             /* Pop the first item from the list */  
  97.             it = pendingList.begin();  
  98.             SocketClient* c = *it;  
  99.             pendingList.erase(it);  
  100.             /* Process it, if false is returned, remove from list */  
  101.             if (!onDataAvailable(c)) {//有数据来,调用FrameworkListener::onDataAvailable()处理  
  102.                 release(c, false);  
  103.             }  
  104.             c->decRef();  
  105.         }  
  106.     }  
  107. }  
由于mListen值的变化(此时为true),处理流程有所不同。首先作为服务端,会等待Client的连接请求;如果有连接请求,并有数据发送过来,则通过
onDataAvailable()处理。根据继承关系,此处调用FrameworkListener::onDataAvailable():
  1. bool FrameworkListener::onDataAvailable(SocketClient *c) {  
  2.     char buffer[CMD_BUF_SIZE];  
  3.     int len;  
  4.   
  5.     len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));//从MountService接收指令数据,存入buffer中  
  6.     if (len < 0) {  
  7.         SLOGE("read() failed (%s)", strerror(errno));  
  8.         return false;  
  9.     } else if (!len)  
  10.         return false;  
  11.    if(buffer[len-1] != '\0')  
  12.         SLOGW("String is not zero-terminated");  
  13.   
  14.     int offset = 0;  
  15.     int i;  
  16.   
  17.     for (i = 0; i < len; i++) {  
  18.         if (buffer[i] == '\0') {  
  19.             /* IMPORTANT: dispatchCommand() expects a zero-terminated string */  
  20.             dispatchCommand(c, buffer + offset);//命令分发处理  
  21.             offset = i + 1;  
  22.         }  
  23.     }  
  24.   
  25.     return true;  
  26. }  
如果消息不为空,则调用FrameworkListener::dispatchCommand()进行处理:
  1. void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {  
  2.     FrameworkCommandCollection::iterator i;  
  3.     int argc = 0;  
  4.     char *argv[FrameworkListener::CMD_ARGS_MAX];  
  5.     char tmp[CMD_BUF_SIZE];  
  6.     char *p = data;  
  7.     char *q = tmp;  
  8.     char *qlimit = tmp + sizeof(tmp) - 1;  
  9.     bool esc = false;  
  10.     bool quote = false;  
  11.     bool haveCmdNum = !mWithSeq;  
  12.   
  13.     memset(argv, 0, sizeof(argv));  
  14.     memset(tmp, 0, sizeof(tmp));  
  15.     while(*p) {  
  16.         if (*p == '\\') {  
  17.             if (esc) {  
  18.                 if (q >= qlimit)  
  19.                     goto overflow;  
  20.                 *q++ = '\\';  
  21.                 esc = false;  
  22.             } else  
  23.                 esc = true;  
  24.             p++;  
  25.             continue;  
  26.         } else if (esc) {  
  27.             if (*p == '"') {  
  28.                 if (q >= qlimit)  
  29.                     goto overflow;  
  30.                 *q++ = '"';  
  31.             } else if (*p == '\\') {  
  32.                 if (q >= qlimit)  
  33.                     goto overflow;  
  34.                 *q++ = '\\';  
  35.             } else {  
  36.                 cli->sendMsg(500, "Unsupported escape sequence"false);  
  37.                 goto out;  
  38.             }  
  39.             p++;  
  40.             esc = false;  
  41.             continue;  
  42.         }  
  43.   
  44.         if (*p == '"') {  
  45.             if (quote)  
  46.                 quote = false;  
  47.             else  
  48.                 quote = true;  
  49.             p++;  
  50.             continue;  
  51.         }  
  52.   
  53.         if (q >= qlimit)  
  54.             goto overflow;  
  55.         *q = *p++;  
  56.         if (!quote && *q == ' ') {  
  57.             *q = '\0';  
  58.             if (!haveCmdNum) {  
  59.                 char *endptr;  
  60.                 int cmdNum = (int)strtol(tmp, &endptr, 0);  
  61.                 if (endptr == NULL || *endptr != '\0') {  
  62.                     cli->sendMsg(500, "Invalid sequence number"false);  
  63.                     goto out;  
  64.                 }  
  65.                 cli->setCmdNum(cmdNum);  
  66.                 haveCmdNum = true;  
  67.             } else {  
  68.                 if (argc >= CMD_ARGS_MAX)  
  69.                     goto overflow;  
  70.                 argv[argc++] = strdup(tmp);  
  71.             }  
  72.             memset(tmp, 0, sizeof(tmp));  
  73.             q = tmp;  
  74.             continue;  
  75.         }  
  76.         q++;  
  77.     }  
  78.   
  79.     *q = '\0';  
  80.     if (argc >= CMD_ARGS_MAX)  
  81.         goto overflow;  
  82.     argv[argc++] = strdup(tmp);  
  83. #if 0  
  84.     for (int k = 0; k < argc; k++) {  
  85.         SLOGD("arg[%d] = '%s'", k, argv[k]);  
  86.     }  
  87. #endif  
  88.   
  89.     if (quote) {  
  90.         cli->sendMsg(500, "Unclosed quotes error"false);  
  91.         goto out;  
  92.     }  
  93.   
  94.     if (errorRate && (++mCommandCount % errorRate == 0)) {  
  95.         /* ignore this command - let the timeout handler handle it */  
  96.         SLOGE("Faking a timeout");  
  97.         goto out;  
  98.     }  
  99.   
  100.     for (i = mCommands->begin(); i != mCommands->end(); ++i) {  
  101.         FrameworkCommand *c = *i;  
  102.   
  103.         if (!strcmp(argv[0], c->getCommand())) {//获取命令的第一个参数(即标识),遍历mCommands,找到符合的Command对象去执行runCommand();例如,如果标识是volume,则执行VolumeCommand的runCommand()函数处理下发的指令  
  104.             if (c->runCommand(cli, argc, argv)) {//重要,调用不同Command类型的、我们之前注册过的对象调用runCommand()方法处理指令(定义在CommandListener中)  
  105.                 SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));  
  106.             }  
  107.             goto out;  
  108.         }  
  109.     }  
  110.     cli->sendMsg(500, "Command not recognized"false);  
  111. out:  
  112.     int j;  
  113.     for (j = 0; j < argc; j++)  
  114.         free(argv[j]);  
  115.     return;  
  116.   
  117. overflow:  
  118.     LOG_EVENT_INT(78001, cli->getUid());  
  119.     cli->sendMsg(500, "Command too long"false);  
  120.     goto out;  
首先,根据上层下发的指令中的信息去得到一个符合要求的Command命令对象,然后执行相应的runCommand()方法来处理不同的指令。我们下发的指令有一定的规则,一般第一个字符串是标识,用以获得不同的Command对象;第二个参数一般是我们需要进行的操作命令;后续的参数一般都是下发的用以完成操作的数据。一般情况,格式类似于:
  1. volume mount /mnt/sda/sda1  
字符串之间以空格分开。
到此,Vold机制及原理的分析就基本结束了。Vold与MountService的交互后续再介绍。

PS:Android是基于Linux的,其中很多知识都与Linux的内容息息相关,如文件系统、设备管理。懂点Kernel的朋友,搞安卓会有不少优势......
还需努力,其中有些内容较为简略,后续有机会再来补充、完善。有错欢迎指出,乐于讨论,共同进步。

文中图片资源下载:http://download.csdn.net/detail/csdn_of_coder/9702463
转载地址:http://blog.csdn.net/csdn_of_coder/article/details/53447216
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值