Android -- Vold机制简要分析

article/2025/10/2 19:14:16
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进程创建:
service vold /system/bin/vold \--blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \--fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0class coresocket vold stream 0660 root mountsocket cryptd stream 0660 root mountioprio be 2
在创建Vold进程时,会为它创建两个socket,用于与framework进行信息交互。其他的细节,参考之前zygote进程创建的介绍。

二、进入Vold主程序

Vold的主程序在/system/vold目录中,直接看main.cpp::main()函数:
int main(int argc, char** argv) {setenv("ANDROID_LOG_TAGS", "*:v", 1);android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));LOG(INFO) << "Vold 3.0 (the awakening) firing up";LOG(VERBOSE) << "Detected support for:"<< (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")<< (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")<< (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "");VolumeManager *vm;CommandListener *cl;CryptCommandListener *ccl;NetlinkManager *nm;parse_args(argc, argv);sehandle = selinux_android_file_context_handle();if (sehandle) {selinux_android_set_sehandle(sehandle);}// Quickly throw a CLOEXEC on the socket we just inherited from initfcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);//拿到init进程创建的名为vold的socket句柄,并为它设置FD_CLOEXEC标志位fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);//同上mkdir("/dev/block/vold", 0755);//创建/dev/block/vold目录,存放所有subdisk和sdcard的挂载点信息/* For when cryptfs checks and mounts an encrypted filesystem */klog_set_level(6);/* Create our singleton managers *///1、创建VolumeManagerif (!(vm = VolumeManager::Instance())) {LOG(ERROR) << "Unable to create VolumeManager";exit(1);}//2、创建NetlinkManagerif (!(nm = NetlinkManager::Instance())) {LOG(ERROR) << "Unable to create NetlinkManager";exit(1);}if (property_get_bool("vold.debug", false)) {vm->setDebug(true);}//3、创建CommandListener、CryptCommandListenercl = new CommandListener();ccl = new CryptCommandListener();vm->setBroadcaster((SocketListener *) cl);nm->setBroadcaster((SocketListener *) cl);//4、启动VolumeManagerif (vm->start()) {PLOG(ERROR) << "Unable to start VolumeManager";exit(1);}if (process_config(vm)) {PLOG(ERROR) << "Error reading configuration... continuing anyways";}//6、启动NetlinkManager,处理来自kernel的usb/sdcard插拔消息if (nm->start()) {PLOG(ERROR) << "Unable to start NetlinkManager";exit(1);}//7、应用层往/sys/block目录下的uevent文件写"add\n"指令,触发kernel向上发送Uevent消息,获取设备的当前信息coldboot("/sys/block");
//    coldboot("/sys/class/switch");/** Now that we're up, we can respond to commands*///8、启动CommandListenerif (cl->startListener()) {PLOG(ERROR) << "Unable to start CommandListener";exit(1);}//9、启动CryptCommandListenerif (ccl->startListener()) {PLOG(ERROR) << "Unable to start CryptCommandListener";exit(1);}// Eventually we'll become the monitoring threadwhile(1) {sleep(1000);}LOG(ERROR) << "Vold exiting";exit(0);
}
从代码中的注释可知,Vold主要创了三个对象:NetlinkManager、VolumeManager和CommandListener。根据Vold的架构图,现分别对它们的创建及启动过程进行分析。

(1)、NetlinkManager

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

1、NetlinkManager::Instance():
NetlinkManager *NetlinkManager::Instance() {if (!sInstance)sInstance = new NetlinkManager();return sInstance;
}NetlinkManager::NetlinkManager() {mBroadcaster = NULL; //type:SocketListener*,用来进行socket通信
}
这里使用了单例模式来构建NetlinkManager对象,构造函数中只是简单地初始化了成员变量。
2、NetlinkManager::setBroadcaster():
    cl = new CommandListener();nm->setBroadcaster((SocketListener *) cl);void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
setBroadcaster()函数也很简单,为mBroadcast进行赋值。
3、NetlinkManager::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;//创建地址族为PF_NETLINK的socket,与Kernel进行通信;也可以为AF_NETLINK.参照Linux Netlink机制资料if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,NETLINK_KOBJECT_UEVENT)) < 0) {//NETLINK_KOBJECT_UEVENT协议:Kernel messages to userspaceSLOGE("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);//mHandler、mSock都是成员变量.mHandler对象主要保存了套接字的文件描述符,供后续使用if (mHandler->start()) {//startListener通过父类方法,在mSock上监听连接请求SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));goto out;}return 0;out:close(mSock);return -1;
}
start()方法中创建了一个句柄值为mSock的套接字,用来和kernel通信;而实际具体的socket信息交互是由NetlinkHandler处理的。
NetlinkHandler的实现有一套继承机制,其实际继承关系如图所示:


按照继承关系,分析它的构建过程:
mHandler = new NetlinkHandler(mSock);
NetlinkHandler::NetlinkHandler(int listenerSocket) :NetlinkListener(listenerSocket) {
}
/* temporary version until we can get Motorola to update their* ril.so.  Their prebuilt ril.so is using this private class* so changing the NetlinkListener() constructor breaks their ril.*/
NetlinkListener::NetlinkListener(int socket) :SocketListener(socket, false) {mFormat = NETLINK_FORMAT_ASCII;
}
SocketListener::SocketListener(const char *socketName, bool listen) {init(socketName, -1, listen, false);
}
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {mListen = listen;//是否是监听端,这里为falsemSocketName = socketName;//保存socket的名字mSock = socketFd;//保存socket的句柄值,与Kernel通信mUseCmdNum = useCmdNum;pthread_mutex_init(&mClientsLock, NULL);mClients = new SocketClientCollection();//集合对象,保存类型SocketClient为的变量;保存了socket通信中的客户端对象
}
再看NetlinkHandler::start()方法:
int NetlinkHandler::start() {return this->startListener();
}
实际调用的是SocketListener::startListener():
int SocketListener::startListener() {return startListener(4);
}int SocketListener::startListener(int backlog) {if (!mSocketName && mSock == -1) {SLOGE("Failed to start unbound listener");errno = EINVAL;return -1;} else if (mSocketName) {if ((mSock = android_get_control_socket(mSocketName)) < 0) {SLOGE("Obtaining file descriptor socket '%s' failed: %s",mSocketName, strerror(errno));return -1;}SLOGV("got mSock = %d for %s", mSock, mSocketName);fcntl(mSock, F_SETFD, FD_CLOEXEC);}if (mListen && listen(mSock, backlog) < 0) {//如果mListen为true,则监听该socket;表明此socket应该是服务端SLOGE("Unable to listen on socket (%s)", strerror(errno));return -1;} else if (!mListen)//实际传入的mListen值为false,走此分支mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));//创建一个SocketClient对象,并保存到集合中if (pipe(mCtrlPipe)) {SLOGE("pipe failed (%s)", strerror(errno));return -1;}if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {//创建一个线程,在其中调用threadStart(),根据mListen值,等待接收来自Kernel的Uevent消息SLOGE("pthread_create (%s)", strerror(errno));return -1;}return 0;
}
创建一个SocketListener对象,并加入mClients中;随后,创建一个线程,并调用SocketListener::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);if (mListen) {//如果我们是服务端,则将该socket的套接字加入到可读监控队列中max = mSock;FD_SET(mSock, &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) {// NB: calling out to an other object with mClientsLock held (safe)int fd = (*it)->getSocket();FD_SET(fd, &read_fds);if (fd > max) {max = fd;}}pthread_mutex_unlock(&mClientsLock);SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {//如果监测到read_fds集合中有socket可读if (errno == EINTR)continue;SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);sleep(1);continue;} 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)) {//监听端客户端连接请求struct sockaddr addr;socklen_t alen;int c;do {alen = sizeof(addr);c = accept(mSock, &addr, &alen);//接受client的连接请求,c是代表client套接字的文件描述符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));//根据c,创建一个SocketListener对象,并加入到队列中pthread_mutex_unlock(&mClientsLock);}/* Add all active clients to the pending list first */pendingList.clear();pthread_mutex_lock(&mClientsLock);for (it = mClients->begin(); it != mClients->end(); ++it) {SocketClient* c = *it;// NB: calling out to an other object with mClientsLock held (safe)int fd = c->getSocket();if (FD_ISSET(fd, &read_fds)) {//遍历所有保存的客户端;如果该socket可读,将该套接字加入到队列中pendingList.push_back(c);c->incRef();}}pthread_mutex_unlock(&mClientsLock);/* Process the pending list, since it is owned by the thread,* there is no need to lock it */while (!pendingList.empty()) {/* Pop the first item from the list */it = pendingList.begin();SocketClient* c = *it;pendingList.erase(it);/* Process it, if false is returned, remove from list */if (!onDataAvailable(c)) {//客户端收到数据,调用NetlinkListener::onDataAvailable()处理release(c, false);//数据处理失败,则释放socket资源}c->decRef();}}
}
我们初始化NetlinkListener时传入的mListener值为false;上述代码中,会遍历所有保存的客户端socket,如果收到数据,则进行处理。

调用NetlinkListener::onDataAvailable():
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{int socket = cli->getSocket();ssize_t count;uid_t uid = -1;bool require_group = true;if (mFormat == NETLINK_FORMAT_BINARY_UNICAST) {require_group = false;}count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,mBuffer, sizeof(mBuffer), require_group, &uid));//从kernel获取Uevent消息,保存到mBuffer中if (count < 0) {if (uid > 0)LOG_EVENT_INT(65537, uid);SLOGE("recvmsg failed (%s)", strerror(errno));return false;//读取失败,则返回false,上层调用则会关闭socket资源}NetlinkEvent *evt = new NetlinkEvent();//事件的代码封装if (evt->decode(mBuffer, count, mFormat)) {解析Uevent数据,填充到NetlinkEvent对象中onEvent(evt);//NetlinkHandler::onEvent()} else if (mFormat != NETLINK_FORMAT_BINARY) {// Don't complain if parseBinaryNetlinkMessage returns false. That can// just mean that the buffer contained no messages we're interested in.SLOGE("Error decoding NetlinkEvent");}delete evt;return true;
}
先通过socket获取到Uevent数据,再解析并将信息封装到NetlinkEvent对象中。NetlinkHandler::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")) {//如果Uevent是block子系统vm->handleBlockEvent(evt);//进入VolumeManager中处理;此处和VolumeManager进行交互}
}
如果事件是和外部存储有关,则调用VolumeManager::handleBlockEvent()处理该事件;这里,就看到了NetlinkManager和VolumeManager之间进行数据流动的处理了。

(2)、VolumeManager

Vold使用VolumeManager的过程和NetlinkManager类似,也是三步:
  1. vm= VolumeManager::Instance()
  2. vm->setBroadcaster((SocketListener *) cl)
  3. vm->start()
