一 Vold工作機制分析
vold進程:管理和控制Android平台外部存儲設備,包括SD插撥、掛載、卸載、格式化等;
vold進程接收來自內核的外部設備消息。
Vold框架圖如下:
Vold接收來自內核的事件,通過netlink機制。
Netlink 是一種特殊的 socket;
Netlink 是一種在內核與用戶應用間進行雙向數據傳輸的非常好的方式,用戶態應用使用標准的socket API 就可以使用 netlink 提供的強大功能;
Netlink是一種異步通信機制,在內核與用戶態應用之間傳遞的消息保存在socket緩存隊列中;
內核通過Netlink發送uEvent格式消息给用戶空間程序;外部設備發生變化,Kernel發送uevent消息。
二 Vold進程启動過程
service vold /system/bin/vold class core socket vold stream 0660 root mount ioprio be 2
vold進程執行過程:
\system\vold\main.cpp
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); }
process_config解析vold.fstab文件:
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; }
vold.fstab文件:
導出一個我的手機裏面的vold.fstab文件 內容:
dev_mount sdcard /mnt/sdcard emmc@fat /devices/platform/goldfish_mmc.0 /devices/platform/mtk-sd.0/mmc_host dev_mount external_sdcard /mnt/sdcard/external_sd auto /devices/platform/goldfish_mmc.1 /devices/platform/mtk-sd.1/mmc_host
vold.fstab格式是:
type label mount_point part sysfs_path sysfs_path
sysfs_path可以有多個 part指定分區個數,如果是auto沒有分區
三 Vold中各模塊分析
在vold進程main函數中創建了很多的類實例,並將其启動。
int main() { …… vm->start(); nm->start(); cl->startListener(); }
這些類對象之間是如何的,還需要現弄清楚每個類的職責和工作機制。
1 NetlinkManager模塊
NetlinkManager模塊接收從Kernel發送的Uevent消息,解析轉換成NetlinkEvent對象;再將此NetlinkEvent對象傳遞给VolumeManager處理。
此模塊相關的類結構:
下面從start開始,看起如何對Kernel的Uevent消息進行監控的。
NetlinkManager start:
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; }
NetlinkHandler start:
int NetlinkHandler::start() { //父類startListener return this->startListener(); }
NetlinkListener 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); }
線程監聽Kernel netlink發送的UEvent消息:
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 } } } }
Netlink消息處理:
在消息循環中調用onDataAvailable處理消息,onDataAvailable是個虛函數,NetlinkListener重寫了此函數。
NetlinkListener onDataAvailable消息處理:
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重寫了此函數。
NetlinkHandler NetlinkEvent數據處理:
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處理。
2 VolumeManager模塊
此模塊管理所有掛載的設備節點以及相關操作執行;下面是VolumeManager模塊類結構圖:
DirectVolume:一個實體存儲設備在代碼中的抽象。
SocketListenner:創建線程,監聽socket。
這裏VolumeManager構造的SocketListenner與NetlinkManager構造的SocketListenner有所不同的:
NetlinkManager構造的SocketListenner:Kernel與Vold通信;
VolumeManager構造的SocketListenner:Native Vold與Framework MountService 通信;
VolumeManager構造的SocketListenner,由vold進程main函數中創建的CommandListener:
int main() { …… CommandListener *cl; cl = new CommandListener(); vm->setBroadcaster((SocketListener *) cl); //启動CommandListener監聽 cl->startListener(); }
VolumeManager工作流程:
//從main函數中的start開始: int VolumeManager::start() { return 0; }
NetlinkManager接收到Kernel通過netlink發送的Uevent消息,轉化成了NetlinkEvent消息,再傳遞给了VolumeManager處理。
NetlinkManager與VolumeManager交互流程圖:
VolumeManager處理消息 handleBlockEvent:
從NetlinkManager到VolumeManager代碼過程
函數執行從onEvent到handleBlockEvent:
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; } } }
將消息交给各個Volume對象處理:DirectVolume
從VolumeManager到所管理的Volume對象
這裏的Volume为其派生類DirectVolume。
int DirectVolume::handleBlockEvent(NetlinkEvent *evt) { //有狀態變化設備路徑 const char *dp = evt->findParam("DEVPATH"); PathCollection::iterator it; for (it = mPaths->begin(); it != mPaths->end(); ++it) { //匹配 設備路徑 if (!strncmp(dp, *it, strlen(*it))) { int action = evt->getAction(); const char *devtype = evt->findParam("DEVTYPE"); //動作判斷 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); //創建設備節點 createDeviceNode(nodepath, major, minor); if (!strcmp(devtype, "disk")) { //添加disk handleDiskAdded(dp, evt); } else { //添加分區 handlePartitionAdded(dp, evt); } } else if (action == NetlinkEvent::NlActionRemove) { } else if (action == NetlinkEvent::NlActionChange) { } else { SLOGW("Ignoring non add/remove/change event"); } return 0; } } }
为什麼要讓VolumeManager中的每一個Volume對象都去處理SD狀態變換消息,
每一個Volume可能對應多個Path;即一個掛載點對應多個物理設備。
抽象存儲設備DirectVolume 動作狀態變化處理:
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); }
消息通知Framework層存儲設備狀態變化:
類繼承關系:
發送消息通知Framework層是在SocketListener中完成;
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); }
這裏工作的SocketListener是VolumeManager的,SocketListener的派生類CommandListener,
用來與Framework交互的,監聽Socket消息。通過VolumeManager中調用sendBroadcast,與CommandListener模塊進行交互。
由此需要清楚CommandListener模塊工作流程。
3 CommandListener模塊
CommandListener監聽Socket,使Vold與Framework層進行進程通信;
其相關類繼承結構圖如下:
CommandListener工作流程:
int main() { VolumeManager *vm; CommandListener *cl; NetlinkManager *nm; //CommandListener 創建vold socket監聽上層消息 cl = new CommandListener(); //作为VolumeManager與NetlinkManager的Broadcaster vm->setBroadcaster((SocketListener *) cl); nm->setBroadcaster((SocketListener *) cl); //启動CommandListener監聽 cl->startListener(); …… }
CommandListener實例的創建:構造函數
CommandListener構造函數:
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) { mCommands = new FrameworkCommandCollection(); mWithSeq = withSeq; }
注冊Command:
void FrameworkListener::registerCmd(FrameworkCommand *cmd) { mCommands->push_back(cmd); }
SocketListener構造函數:
SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) { //mListen = true 正常的socket監聽 mListen = listen; //socket 名稱“vold” mSocketName = socketName; mSock = -1; mUseCmdNum = useCmdNum; //初始化锁 pthread_mutex_init(&mClientsLock, NULL); //構造Listener Client List mClients = new SocketClientCollection(); }
CommandListener启動 startListener:
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启動的線程監聽Socket消息,接收到的消息處理onDataAvailable。
CommandListener父類FrameworkCommand重寫了此函數。
CommandListener監聽Socket消息處理:
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; }
Command執行處理:以VolumeCommand为例
CommandListener::VolumeCmd::VolumeCmd() : VoldCommand("volume") { } int CommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) { //獲取VolumeManager實例 VolumeManager *vm = VolumeManager::Instance(); //Action判斷 傳遞给VolumeManager處理 if (!strcmp(argv[1], "list")) { return vm->listVolumes(cli); } else if (!strcmp(argv[1], "debug")) { vm->setDebug(!strcmp(argv[2], "on") ? true : false); } else if (!strcmp(argv[1], "mount")) { rc = vm->mountVolume(argv[2]); } else if (!strcmp(argv[1], "unmount")) { rc = vm->unmountVolume(argv[2], force, revert); } else if (!strcmp(argv[1], "format")) { rc = vm->formatVolume(argv[2]); } else if (!strcmp(argv[1], "share")) { rc = vm->shareVolume(argv[2], argv[3]); } else if (!strcmp(argv[1], "unshare")) { rc = vm->unshareVolume(argv[2], argv[3]); } else if (!strcmp(argv[1], "shared")) { …… return 0; }
CommandListener使用Command模式。
CommandListener接收到來自Framework層得消息,派發命令處理,再傳遞给VolumeManager處理。
VolumeManager中Action處理:
int VolumeManager::unmountVolume(const char *label) { //查找Volume Volume *v = lookupVolume(label); //Volume執行動作 return v-> unmountVol (); } //VolumeAction處理: int Volume::unmountVol(bool force, bool revert) { doUnmount(Volume::SEC_STG_SECIMGDIR, force); …… } int Volume::doUnmount(const char *path, bool force) { …… //systemcall umount(path); }
整個Vold處理過程框架圖如下: