一、Vold工作机制
Vold是Volume Daemon的缩写,它是Android平台中外部存储系统的管控中心,是管理和控制Android平台外部存储设备的后台进程。其功能主要包括:SD卡的插拔事件检测、SD卡挂载、卸载、格式化等。
如上图所示,Vold中的NetlinkManager模块接收来自Linux Kernel的uevent消息。
NetlinkManager将这些消息转发给VolumeManager模块。VolumeManager会对应做一些操作,然后把相关信息通过CommandListener发送给MountService。
MountService根据收到的消息后,根据情况会利用CommandListener发送相关的处理命令给VolumeManager做进一步处理。
CommandListener模块内部封装了一个Socket用于跨进程通信。它一方面接收来自MountService的控制命令,另一方面VolumeManager通过它将消息发送给MountService。
Tips:
Netlink是Linux系统中用户空间进程和Kernel进行通信的一种机制,是基于Socket的异步通信机制。
通过这种机制,位于用户空间的进程可以接收来自Kernel的一些信息,同时用户空间进程也可以利用Netlink向Kernel发送一些控制命令。
二、Vold进程启动过程
Vold进程启动文件定义于system/vold/vold.rc文件中:
service vold /system/bin/vold \ class core socket vold stream 0660 root mount socket cryptd stream 0660 root mount ioprio be 2 writepid /dev/cpuset/foreground/tasks
被init进程启动后,将调用system/vold/main.cpp中的main函数:
int main(int argc, char** argv) { ............. VolumeManager *vm; CommandListener *cl; ............. NetlinkManager *nm; parse_args(argc, argv); .............. fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC); fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC); mkdir("/dev/block/vold", 0755); ......... if (!(vm = VolumeManager::Instance())) { LOG(ERROR) << "Unable to create VolumeManager"; exit(1); } if (!(nm = NetlinkManager::Instance())) { LOG(ERROR) << "Unable to create NetlinkManager"; exit(1); } ................... cl = new CommandListener(); ............ vm->setBroadcaster((SocketListener *) cl); nm->setBroadcaster((SocketListener *) cl); if (vm->start()) { PLOG(ERROR) << "Unable to start VolumeManager"; exit(1); } if (process_config(vm)) { PLOG(ERROR) << "Error reading configuration... continuing anyways"; } if (nm->start()) { PLOG(ERROR) << "Unable to start NetlinkManager"; exit(1); } coldboot("/sys/block"); if (cl->startListener()) { PLOG(ERROR) << "Unable to start CommandListener"; exit(1); } ....... while(1) { sleep(1000); } LOG(ERROR) << "Vold exiting"; exit(0);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
从上面的代码不难看出,Vold进程的main函数中,创建并启动其子模块VolumeManager、NetlinkManager和CommandListener后,就不再执行实际的工作了。
以后Vold进程具体的工作就会交付给子模块进行处理。
三、Vold进程中各模块分析
为了进一步了解整个Vold进程的主要工作流程,接下来我们分析一下其主要模块的工作流程。
1、NetlinkManager模块
1.1 NetlinkManager的创建和启动
在Vold的main函数中,调用NetlinkManager::Instance创建出NetlinkManager:
NetlinkManager *NetlinkManager::Instance() { if (!sInstance) sInstance = new NetlinkManager(); return sInstance;}NetlinkManager::NetlinkManager() { mBroadcaster = NULL;}
从上面的代码可以看到,NetlinkManager的创建比较简单。
在创建出NetlinkManager后,Vold调用了NetlinkManager的setBroadcaster函数:
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
依然言简意赅。
这里唯一需要说明的是,Android这里的设计看起来比较很奇怪,虽然NetlinkManager设置了CommandListener对象,但它并没有通过CommandListener发送消息和接收命令。
配置好NetlinkManager后,Vold就调用了NetlinkManger的start函数:
int NetlinkManager::start() { struct sockaddr_nl nladdr; int sz = 64 * 1024; int on = 1; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = getpid(); nladdr.nl_groups = 0xffffffff; if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) < 0) { SLOGE("Unable to create uevent socket: %s", strerror(errno)); return -1; } if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) { SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno)); goto out; } if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno)); goto out; } if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { SLOGE("Unable to bind uevent socket: %s", strerror(errno)); goto out; } mHandler = new NetlinkHandler(mSock); if (mHandler->start()) { SLOGE("Unable to start NetlinkHandler: %s", strerror(errno)); goto out; } return 0;out: close(mSock); return -1;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
通过上面的代码不难看出,其实NetlinkManager启动后就是创建一个可以接收Kernel消息的socket,并以此socket构建并启动NetlinkHandler。
可以预见NetlinkHandler将用来处理socket收到的信息。
1.2 NetlinkHandler
NetlinkHandler::NetlinkHandler(int listenerSocket) : NetlinkListener(listenerSocket) {}
NetlinkHandler初始化时,将与Kernel通信的socket描述符传入到父类NetlinkListener中。
NetlinkListener::NetlinkListener(int socket) : SocketListener(socket, false) { mFormat = NETLINK_FORMAT_ASCII;}
NetlinkListener又进一步调用其父类SocketListener:
SocketListener::SocketListener(int socketFd, bool listen) { init(NULL, socketFd, listen, false);}void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) { mListen = listen; mSocketName = socketName; mSock = socketFd; mUseCmdNum = useCmdNum; pthread_mutex_init(&mClientsLock, NULL); mClients = new SocketClientCollection();}
从上面的代码可以看出,NetlinkHandler对应的继承体系如下图所示:
创建完NetlinkHandler后,NetlinkManager调用了NetlinkHandler的start方法:
int NetlinkHandler::start() { return this->startListener();}int SocketListener::startListener() { return startListener(4);}int SocketListener::startListener(int backlog) { ................ if (mListen && listen(mSock, backlog) < 0) { .............. } else if (!mListen) mClients->push_back(new SocketClient(mSock, false, mUseCmdNum)); if (pipe(mCtrlPipe)) { SLOGE("pipe failed (%s)", strerror(errno)); return -1; } if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) { SLOGE("pthread_create (%s)", strerror(errno)); return -1; } return 0;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
至此,我们知道了NetlinkHandler启动后,创建了一个工作线程,用于接收和处理数据。现在进一步看看threadStart函数:
void *SocketListener::threadStart(void *obj) { SocketListener *me = reinterpret_cast<SocketListener *>(obj); me->runListener(); pthread_exit(NULL); return NULL;}void SocketListener::runListener() { SocketClientCollection pendingList; while(1) { SocketClientCollection::iterator it; fd_set read_fds; int rc = 0; int max = -1; FD_ZERO(&read_fds); .......... FD_SET(mCtrlPipe[0], &read_fds); if (mCtrlPipe[0] > max) max = mCtrlPipe[0]; pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { int fd = (*it)->getSocket(); FD_SET(fd, &read_fds); if (fd > max) { max = fd; } } pthread_mutex_unlock(&mClientsLock); ............... if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { ............ } else if (!rc) continue; if (FD_ISSET(mCtrlPipe[0], &read_fds)) { char c = CtrlPipe_Shutdown; TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1)); if (c == CtrlPipe_Shutdown) { break; } continue; } if (mListen && FD_ISSET(mSock, &read_fds)) { sockaddr_storage ss; sockaddr* addrp = reinterpret_cast<sockaddr*>(&ss); socklen_t alen; int c; do { alen = sizeof(ss); c = accept(mSock, addrp, &alen); SLOGV("%s got %d from accept", mSocketName, c); } while (c < 0 && errno == EINTR); if (c < 0) { SLOGE("accept failed (%s)", strerror(errno)); sleep(1); continue; } fcntl(c, F_SETFD, FD_CLOEXEC); pthread_mutex_lock(&mClientsLock); mClients->push_back(new SocketClient(c, true, mUseCmdNum)); pthread_mutex_unlock(&mClientsLock); } pendingList.clear(); pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { SocketClient* c = *it; int fd = c->getSocket(); if (FD_ISSET(fd, &read_fds)) { pendingList.push_back(c); c->incRef(); } } pthread_mutex_unlock(&mClientsLock); while (!pendingList.empty()) { it = pendingList.begin(); SocketClient* c = *it; pendingList.erase(it); if (!onDataAvailable(c)) { release(c, false); } c->decRef(); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
上面的代码看起来比较复杂,主要是因为考虑到了Socket作为服务端的情况。
在NetlinkHandler中Socket仅作为客户端接收数据,因此在上面的代码中,其实就是利用子类的onDataAvailable函数处理收到的数据而已。
bool NetlinkListener::onDataAvailable(SocketClient *cli){ int socket = cli->getSocket(); ssize_t count; uid_t uid = -1; ................. count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket, mBuffer, sizeof(mBuffer), require_group, &uid)); if (count < 0) { ........ return false; } NetlinkEvent *evt = new NetlinkEvent(); if (evt->decode(mBuffer, count, mFormat)) { onEvent(evt); } else if (mFormat != NETLINK_FORMAT_BINARY) { .......... } delete evt; return true;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
上面的代码比较简单,其实就是从socket中的字节流中取出Uevent事件,然后将这些事件解码成NetlinkEvent,然后利用子类的onEvent做进一步处理。
void NetlinkHandler::onEvent(NetlinkEvent *evt) { VolumeManager *vm = VolumeManager::Instance(); const char *subsys = evt->getSubsystem(); if (!subsys) { SLOGW("No subsystem found in netlink event"); return; } if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); }}
最后总结一下NetlinkManager模块的工作,如上图所示:
1、NetlinkManager启动后,将创建出与Kernel通信的socket,并用此socket创建出NetlinkHandler。
2、NetlinkHandler启动后,将创建出工作线程(其父类函数完成)。
3、工作线程启动后,将负责监听socket是否有数据到来。
4、当工作线程监听到数据到来后,负责将数据递交给NetlinkHandler。
5、NetlinkHandler负责从socket中的数据中解析出Uevent,并进一步解码成NetlinkEvent,以递交给VolumeManager。
2、VolumeManager模块
2.1 VolumeManager的创建和启动
在Vold的main函数中,调用VolumeManager的instance函数创建VolumeManager:
VolumeManager *VolumeManager::Instance() { if (!sInstance) sInstance = new VolumeManager(); return sInstance;}VolumeManager::VolumeManager() { mDebug = false; mActiveContainers = new AsecIdCollection(); mBroadcaster = NULL; mUmsSharingCount = 0; mSavedDirtyRatio = -1; mUmsDirtyRatio = 0;}
容易看出VolumeManager也是单例模式创建的。
接着,Vold进程利用VolumeManager的setBroadcaster函数,将Commandlistener对象赋予VolumeManager。
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
完成VolumeManager的创建后,Vold进程调用start函数,启动VolumeManager:
int VolumeManager::start() { unmountAll(); CHECK(mInternalEmulated == nullptr); mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>( new android::vold::EmulatedVolume("/data/media")); mInternalEmulated->create(); return 0;}
从上面的代码可以看出,VolumeManager启动后就干了两件事:
1、清楚所有已挂载的设备。正如注释所说的,通过这种方式可以让VolumeManager每次都从一个确定的“干净”的状态启动,避免之前出现Vold进程出现过crash。
看看unmountAll函数:
int VolumeManager::unmountAll() { std::lock_guard<std::mutex> lock(mLock); if (mInternalEmulated != nullptr) { mInternalEmulated->unmount(); } for (auto disk : mDisks) { disk->unmountAll(); } FILE* fp = setmntent("/proc/mounts", "r"); if (fp == NULL) { SLOGE("Error opening /proc/mounts: %s", strerror(errno)); return -errno; } std::list<std::string> toUnmount; mntent* mentry; while ((mentry = getmntent(fp)) != NULL) { if (strncmp(mentry->mnt_dir, "/mnt/", 5) == 0 || strncmp(mentry->mnt_dir, "/storage/", 9) == 0) { toUnmount.push_front(std::string(mentry->mnt_dir)); } } endmntent(fp); for (auto path : toUnmount) { SLOGW("Tearing down stale mount %s", path.c_str()); android::vold::ForceUnmount(path); } return 0;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
unmountAll的内容比较简单,同时注释清晰,此处不再赘述。
2、创建一个内部的挂载设备。
mInternalEmulated是一个VolumeBase类型的对象,我们看看其create函数:
status_t VolumeBase::create() { CHECK(!mCreated) mCreated = true //doCreate进行实际的创建 status_t res = doCreate() //通过CommandListener通知框架中的MountService notifyEvent(ResponseCode::VolumeCreated, StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str())) setState(State::kUnmounted) return res}
2.2 配置VolumeManager
当Vold创建并启动完VolumeManager后,就调用process_config函数对VolumeManager进行配置:
static int process_config(VolumeManager *vm) { std::string path(android::vold::DefaultFstabPath()); fstab = fs_mgr_read_fstab(path.c_str()); ........... ........ for (int i = 0; i < fstab->num_entries; i++) { if (fs_mgr_is_voldmanaged(&fstab->recs[i])) { ........... vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>( new VolumeManager::DiskSource(sysPattern, nickname, flags))); } } ................}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
结合代码,我们知道process_config其实就是解析fstab文件,然后设置一些存储设备的挂载点。
2.3 NetlinkManager与VolumeManager之间的交互
在前面介绍NetlinkManager时,我们知道当NetlinkManager收到Kernel的事件后,将利用NetlinkHandler通知VolumeManager:
void NetlinkHandler::onEvent(NetlinkEvent *evt) { VolumeManager *vm = VolumeManager::Instance(); const char *subsys = evt->getSubsystem(); if (!subsys) { SLOGW("No subsystem found in netlink event"); return; } if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); }}
我们看看VolumeManager的handleBlockeEvent:
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) { .............. int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); dev_t device = makedev(major, minor); switch (evt->getAction()) { case NetlinkEvent::Action::kAdd: { ......... auto disk = new android::vold::Disk(eventPath, device, source->getNickname(), flags); disk->create(); mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk)); break; } case NetlinkEvent::Action::kChange: { .......... for (auto disk : mDisks) { if (disk->getDevice() == device) { disk->readMetadata(); disk->readPartitions(); } } break; } case NetlinkEvent::Action::kRemove: { auto i = mDisks.begin(); while (i != mDisks.end()) { if ((*i)->getDevice() == device) { (*i)->destroy(); i = mDisks.erase(i); } else { ++i; } } break; } default: { LOG(WARNING) << "Unexpected block event action " << (int) evt->getAction(); break; } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
至此VolumeManager的主要工作介绍完毕,从上面的代码可以看出VolumeManager使用Disk对象来抽象实际的存储设备。
我们现在可以结合上图总结一下VolumeManager的工作流程:
1、存储设备发生变化(如热插拔等),将导致Linux Kernel发出Uevent消息给NetlinkManager。
2、NetlinkManager将事件通知给VolumeManager。
3、VolumeManager根据事件的内容,判断是设备的变化情况,然后操作对应的Disk对象。例如新增存储设备,就利用事件中的内容创建出新的Disk(创建Disk时,就会进一步读取分区信息,创建出Volume对象,此处不再细分);设备被移除了,VolumeManager就负责移除对应的Disk。
到目前为止,我们分析的流程都停留在Vold进程中,并没有与Android框架发生实际的交互。为了引出交互的实际流程,我们需要先分析一下Vold进程与框架交互的桥梁,即CommandListener。
3、CommandListener
3.1 CommandListener的创建
Vold进程在main函数中创建出了CommandListener,然后调用了CommandListener的startListener函数。
我们先看看CommandListener的构造函数:
class CommandListener : public FrameworkListener {........}
从定义来看CommandListener继承于FrameworkListener。
CommandListener::CommandListener() : FrameworkListener("vold", true) { //注册CommandListener支持的命令 registerCmd(new DumpCmd()); registerCmd(new VolumeCmd()); registerCmd(new AsecCmd()); registerCmd(new ObbCmd()); registerCmd(new StorageCmd()); registerCmd(new FstrimCmd()); registerCmd(new AppFuseCmd());}
在CommandListener的构造函数中,调用了父类的构造函数,同时利用其父类的registerCmd函数创建并注册了一些Cmd对象。
我们看看FrameworkListener:
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) : SocketListener(socketName, true, withSeq) { init(socketName, withSeq);}void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) { mCommands = new FrameworkCommandCollection(); errorRate = 0; mCommandCount = 0; mWithSeq = withSeq;}void FrameworkListener::registerCmd(FrameworkCommand *cmd) { mCommands->push_back(cmd);}
根据前面的代码,我们可以得到上图的继承关系。
可以看到与之前分析NetlinkManager一样,CommandListener最终继承自SocketListener。不过与NetlinkManager不同的是,CommandListener传入到SocketListener的mListen参数为true,这意味着CommandListener中的socket将作为服务端存在。
从FrameworkListener的registerCmd函数来看,FrameworkListener仅仅是保存了新创建的Cmd对象。这里采用了设计模式中的Command模式,每个命令的处理函数都是runCommand。
3.2 CommandListener启动
当Vold进程创建出CommandListener后,同样调用了CommandListener的startListener函数。
根据继承关系,我们知道最终将会调用到SocketListener的startListener函数。
在分析NetlinkManager时,我们已经分析过SocketListener的startListener函数。在startListener函数中将启动一个工作线程,以监听对应socket的数据。
此处CommandListener监听的是init进程创建出Vold进程后,Vold进程创建的名为”vold”的socket,并且该socket是作为服务端存在的。当服务端收到注册请求后,将生成对应的SocketClient对象。然后,工作线程就可以监听SocketClient是否有数据到来。
“vold”的客户端是MountService。与之前分析的一样,当工作线程收到客户端数据时,将调用子类的onDataAvailable函数进行处理。
此时SocketListener的子类是FrameworkListener:
bool FrameworkListener::onDataAvailable(SocketClient *c) { char buffer[CMD_BUF_SIZE]; int len; len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer))); ............ for (i = 0; i < len; i++) { if (buffer[i] == '\0') { dispatchCommand(c, buffer + offset); offset = i + 1; } } return true;}void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { ............ for (i = mCommands->begin(); i != mCommands->end(); ++i) { FrameworkCommand *c = *i; if (!strcmp(argv[0], c->getCommand())) { if (c->runCommand(cli, argc, argv)) { SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno)); } goto out; } } ............}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
以DumpCommand举例,看看runCommand函数:
int CommandListener::DumpCmd::runCommand(SocketClient *cli, int , char ** ) { cli->sendMsg(0, "Dumping loop status", false); if (Loop::dumpState(cli)) { cli->sendMsg(ResponseCode::CommandOkay, "Loop dump failed", true); } cli->sendMsg(0, "Dumping DM status", false); if (Devmapper::dumpState(cli)) { cli->sendMsg(ResponseCode::CommandOkay, "Devmapper dump failed", true); } cli->sendMsg(0, "Dumping mounted filesystems", false); FILE *fp = fopen("/proc/mounts", "r"); if (fp) { char line[1024]; while (fgets(line, sizeof(line), fp)) { line[strlen(line)-1] = '\0'; cli->sendMsg(0, line, false);; } fclose(fp); } cli->sendMsg(ResponseCode::CommandOkay, "dump complete", false); return 0;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
从上面的代码容易看出,DumpCmd执行相应的操作后,都是通过SocketClient的sendMsg发送结果。在SocketClient的底层,就是靠”vold” socket将数据返回给MountService。
现在我们总结一下CommandListener涉及的工作流程:
如上图所示:
1、init进程启动Vold进程时,根据vold.rc创建了”vold” socket,”vold” socket作为server端存在于Vold进程中。
2、在Vold进程的main函数中,创建出了CommandListener(部分工作尤其父类完成);CommandListener创建一些Cmd。
3、调用CommandListener的startListener函数,尤其父类SocketListener中创建出实际的工作线程,监听”vold” socket是否有请求到来。
4、框架中的MountService启动后,间接利用socket与”vold”通信(通过NativeDaemonConnector封装)。初始时,将向”vold”发送connect请求。
5、当工作线程监听到”vold”有请求到来后,利用accept函数创建出与MountService端通信的server端,即上图的s。接下来,工作线程开始监听s上是否有数据到来。
6、当工作线程监听到s有数据到来后,将数据递交给CommandListener(实际是FrameworkListener处理)。
7、CommandListener根据数据的类型,调用对应的Command进行处理。
8、实际的Cmd根据参数进行实际的操作,然后将运行结果递交给s,s再将数据通过c递交给MountService。
接下来,我们看看运行在框架层中的MountService。
4 MountService
有些应用程序需要检测外部存储卡的插入/拔出事件,这些事件由MountService通过Intent广播发送。例如外部存储卡插入后,MountService就会发送Intent.ACTION_MEDIA_MOUNTED消息。
MountService由SystemServer启动,我们简单看看它的构造函数:
class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks, Watchdog.Monitor { public MountService(Context context) { ......... mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25, null); ......... mConnectorThread = new Thread(mConnector, VOLD_TAG); .......... } private void start() { mConnectorThread.start(); ......... }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
从MountService的启动情况来看,对于Vold进程而言,我们需要关注的就是MountService利用NativeDaemonConnector建立与”vold”的连接,使得Vold进程能够与Android框架进行沟通了。
Android中的Service启动后,基本上都是靠事件驱动的,因此无法按一个有序的流程进行全面的介绍,比较好的方式还是了解整体架构后,分析一个具体的示例。
因此接下来我们以设备插入为例,分析一下MountService的主要工作。
根据上文的分析,我们知道当设备插入后,Kernel发送消息是的NetlinkManager能够收到Uevent。然后,NetlinkManager将会构造出NetlinkEvent,并通知VolumeManager进行处理。
在VolumeManager中,利用handleBlockEvent根据事件的类型进行相应的处理,我们截取设备添加时的处理代码:
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) { ........ switch (evt->getAction()) { case NetlinkEvent::Action::kAdd: { for (auto source : mDiskSources) { if (source->matches(eventPath)) { ....... auto disk = new android::vold::Disk(eventPath, device, source->getNickname(), flags); disk->create(); mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk)); break; } } break; } ........}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
我们看看Disk的代码:
Disk::Disk(const std::string& eventPath, dev_t device, const std::string& nickname, int flags) : mDevice(device), mSize(-1), mNickname(nickname), mFlags(flags), mCreated( false), mJustPartitioned(false) { mId = StringPrintf("disk:%u,%u", major(device), minor(device)); mEventPath = eventPath; mSysPath = StringPrintf("/sys/%s", eventPath.c_str()); mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str()); CreateDeviceNode(mDevPath, mDevice);}status_t Disk::create() { CHECK(!mCreated); mCreated = true; notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags)); readMetadata(); readPartitions(); return OK;}status_t Disk::readMetadata() { ........... notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRIu64, mSize)); notifyEvent(ResponseCode::DiskLabelChanged, mLabel); notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath); return OK;}status_t Disk::readPartitions() { ............. Table table = Table::kUnknown; bool foundParts = false; for (auto line : output) { .......... if (!strcmp(token, "DISK")) { const char* type = strtok(nullptr, kSgdiskToken); if (!strcmp(type, "mbr")) { table = Table::kMbr; } else if (!strcmp(type, "gpt")) { table = Table::kGpt; } } else if (!strcmp(token, "PART")) { ......... dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i); if (table == Table::kMbr) { ........ createPublicVolume(partDevice); ........ } else if (table == Table::kGpt) { const char* typeGuid = strtok(nullptr, kSgdiskToken); const char* partGuid = strtok(nullptr, kSgdiskToken); if (!strcasecmp(typeGuid, kGptBasicData)) { createPublicVolume(partDevice); } else if (!strcasecmp(typeGuid, kGptAndroidExpand)) { createPrivateVolume(partDevice, partGuid); } } } } ...............}void Disk::createPublicVolume(dev_t device) { auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device)); if (mJustPartitioned) { LOG(DEBUG) << "Device just partitioned; silently formatting"; vol->setSilent(true); vol->create(); vol->format("auto"); vol->destroy(); vol->setSilent(false); } mVolumes.push_back(vol); vol->setDiskId(getId()); vol->create();}status_t VolumeBase::create() { CHECK(!mCreated); mCreated = true; status_t res = doCreate(); notifyEvent(ResponseCode::VolumeCreated, StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str())); setState(State::kUnmounted); return res;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
上面列举了创建Disk和Volume的代码,注意到进行实际工作后,均会调用notifyEvent函数。
我们接下来就看看notifyEvent函数的用途:
void Disk::notifyEvent(int event, const std::string& value) { VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event, StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);}
现在我们看看CommandListener的sendBroadcast函数(实际定义于父类的父类SocketListener中):
void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { SocketClientCollection safeList; safeList.clear(); pthread_mutex_lock(&mClientsLock); SocketClientCollection::iterator i; for (i = mClients->begin(); i != mClients->end(); ++i) { SocketClient* c = *i; c->incRef(); safeList.push_back(c); } pthread_mutex_unlock(&mClientsLock); while (!safeList.empty()) { i = safeList.begin(); SocketClient* c = *i; safeList.erase(i); if (c->sendMsg(code, msg, addErrno, false)) { SLOGW("Error sending broadcast (%s)", strerror(errno)); } c->decRef(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
从上面的代码,我们知道将由MountService来处理socket中的数据。
前面我们已经知道,MountService创建NativeDaemonConnector来封装socket相关的操作,在创建NativeDaemonConnector时需要传入回调接口。当NativeDaemonConnector收到数据后,通过回调接口进行通知。
MountService继承了INativeDaemonConnectorCallbacks,我们看看它的onEvent函数:
@Overridepublic boolean onEvent(int code, String raw, String[] cooked) { synchronized (mLock) { return onEventLocked(code, raw, cooked); }}private boolean onEventLocked(int code, String raw, String[] cooked) { switch (code) { case VoldResponseCode.DISK_CREATED: { if (cooked.length != 3) break; final String id = cooked[1]; int flags = Integer.parseInt(cooked[2]); if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false) || mForceAdoptable) { flags |= DiskInfo.FLAG_ADOPTABLE; } mDisks.put(id, new DiskInfo(id, flags)); break; } ........... case VoldResponseCode.VOLUME_CREATED: { final String id = cooked[1]; final int type = Integer.parseInt(cooked[2]); final String diskId = TextUtils.nullIfEmpty(cooked[3]); final String partGuid = TextUtils.nullIfEmpty(cooked[4]); final DiskInfo disk = mDisks.get(diskId); final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid); mVolumes.put(id, vol); onVolumeCreatedLocked(vol); break; } ........... } return true;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
从上面的代码可以看出,MountService收到DISK_CREATED消息后,仅会记录DiskInfo;收到VOLUME_CREATED消息后,还需要调用onVolumeCreatedLocked函数作进一步地处理。
private void onVolumeCreatedLocked(VolumeInfo vol) { ......... if (vol.type == VolumeInfo.TYPE_EMULATED) { ....... } else if (vol.type == VolumeInfo.TYPE_PUBLIC) { mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); } else if (vol.type == VolumeInfo.TYPE_PRIVATE) { ........ } else { ....... }}
class MountServiceHandler extends Handler { public MountServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { ........ final VolumeInfo vol = (VolumeInfo) msg.obj; if (isMountDisallowed(vol)) { Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy"); break; } try { mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId); } catch (NativeDaemonConnectorException ignored) { } break; ........ } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
注意消息重新发回到了CommandListener,根据前面的代码的分析我们知道,在FrameworkListener中将利用dispatchCommand根据类型,调用不同Command的runCommand方法,此处将调用volumeCommand的运行方法:
int CommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) { ......... else if (cmd == "mount" && argc > 2) { std::string id(argv[2]); auto vol = vm->findVolume(id); if (vol == nullptr) { return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false); } int mountFlags = (argc > 3) ? atoi(argv[3]) : 0; userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1; vol->setMountFlags(mountFlags); vol->setMountUserId(mountUserId); int res = vol->mount(); if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) { vm->setPrimary(vol); } return sendGenericOkFail(cli, res); } .........}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
我们跟进一下VolumeBase的mount函数:
void VolumeBase::setState(State state) { mState = state; notifyEvent(ResponseCode::VolumeStateChanged, StringPrintf("%d", mState));}
在MountService的onEvent函数中,将再次处理VolumeStateChanged事件,实际上就是发送ACTION_MEDIA_MOUNTED广播。
上述的整个过程略去了大量的细节,但看起来仍很琐碎。不过,若是理解了前面介绍CommandListener时,分析的整个通信架构,那么这些流程的大致方向是比较好理解的。
结束语
Vold进程的主要内容基本上就是这些,在实际的工作中大多数人应该不会接触到这个进程。但是它整个架构是非常具有参考意义的,很清晰地阐释了Android中的框架层、Native层以及Kernel是如何交互的。Android中还有许多重要部分也采用了类似的架构,比较明显的就是netd。因此,以Vold入手进行分析,重在理解这种通信架构。