1、2步与NetlinkManager的处理类似:
//单例模式
VolumeManager *VolumeManager::Instance() {if (!sInstance)sInstance = new VolumeManager();return sInstance;
}VolumeManager::VolumeManager() {mDebug = false;mActiveContainers = new AsecIdCollection();mBroadcaster = NULL;mUmsSharingCount = 0;mSavedDirtyRatio = -1;// set dirty ratio to 0 when UMS is activemUmsDirtyRatio = 0;
}void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
直接看VolumeManager::start()的处理:
int VolumeManager::start() {// Always start from a clean slate by unmounting everything in// directories that we own, in case we crashed.unmountAll();//在处理外部设备事件之前,先重置所有状态// Assume that we always have an emulated volume on internal// storage; the framework will decide if it should be mounted.CHECK(mInternalEmulated == nullptr);mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(new android::vold::EmulatedVolume("/data/media"));mInternalEmulated->create();//预先设定/data/media,由framework决定是否mount;EmulatedVolume和VolumeBase之间是继承关系,代表不同类型的Volumereturn 0;
}
再直接看NetlinkManager和VolumeManager之间信息处理的调用过程:
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {std::lock_guard<std::mutex> lock(mLock);if (mDebug) {LOG(VERBOSE) << "----------------";LOG(VERBOSE) << "handleBlockEvent with action " << (int) evt->getAction();evt->dump();}std::string eventPath(evt->findParam("DEVPATH"));//设备路径std::string devType(evt->findParam("DEVTYPE"));//设备类型if (devType != "disk") return;//主次设备号,两者可以描述一个具体设备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: {for (auto source : mDiskSources) {if (source->matches(eventPath)) {// For now, assume that MMC devices are SD, and that// everything else is USBint flags = source->getFlags();if (major == kMajorBlockMmc) {flags |= android::vold::Disk::Flags::kSd;} else {flags |= android::vold::Disk::Flags::kUsb;}auto disk = new android::vold::Disk(eventPath, device,source->getNickname(), flags);//将信息封装成Disk对象,表示一个检测到的物理设备disk->create();//Disk::create()mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));//加进集合break;}}break;}case NetlinkEvent::Action::kChange: {LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";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;}}
}
向上层发送各种类型的消息都是通过notifyEvent()函数处理的,其实际就是通过socket来发送的。
同时,Vold的主函数中还有一个重要的函数调用process_config():
static int process_config(VolumeManager *vm) {std::string path(android::vold::DefaultFstabPath()); //获取到vold.fstab文件路径fstab = fs_mgr_read_fstab(path.c_str());//解析.fstab文件,并返回封装的fstab对象if (!fstab) {PLOG(ERROR) << "Failed to open default fstab " << path;return -1;}/* Loop through entries looking for ones that vold manages */bool has_adoptable = false;for (int i = 0; i < fstab->num_entries; i++) {if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {if (fs_mgr_is_nonremovable(&fstab->recs[i])) {LOG(WARNING) << "nonremovable no longer supported; ignoring volume";continue;}std::string sysPattern(fstab->recs[i].blk_device);std::string nickname(fstab->recs[i].label);int flags = 0;if (fs_mgr_is_encryptable(&fstab->recs[i])) {flags |= android::vold::Disk::Flags::kAdoptable;has_adoptable = true;}if (fs_mgr_is_noemulatedsd(&fstab->recs[i])|| property_get_bool("vold.debug.default_primary", false)) {flags |= android::vold::Disk::Flags::kDefaultPrimary;//is Primary?}vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(new VolumeManager::DiskSource(sysPattern, nickname, flags)));//添加一个VolumeManager::DiskSource对象,保存了一些信息}}property_set("vold.has_adoptable", has_adoptable ? "1" : "0");return 0;
}
//解析.fstab配置文件,并返回一个fstab结构对象
struct fstab *fs_mgr_read_fstab(const char *fstab_path)
{FILE *fstab_file;int cnt, entries;ssize_t len;size_t alloc_len = 0;char *line = NULL;const char *delim = " \t";char *save_ptr, *p;struct fstab *fstab = NULL;struct fs_mgr_flag_values flag_vals;
#define FS_OPTIONS_LEN 1024char tmp_fs_options[FS_OPTIONS_LEN];fstab_file = fopen(fstab_path, "r");if (!fstab_file) {ERROR("Cannot open file %s\n", fstab_path);return 0;}entries = 0;while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {/* if the last character is a newline, shorten the string by 1 byte */if (line[len - 1] == '\n') {line[len - 1] = '\0';}/* Skip any leading whitespace */p = line;while (isspace(*p)) {p++;}/* ignore comments or empty lines */if (*p == '#' || *p == '\0')continue;entries++;}if (!entries) {ERROR("No entries found in fstab\n");goto err;}/* Allocate and init the fstab structure */fstab = calloc(1, sizeof(struct fstab));fstab->num_entries = entries;fstab->fstab_filename = strdup(fstab_path);fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));fseek(fstab_file, 0, SEEK_SET);cnt = 0;//解析fstab中每行的内容,并进行封装while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {/* if the last character is a newline, shorten the string by 1 byte */if (line[len - 1] == '\n') {line[len - 1] = '\0';}/* Skip any leading whitespace */p = line;while (isspace(*p)) {p++;}/* ignore comments or empty lines */if (*p == '#' || *p == '\0')continue;/* If a non-comment entry is greater than the size we allocated, give an* error and quit.  This can happen in the unlikely case the file changes* between the two reads.*/if (cnt >= entries) {ERROR("Tried to process more entries than counted\n");break;}if (!(p = strtok_r(line, delim, &save_ptr))) {ERROR("Error parsing mount source\n");goto err;}fstab->recs[cnt].blk_device = strdup(p);if (!(p = strtok_r(NULL, delim, &save_ptr))) {ERROR("Error parsing mount_point\n");goto err;}fstab->recs[cnt].mount_point = strdup(p);//mount的位置if (!(p = strtok_r(NULL, delim, &save_ptr))) {ERROR("Error parsing fs_type\n");goto err;}fstab->recs[cnt].fs_type = strdup(p);if (!(p = strtok_r(NULL, delim, &save_ptr))) {ERROR("Error parsing mount_flags\n");goto err;}tmp_fs_options[0] = '\0';fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,tmp_fs_options, FS_OPTIONS_LEN);/* fs_options are optional */if (tmp_fs_options[0]) {fstab->recs[cnt].fs_options = strdup(tmp_fs_options);} else {fstab->recs[cnt].fs_options = NULL;}if (!(p = strtok_r(NULL, delim, &save_ptr))) {ERROR("Error parsing fs_mgr_options\n");goto err;}fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,&flag_vals, NULL, 0);fstab->recs[cnt].key_loc = flag_vals.key_loc;fstab->recs[cnt].verity_loc = flag_vals.verity_loc;fstab->recs[cnt].length = flag_vals.part_length;fstab->recs[cnt].label = flag_vals.label;fstab->recs[cnt].partnum = flag_vals.partnum;fstab->recs[cnt].swap_prio = flag_vals.swap_prio;fstab->recs[cnt].zram_size = flag_vals.zram_size;cnt++;}fclose(fstab_file);free(line);return fstab;err:fclose(fstab_file);free(line);if (fstab)fs_mgr_free_fstab(fstab);return NULL;
}
fstab文件是Linux下配置分区的一个文件,这部分后续补充......总之,就是解析fstab文件后,会根据配置信息创建DiskSource对象,加入到VolumeManager::mDiskSource中。

(3 )、CommandListener

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


CommandListener的创建过程跟NetlinkManager类似:
CommandListener::CommandListener() :FrameworkListener("vold", true) {//vold是socket名称,init.rc文件中声明的一个socket资源,用于和framework通信registerCmd(new DumpCmd());//注册不同的命令对象,保存到mCommands成员中;同时,创建Cmd对象时,会保存一个字符串标识(一般是上层下发命令中的第一个字符串),用于后续区分不同的命令registerCmd(new VolumeCmd());//标识:volumeregisterCmd(new AsecCmd());//标识:asecregisterCmd(new ObbCmd());//标识:obbregisterCmd(new StorageCmd());//标识:storageregisterCmd(new FstrimCmd());//标识:fstrim
}
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :SocketListener(socketName, true, withSeq) {init(socketName, withSeq);
}
SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {init(socketName, -1, listen, useCmdNum);
}void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {mListen = listen;//是否是监听端,与前面不同,这里为truemSocketName = socketName;//保存socket的名字"vold",与MountService通信mSock = socketFd;//保存socket的句柄值mUseCmdNum = useCmdNum;pthread_mutex_init(&mClientsLock, NULL);mClients = new SocketClientCollection();//集合对象
}
下面直接看CommandListener->startListener():
int SocketListener::startListener() {return startListener(4);
}int SocketListener::startListener(int backlog) {if (!mSocketName && mSock == -1) {SLOGE("Failed to start unbound listener");errno = EINVAL;return -1;} else if (mSocketName) {if ((mSock = android_get_control_socket(mSocketName)) < 0) {//名为"vold"的socket的句柄值SLOGE("Obtaining file descriptor socket '%s' failed: %s",mSocketName, strerror(errno));return -1;}SLOGV("got mSock = %d for %s", mSock, mSocketName);fcntl(mSock, F_SETFD, FD_CLOEXEC);}if (mListen && listen(mSock, backlog) < 0) {//mListener为true,则监听该socketSLOGE("Unable to listen on socket (%s)", strerror(errno));return -1;} else if (!mListen)//mListener为false,走此分支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)) {//创建一个线程,在其中调用threadStart(),并在mSock代表的套接字上等待客户端的连接请求SLOGE("pthread_create (%s)", strerror(errno));return -1;}return 0;
}
在CommandListener监听流程中,mListene为true;表示这一端是监听侧,等待Client的连接请求。这种场景下,MountService就是这里描述的客户端。MountService在创建过程,会通过创建NativeDaemonConnector对象,去连接名为"vold"的socket,这样两者就可以通信了。

SocketListener::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);if (mListen) {//如果我们是服务端,则将该socket的套接字加入到可读监控队列中max = mSock;FD_SET(mSock, &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) {// NB: calling out to an other object with mClientsLock held (safe)int fd = (*it)->getSocket();FD_SET(fd, &read_fds);if (fd > max) {max = fd;}}pthread_mutex_unlock(&mClientsLock);SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {//如果集合read_fds中有socket可读if (errno == EINTR)监测continue;SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);sleep(1);continue;} 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)) {//mListener值实际为true;服务端,等待客户端连接请求struct sockaddr addr;socklen_t alen;int c;do {alen = sizeof(addr);c = accept(mSock, &addr, &alen);//接受MountService发起的socket连接请求,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));//根据c,创建一个SocketListener对象,并加入到集合中pthread_mutex_unlock(&mClientsLock);}/* Add all active clients to the pending list first */pendingList.clear();pthread_mutex_lock(&mClientsLock);for (it = mClients->begin(); it != mClients->end(); ++it) {SocketClient* c = *it;// NB: calling out to an other object with mClientsLock held (safe)int fd = c->getSocket();if (FD_ISSET(fd, &read_fds)) {//遍历保存的所有客户端socket,如果对应的socket可读,则将该套接字加入到队列中pendingList.push_back(c);c->incRef();}}pthread_mutex_unlock(&mClientsLock);/* Process the pending list, since it is owned by the thread,* there is no need to lock it */while (!pendingList.empty()) {/* Pop the first item from the list */it = pendingList.begin();SocketClient* c = *it;pendingList.erase(it);/* Process it, if false is returned, remove from list */if (!onDataAvailable(c)) {//有数据来,调用FrameworkListener::onDataAvailable()处理release(c, false);}c->decRef();}}
}
由于mListen值的变化(此时为true),处理流程有所不同。首先作为服务端,会等待Client的连接请求;如果有连接请求,并有数据发送过来,则通过
onDataAvailable()处理。根据继承关系,此处调用FrameworkListener::onDataAvailable():
bool FrameworkListener::onDataAvailable(SocketClient *c) {char buffer[CMD_BUF_SIZE];int len;len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));//从MountService接收指令数据,存入buffer中if (len < 0) {SLOGE("read() failed (%s)", strerror(errno));return false;} else if (!len)return false;if(buffer[len-1] != '\0')SLOGW("String is not zero-terminated");int offset = 0;int i;for (i = 0; i < len; i++) {if (buffer[i] == '\0') {/* IMPORTANT: dispatchCommand() expects a zero-terminated string */dispatchCommand(c, buffer + offset);//命令分发处理offset = i + 1;}}return true;
}
如果消息不为空,则调用FrameworkListener::dispatchCommand()进行处理:
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {FrameworkCommandCollection::iterator i;int argc = 0;char *argv[FrameworkListener::CMD_ARGS_MAX];char tmp[CMD_BUF_SIZE];char *p = data;char *q = tmp;char *qlimit = tmp + sizeof(tmp) - 1;bool esc = false;bool quote = false;bool haveCmdNum = !mWithSeq;memset(argv, 0, sizeof(argv));memset(tmp, 0, sizeof(tmp));while(*p) {if (*p == '\\') {if (esc) {if (q >= qlimit)goto overflow;*q++ = '\\';esc = false;} elseesc = true;p++;continue;} else if (esc) {if (*p == '"') {if (q >= qlimit)goto overflow;*q++ = '"';} else if (*p == '\\') {if (q >= qlimit)goto overflow;*q++ = '\\';} else {cli->sendMsg(500, "Unsupported escape sequence", false);goto out;}p++;esc = false;continue;}if (*p == '"') {if (quote)quote = false;elsequote = true;p++;continue;}if (q >= qlimit)goto overflow;*q = *p++;if (!quote && *q == ' ') {*q = '\0';if (!haveCmdNum) {char *endptr;int cmdNum = (int)strtol(tmp, &endptr, 0);if (endptr == NULL || *endptr != '\0') {cli->sendMsg(500, "Invalid sequence number", false);goto out;}cli->setCmdNum(cmdNum);haveCmdNum = true;} else {if (argc >= CMD_ARGS_MAX)goto overflow;argv[argc++] = strdup(tmp);}memset(tmp, 0, sizeof(tmp));q = tmp;continue;}q++;}*q = '\0';if (argc >= CMD_ARGS_MAX)goto overflow;argv[argc++] = strdup(tmp);
#if 0for (int k = 0; k < argc; k++) {SLOGD("arg[%d] = '%s'", k, argv[k]);}
#endifif (quote) {cli->sendMsg(500, "Unclosed quotes error", false);goto out;}if (errorRate && (++mCommandCount % errorRate == 0)) {/* ignore this command - let the timeout handler handle it */SLOGE("Faking a timeout");goto out;}for (i = mCommands->begin(); i != mCommands->end(); ++i) {FrameworkCommand *c = *i;if (!strcmp(argv[0], c->getCommand())) {//获取命令的第一个参数(即标识),遍历mCommands,找到符合的Command对象去执行runCommand();例如,如果标识是volume,则执行VolumeCommand的runCommand()函数处理下发的指令if (c->runCommand(cli, argc, argv)) {//重要,调用不同Command类型的、我们之前注册过的对象调用runCommand()方法处理指令(定义在CommandListener中)SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));}goto out;}}cli->sendMsg(500, "Command not recognized", false);
out:int j;for (j = 0; j < argc; j++)free(argv[j]);return;overflow:LOG_EVENT_INT(78001, cli->getUid());cli->sendMsg(500, "Command too long", false);goto out;
首先,根据上层下发的指令中的信息去得到一个符合要求的Command命令对象,然后执行相应的runCommand()方法来处理不同的指令。我们下发的指令有一定的规则,一般第一个字符串是标识,用以获得不同的Command对象;第二个参数一般是我们需要进行的操作命令;后续的参数一般都是下发的用以完成操作的数据。一般情况,格式类似于:
volume mount /mnt/sda/sda1
字符串之间以空格分开。
到此,Vold机制及原理的分析就基本结束了。Vold与MountService的交互后续再介绍。

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

文中图片资源下载:http://download.csdn.net/detail/csdn_of_coder/9702463

http://chatgpt.dhexx.cn/article/qkx7D85I.shtml

相关文章

Android Vold简介(一)

Vold(volume Daemon)&#xff0c;即Volume守护进程&#xff0c;用来管理Android中存储类的热拔插事件&#xff0c;处于Kernel和Framework之间&#xff0c;是两个层级连接的桥梁。先来看一下Vold在Android系统的整体架构。 该图主要包含了Framework和Vold进程的&#xff0c;Ke…

vold

一、Vold工作机制 Vold是Volume Daemon的缩写&#xff0c;它是Android平台中外部存储系统的管控中心&#xff0c;是管理和控制Android平台外部存储设备的后台进程。其功能主要包括&#xff1a;SD卡的插拔事件检测、SD卡挂载、卸载、格式化等。 如上图所示&#xff0c;Vold中的…

Vold工作流程分析学习

一 Vold工作机制分析 vold进程&#xff1a;管理和控制Android平台外部存储设备&#xff0c;包括SD插拨、挂载、卸载、格式化等&#xff1b; vold进程接收来自内核的外部设备消息。 Vold框架图如下&#xff1a; Vold接收来自内核的事件&#xff0c;通过netlink机制。 Netlink 是…

Android vold介绍

目录 1. 前言2. vold概述3. vold初始化|- -vm->start()|- -process_config|- -hardware::configureRpcThreadpool|- -vold::VoldNativeService::start|- -nm->start() 4. StorageManagerService|- -SM与vold建立关联|- - -startService(serviceClass) |- -StorageManager…

<Android开发> Android vold - 第一篇 vold前言简介

本系列主要介绍 Android vold&#xff0c;分为以下篇章 &#xff1c;Android开发&#xff1e; Android vold - 第一篇 vold前言简介 &#xff1c;Android开发&#xff1e; Android vold - 第二篇 vold 的main()函数简介 &#xff1c;Android开发&#xff1e; Android vold - 第…

Vold原理介绍

一、 Vold简介 Android中Vold是volume Daemon&#xff0c;即Volume守护进程&#xff0c;用来管理Android中存储类的热拔插事件。这里的热插拔涉及的场景如&#xff1a; 1. 手机usb以MTP或者传输照片方式插拔PC端后磁盘数据的挂卸载&#xff1b; 2. 设备开关机过程中存储设备各分…

C语言Switch....case用法

概述 C语言switch语句通常用于多个条件判断&#xff0c;根据不同情况执行不同的代码块。它的使用形式如下&#xff1a; switch&#xff08;表达式&#xff09; { case 常量表达式1&#xff1a; 语句序列1 break; case 常量表达式2&#xff1a; 语句序列2 break; ...... default…

MySQL 入门:Case 语句很好用

引言 MySQL CASE 是一个 MySQL 语句查询关键字&#xff0c;它定义了处理循环概念以执行条件集并使用 IF ELSE 返回匹配案例的方式。 MySQL 中的 CASE 是一种控制语句&#xff0c;它验证条件案例集&#xff0c;并在第一个案例满足 else 值时显示值并退出循环。 如果没有找到 T…

Shell 编程之 case 语句

一、case 语句 1、case 语句概述 (1)case 语句的作用 使用 case 语句改写 if 多分支可以使脚本结构更加清晰、层次分明。针对变量的不同取 值&#xff0c;执行不同的命令序列。 2、case 语句的结构: case 变量值 in 模式 1) 命令序列 1 ;; 模式 2) 命令序列 2 ;; *…

【shell】case实现简单的系统工具箱

case实现简单的系统工具箱 case实现简单的系统工具箱 #!/usr/bin/bash #system manage #by racon 2020-04-19menu() {cat <<-EOF########################################## h. help ## f. disk partition ## d. filesystem mount ## m. memory ## u. system lo…

SQL CASE语句的使用

SQL CASE语句的使用 CASE是一个控制流语句&#xff0c;其作用与IF-THEN-ELSE语句非常相似&#xff0c;可根据数据选择值。 CASE语句遍历条件并在满足第一个条件时返回值。 因此&#xff0c;一旦条件成立&#xff0c;它将短路&#xff0c;从而忽略后面的子句并返回结果。 正如我…

数据库 case 用法

【转载】:数据库中case when 的用法 CASE WHEN 及 SELECT CASE WHEN的用法Case具有两种格式。简单Case函数和Case搜索函数。 1.简单Case函数 CASE sex WHEN 1 THEN 男 WHEN 2 THEN 女 ELSE 其他 END 2.Case搜索函数 CASE WHEN sex 1 THEN 男 WHEN sex 2 THEN 女 ELSE 其他 …

SQL之CASE WHEN用法详解

简单CASE WHEN函数&#xff1a; CASE SCORE WHEN A THEN 优 ELSE 不及格 END CASE SCORE WHEN B THEN 良 ELSE 不及格 END CASE SCORE WHEN C THEN 中 ELSE 不及格 END 等同于&#xff0c;使用CASE WHEN条件表达式函数实现&#xff1a; CASE WHEN SCORE A THEN 优WHEN SCORE…

switch case语法

文章目录 switch case组合不要拿青龙偃月刀去削苹果case的作用是什么&#xff1f;break的作用是什么&#xff1f;case后面的值有什么要求吗&#xff1f;case语句的排列顺序问题default语句相关问题使用case语句的一些注意事项 switch case组合 基本语法结构 switch(整型常量/…

SQL中case的使用方法

Case具有两种格式。简单Case函数和Case搜索函数。 1.简单Case函数 CASE sex WHEN 1 THEN 男WHEN 2 THEN 女 ELSE 其他 END2.Case搜索函数 CASE WHEN sex 1 THEN 男WHEN sex 2 THEN 女ELSE 其他 END3.简单case函数 VS case搜索函数 这两种方式&#xff0c;可以实现相同的功…

[转载]常用CASE工具介绍

[转载]常用CASE工具介绍转载自&#xff1a;http://www.cnblogs.com/powerlc/archive/2006/01/12/315959.html 一&#xff0c;概述 今天, 代码变得日益简单, 在Model的指导下, 思想, 设计, 分析都变得异常重要。企业业务建模工具, 产品非常多, 特别是在MDA日益流行的今天. Work…

CASE语句的使用方法

CASE语句有两种&#xff1a; 一种是case [column] when&#xff0c;指定了判断条件所在的列。 另一种是case when [column]&#xff0c;因为条件在子句中所以能对任意列进行判断。 本例建立一个员工表&#xff0c;有员工id&#xff08;id&#xff09;&#xff0c;员工姓名&am…

常用CASE工具介绍 ZZ

常用CASE工具介绍 一&#xff0c;概述  今天, 代码变得日益简单, 在Model的指导下, 思想, 设计, 分析都变得异常重要。企业业务建模工具, 产品非常多, 特别是在MDA日益流行的今天. WorkFlow是典型的业务及流程建模。 二&#xff0c;软件开发CASE工具简介   (一)图稿绘制&…

CASE 工具有哪些

CASE 工具 CASE工具设置的软件应用程序。这使用为自动的SDLC活动。 CASE工具所使用的软件项目经理,分析师和工程师开发的软件系统. 有许多CASE工具做软件开发生命周期的各个阶段,如工具,设计工具,项目管理工具,数据库管理工具,文档工具分析. 为了得到所需的结果,CASE工具…

网络:简述路由算法之动态路由算法

网络&#xff1a;简述路由算法之动态路由算法 在计算机网络中&#xff0c;路由器的一个很重要责任就是要在端对端的节点中找出一条最佳路径出来&#xff0c;通过自己与相邻节点之间的信息&#xff0c;来计算出从自己位置到目的节点之间的最佳线路&#xff0c;这种算法我们可以理…