vold进程接收来自内核的外部设备消息,用于管理和控制Android平台外部存储设备,包括SD插拨、挂载、卸载、格式化等;当外部设备发生变化时,内核通过Netlink发送uEvent格式的消息给用户空间程序,Netlink 是一种基于异步通信机制,在内核与用户应用间进行双向数据传输的特殊 socket,用户态应用使用标准的socket API 就可以使用 netlink 提供的强大功能;
int main() { VolumeManager *vm; CommandListener *cl; NetlinkManager *nm; //创建vold设备文件夹 mkdir("/dev/block/vold", 0755); //初始化Vold相关的类实例 single vm = VolumeManager::Instance(); nm = NetlinkManager::Instance(); //CommandListener 创建vold socket监听上层消息 cl = new CommandListener(); vm->setBroadcaster((SocketListener *) cl); nm->setBroadcaster((SocketListener *) cl); //启动VolumeManager vm->start(); //根据配置文件/etc/vold.fstab 初始化VolumeManager process_config(vm); //启动NetlinkManager socket监听内核发送uevent nm->start(); //向/sys/block/目录下所有设备uevent文件写入“add\n”, //触发内核sysfs发送uevent消息 coldboot("/sys/block"); //启动CommandListener监听vold socket cl->startListener(); // Eventually we'll become the monitoring thread while(1) { sleep(1000); } exit(0); }/etc/vold.fstab的内容如下:
####################### ## Regular device mount ## ## Format: dev_mount <label> <mount_point> <part> <sysfs_path1...> ## label - Label for the volume ## mount_point - Where the volume will be mounted ## part - Partition # (1 based), or 'auto' for first usable partition. ## <sysfs_path> - List of sysfs paths to source devices ###################### # Mounts the first usable partition of the specified device #dev_mount sdcard /mnt/sdcard auto /block/mmcblk0 dev_mount internal /mnt/sdcard 19 /devices/platform/sprd-sdhci.3/mmc_host/mmc3 dev_mount sdcard /mnt/sdcard/external auto /devices/platform/sprd-sdhci.0/mmc_host/mmc0
static int process_config(VolumeManager *vm) { //打开vold.fstab的配置文件 fp = fopen("/etc/vold.fstab", "r") //解析vold.fstab 配置存储设备的挂载点 while(fgets(line, sizeof(line), fp)) { const char *delim = " \t"; char *type, *label, *mount_point, *part, *mount_flags, *sysfs_path; type = strtok_r(line, delim, &save_ptr) label = strtok_r(NULL, delim, &save_ptr) mount_point = strtok_r(NULL, delim, &save_ptr) //判断分区 auto没有分区 part = strtok_r(NULL, delim, &save_ptr) if (!strcmp(part, "auto")) { //创建DirectVolume对象 相关的挂载点设备的操作 dv = new DirectVolume(vm, label, mount_point, -1); } else { dv = new DirectVolume(vm, label, mount_point, atoi(part)); } //添加挂载点设备路径 while ((sysfs_path = strtok_r(NULL, delim, &save_ptr))) { dv->addPath(sysfs_path) } //将DirectVolume 添加到VolumeManager管理 vm->addVolume(dv); } fclose(fp); return 0; }
NetlinkManager *NetlinkManager::Instance() { if (!sInstance) //采用单例模式创建NetlinkManager实例 sInstance = new NetlinkManager(); return sInstance; }
int NetlinkManager::start() { //netlink使用的socket结构 struct sockaddr_nl nladdr; //初始化socket数据结构 memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = getpid(); nladdr.nl_groups = 0xffffffff; //创建socket PF_NETLINK类型 mSock = socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT); //配置socket 大小 setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz); setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on); //bindsocket地址 bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr); //创建NetlinkHandler 传递socket标识,并启动 mHandler = new NetlinkHandler(mSock); mHandler->start(); return 0; }在启动NetlinkManager时,初始化socket用于创建NetlinkManager的属性变量mHandle实例,并启动NetlinkHandler,NetlinkHandler继承于NetlinkListener,NetlinkListener又继承于SocketListener,因此在构造NetlinkHandler实例时首先构造SocketListener及NetlinkListener,构造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(); }NetlinkListener构造函数:
NetlinkListener::NetlinkListener(int socket) : SocketListener(socket, false) { mFormat = NETLINK_FORMAT_ASCII; }NetlinkHandler构造函数:
NetlinkHandler::NetlinkHandler(int listenerSocket) : NetlinkListener(listenerSocket) { }因此构造NetlinkHandler实例过程仅仅创建良一个socket客户端连接。
int NetlinkHandler::start() { //父类startListener return this->startListener(); }SocketListener start:
int SocketListener::startListener() { //NetlinkHandler mListen为false if (mListen && listen(mSock, 4) < 0) { return -1; } else if (!mListen){ //mListen为false 用于netlink消息监听 //创建SocketClient作为SocketListener 的客户端 mClients->push_back(new SocketClient(mSock, false, mUseCmdNum)); } //创建匿名管道 pipe(mCtrlPipe); //创建线程执行函数threadStart 参this pthread_create(&mThread, NULL, SocketListener::threadStart, this); }
void *SocketListener::threadStart(void *obj) { //参数转换 SocketListener *me = reinterpret_cast<SocketListener *>(obj); me->runListener(); pthread_exit(NULL); return NULL; }SocketListener 线程消息循环:
void SocketListener::runListener() { //SocketClient List SocketClientCollection *pendingList = new SocketClientCollection(); while(1) { fd_set read_fds; //mListen 为false if (mListen) { max = mSock; FD_SET(mSock, &read_fds); } //加入一组文件描述符集合 选择fd最大的max FD_SET(mCtrlPipe[0], &read_fds); 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); //监听文件描述符是否变化 rc = select(max + 1, &read_fds, NULL, NULL, NULL); //匿名管道被写,退出线程 if (FD_ISSET(mCtrlPipe[0], &read_fds)) break; //mListen 为false if (mListen && FD_ISSET(mSock, &read_fds)) { //mListen 为ture 表示正常监听socket struct sockaddr addr; do { //接收客户端连接 c = accept(mSock, &addr, &alen); } while (c < 0 && errno == EINTR); //此处创建一个客户端SocketClient加入mClients列表中,异步延迟处理 pthread_mutex_lock(&mClientsLock); mClients->push_back(new SocketClient(c, true, mUseCmdNum)); pthread_mutex_unlock(&mClientsLock); } /* Add all active clients to the pending list first */ pendingList->clear(); //将所有有消息的Client加入到pendingList中 pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { int fd = (*it)->getSocket(); if (FD_ISSET(fd, &read_fds)) { pendingList->push_back(*it); } } pthread_mutex_unlock(&mClientsLock); //处理所有消息 while (!pendingList->empty()) { it = pendingList->begin(); SocketClient* c = *it; pendingList->erase(it); //处理有数据发送的socket 虚函数 if (!onDataAvailable(c) && mListen) { //mListen为false } } } }在消息循环中调用onDataAvailable处理消息,onDataAvailable是个虚函数,NetlinkListener重写了此函数。
bool NetlinkListener::onDataAvailable(SocketClient *cli) { //获取socket id int socket = cli->getSocket(); //接收netlink uevent消息 count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv( socket, mBuffer, sizeof(mBuffer), &uid)); //解析uevent消息为NetlinkEvent消息 NetlinkEvent *evt = new NetlinkEvent(); evt->decode(mBuffer, count, mFormat); //处理NetlinkEvent onEvent虚函数 onEvent(evt); }将接收的Uevent数据转化成NetlinkEvent数据,调用onEvent处理,NetlinkListener子类NetlinkHandler重写了此函数。
void NetlinkHandler::onEvent(NetlinkEvent *evt) { //获取VolumeManager实例 VolumeManager *vm = VolumeManager::Instance(); //设备类型 const char *subsys = evt->getSubsystem(); //将消息传递给VolumeManager处理 if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); } }
NetlinkManager通过NetlinkHandler将接收到Kernel内核发送的Uenvet消息,转化成了NetlinkEvent结构数据传递给VolumeManager处理,uevent消息的上传流程:
VolumeManager类关系图:
DirectVolume是一个实体存储设备的抽象,通过系统调用直接操作存储设备。VolumeManager的SocketListenner与NetlinkManager的SocketListenner有所不同的:
NetlinkManager构造的SocketListenner:Kernel与Vold通信;
VolumeManager构造的SocketListenner:Native Vold与Framework MountService 通信;
NetlinkManager与VolumeManager交互流程图:
VolumeManager *VolumeManager::Instance() { if (!sInstance) sInstance = new VolumeManager(); return sInstance; } VolumeManager::VolumeManager() { mDebug = false; mVolumes = new VolumeCollection(); mActiveContainers = new AsecIdCollection(); mBroadcaster = NULL; mUmsSharingCount = 0; mSavedDirtyRatio = -1; // set dirty ratio to 20 when UMS is active mUmsDirtyRatio = 20; mVolManagerDisabled = 0; }
int VolumeManager::start() { return 0; }VolumeManager启动过程什么都没有做。
void NetlinkHandler::onEvent(NetlinkEvent *evt) { …… //将消息传递给VolumeManager处理 if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); } }
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) { //有状态变化设备路径 const char *devpath = evt->findParam("DEVPATH"); //遍历VolumeManager中所管理Volume对象(各存储设备代码抽象) for (it = mVolumes->begin(); it != mVolumes->end(); ++it) { if (!(*it)->handleBlockEvent(evt)) { hit = true; break; } }VolumeManager将消息交给各个DirectVolume对象处理:
int DirectVolume::handleBlockEvent(NetlinkEvent *evt) { //从NetlinkEvent消息中取出有状态变化设备路径 const char *dp = evt->findParam("DEVPATH"); PathCollection::iterator it; //遍历所有的存储设备 for (it = mPaths->begin(); it != mPaths->end(); ++it) { //根据存储设备路径进行匹配 if (!strncmp(dp, *it, strlen(*it))) { /* 从NetlinkEvent消息中取出设备变化的动作 */ int action = evt->getAction(); /* 从NetlinkEvent消息中取出设备类型 */ const char *devtype = evt->findParam("DEVTYPE"); SLOGE("DirectVolume::handleBlockEvent() evt's DEVPATH= %s DEVTYPE= %s action= %d",dp, devtype, action); //设备插入 if (action == NetlinkEvent::NlActionAdd) { int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); char nodepath[255]; snprintf(nodepath,sizeof(nodepath), "/dev/block/vold/%d:%d",major, minor); SLOGE("DirectVolume::handleBlockEvent() NlActionAdd - /dev/block/vold/%d:%d\n", major, minor); if (createDeviceNode(nodepath, major, minor)) { SLOGE("Error making device node '%s' (%s)", nodepath,strerror(errno)); } //新增磁盘 if (!strcmp(devtype, "disk")) { handleDiskAdded(dp, evt); //新增分区 } else { handlePartitionAdded(dp, evt); } //设备移除 } else if (action == NetlinkEvent::NlActionRemove) { SLOGE("Volume partition %d:%d removed", atoi(evt->findParam("MAJOR")), atoi(evt->findParam("MINOR"))); //删除磁盘 if (!strcmp(devtype, "disk")) { handleDiskRemoved(dp, evt); //删除分区 } else { handlePartitionRemoved(dp, evt); } //设备改变 } else if (action == NetlinkEvent::NlActionChange) { //磁盘变化 if (!strcmp(devtype, "disk")) { handleDiskChanged(dp, evt); //分区变化 } else { handlePartitionChanged(dp, evt); } } else { SLOGW("Ignoring non add/remove/change event"); } return 0; } } errno = ENODEV; return -1; }
每一个Volume可能对应多个Path;即一个挂载点对应多个物理设备,因此VolumeManager中的每一个Volume对象都需要处理SD状态变换消息,当新增一个disk时:
void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) { //主次设备号 mDiskMajor = atoi(evt->findParam("MAJOR")); mDiskMinor = atoi(evt->findParam("MINOR")); //设备分区情况 const char *tmp = evt->findParam("NPARTS"); mDiskNumParts = atoi(tmp); if (mDiskNumParts == 0) { //没有分区,Volume状态为Idle setState(Volume::State_Idle); } else { //有分区未加载,设置Volume状态Pending setState(Volume::State_Pending); } //格式化通知msg:"Volume sdcard /mnt/sdcard disk inserted (179:0)" char msg[255]; snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",getLabel(), getMountpoint(), mDiskMajor, mDiskMinor); //调用VolumeManager中的Broadcaster——>CommandListener 发送此msg mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,msg, false); }将新增一个disk以消息的方式通过CommandListener向MountService发送,由于CommandListener继承于FrameworkListener,而FrameworkListener又继承于SocketListener,CommandListener和FrameworkListener都没用重写父类的sendBroadcast方法,因此消息是通过 SocketListener的sendBroadcast函数向上层发送的,VolumeManager通知上层的消息流程图:
void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { pthread_mutex_lock(&mClientsLock); //遍历所有的消息接收时创建的Client SocketClient // SocketClient将消息通过socket(“vold”)通信 for (i = mClients->begin(); i != mClients->end(); ++i) { (*i)->sendMsg(code, msg, addErrno, false); } pthread_mutex_unlock(&mClientsLock); }
CommandListener::CommandListener() : FrameworkListener("vold", true) { //注册Framework发送的相关命令 Command模式 registerCmd(new DumpCmd()); registerCmd(new VolumeCmd()); registerCmd(new AsecCmd()); registerCmd(new ObbCmd()); registerCmd(new StorageCmd()); registerCmd(new XwarpCmd()); registerCmd(new CryptfsCmd()); }父类FrameworkListener构造:
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) : SocketListener(socketName, true, withSeq) { init(socketName, withSeq); }直接调用init函数;
void FrameworkListener::init(const char *socketName, bool withSeq) { mCommands = new FrameworkCommandCollection(); errorRate = 0; mCommandCount = 0; mWithSeq = withSeq; }SocketListener的构造函数:
SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) { init(socketName, -1, listen, useCmdNum); }同样调用init函数进行初始化:
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(); }构造CommandListener对象过程中,首先注册了各种命令,并创建良一个socket客户端连接。命令注册过程:
void FrameworkListener::registerCmd(FrameworkCommand *cmd) { mCommands->push_back(cmd); }将各种命令存放到mCommand列表中。
int SocketListener::startListener() { //mSocketName = “Vold” mSock = android_get_control_socket(mSocketName); //NetlinkHandler mListen为true 监听socket if (mListen && < 0) { return -1; } else if (!mListen){ mClients->push_back(new SocketClient(mSock, false, mUseCmdNum)); } //创建匿名管道 pipe(mCtrlPipe); //创建线程执行函数threadStart 参数this pthread_create(&mThread, NULL, SocketListener::threadStart, this); } void *SocketListener::threadStart(void *obj) { SocketListener *me = reinterpret_cast<SocketListener *>(obj); me->runListener(); } void SocketListener::runListener() { //SocketClient List SocketClientCollection *pendingList = new SocketClientCollection(); while(1) { fd_set read_fds; //mListen 为true if (mListen) { max = mSock; FD_SET(mSock, &read_fds); } //加入一组文件描述符集合 选择fd最大的max select有关 FD_SET(mCtrlPipe[0], &read_fds); 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); //监听文件描述符是否变化 rc = select(max + 1, &read_fds, NULL, NULL, NULL); //匿名管道被写,退出线程 if (FD_ISSET(mCtrlPipe[0], &read_fds)) break; //mListen 为true if (mListen && FD_ISSET(mSock, &read_fds)) { //mListen 为ture 表示正常监听socket struct sockaddr addr; do { c = accept(mSock, &addr, &alen); } while (c < 0 && errno == EINTR); //创建一个客户端SocketClient,加入mClients列表中 到异步延迟处理 pthread_mutex_lock(&mClientsLock); mClients->push_back(new SocketClient(c, true, mUseCmdNum)); pthread_mutex_unlock(&mClientsLock); } /* Add all active clients to the pending list first */ pendingList->clear(); //将所有有消息的Client加入到pendingList中 pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { int fd = (*it)->getSocket(); if (FD_ISSET(fd, &read_fds)) { pendingList->push_back(*it); } } pthread_mutex_unlock(&mClientsLock); /* Process the pending list, since it is owned by the thread,*/ while (!pendingList->empty()) { it = pendingList->begin(); SocketClient* c = *it; //处理有数据发送的socket if (!onDataAvailable(c) && mListen) { //mListen为true …… } } } }
启动CommandListener监听过程其实就是创建一个监听的工作线程,用于监听客户端即MountService发过来的命令。
当接收到MountService发送的消息时,将回调onDataAvailable函数进行处理。CommandListener父类FrameworkCommand重写了消息处理onDataAvailable函数。
bool FrameworkListener::onDataAvailable(SocketClient *c) { char buffer[255]; //读取socket消息 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) { char *argv[FrameworkListener::CMD_ARGS_MAX]; //解析消息内容 命令 参数 …… //执行对应的消息 for (i = mCommands->begin(); i != mCommands->end(); ++i) { FrameworkCommand *c = *i; //匹配命令 if (!strcmp(argv[0], c->getCommand())) { //执行命令 c->runCommand(cli, argc, argv); goto out; } } out: return; }对于 VolumeCommand,其runCommand函数为:
int CommandListener::VolumeCmd::runCommand(SocketClient *cli,int argc, char **argv) { dumpArgs(argc, argv, -1); if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false); return 0; } VolumeManager *vm = VolumeManager::Instance(); int rc = 0; //查看存储设备 if (!strcmp(argv[1], "list")) { return vm->listVolumes(cli); } else if (!strcmp(argv[1], "debug")) { if (argc != 3 || (argc == 3 && (strcmp(argv[2], "off") && strcmp(argv[2], "on")))) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume debug <off/on>", false); return 0; } vm->setDebug(!strcmp(argv[2], "on") ? true : false); //挂载存储设备 } else if (!strcmp(argv[1], "mount")) { if (argc != 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false); return 0; } rc = vm->mountVolume(argv[2]); //卸载存储设备 } else if (!strcmp(argv[1], "unmount")) { if (argc < 3 || argc > 4 || ((argc == 4 && strcmp(argv[3], "force")) && (argc == 4 && strcmp(argv[3], "force_and_revert")))) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force|force_and_revert]", false); return 0; } bool force = false; bool revert = false; if (argc >= 4 && !strcmp(argv[3], "force")) { force = true; } else if (argc >= 4 && !strcmp(argv[3], "force_and_revert")) { force = true; revert = true; } rc = vm->unmountVolume(argv[2], force, revert); //格式化存储设备 } else if (!strcmp(argv[1], "format")) { if (argc != 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path>", false); return 0; } rc = vm->formatVolume(argv[2]); //共享存储设备 } else if (!strcmp(argv[1], "share")) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume share <path> <method>", false); return 0; } rc = vm->shareVolume(argv[2], argv[3]); } else if (!strcmp(argv[1], "unshare")) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unshare <path> <method>", false); return 0; } rc = vm->unshareVolume(argv[2], argv[3]); } else if (!strcmp(argv[1], "shared")) { bool enabled = false; if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume shared <path> <method>", false); return 0; } if (vm->shareEnabled(argv[2], argv[3], &enabled)) { cli->sendMsg( ResponseCode::OperationFailed, "Failed to determine share enable state", true); } else { cli->sendMsg(ResponseCode::ShareEnabledResult, (enabled ? "Share enabled" : "Share disabled"), false); } return 0; } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume cmd", false); } if (!rc) { cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded", false); } else { int erno = errno; rc = ResponseCode::convertFromErrno(); cli->sendMsg(rc, "volume operation failed", true); } return 0; }针对不同的命令,调用VolumeManager的不同函数对存储设备进行操作,如挂载磁盘命令,则调用mountVolume函数:
int VolumeManager::mountVolume(const char *label) { //根据卷标查找Volume Volume *v = lookupVolume(label); if (!v) { errno = ENOENT; return -1; } return v->mountVol(); }调用Volume的mountVol函数来挂载设备。MountServcie发送命令的流程图:
整个Vold处理过程框架图如下: