Android7.0 Vold 進程工作機制分析之整體流程
一、Vold簡介
Vold是Volume Daemon的縮寫,負責管理和控制Android平臺外部存儲設備,包括SD插撥、掛載、卸載、格式化等。它是通過init進程解析init.rc腳本所啓動的進程.它處於Native層.
二、基礎架構
這裏引用Gityuan博客的一張圖。
SystermServer進程和Vold進程是通過Socket進行通信的,Vold進程和Kernel是通過Netlink 進行通信的,Netlink 是一種特殊的Socket。
相關介紹:Netlink是linux提供的用於內核和用戶態進程之間的通信方式,也能用於用戶空間的兩個進程通信,通過這個機制,位於用戶空間的進程,可接收來自Kernel的一些信息(例如Vold中用到的USB或SD的插拔消息),同時應用層也可通過Netlink向Kernel發送一些控制命令。
先大體的簡單介紹一下流程:
1.由SystemServer進程發起掛載/卸載請求
運行在SystemServer進程的MountService通過NativeDaemonConnector給CommandListener發送請求,CommandListener再通知VolumeManager進行實際操作
2.由Kernel發起掛載/卸載請求
Kernel通過Netlink發送請求(傳遞uevent)給NetlinkManager,NetlinkManager通過內部的線程NetlinkHandler交給VolumeManager進行實際操作,然後VolumeManager通過CommandListener通知MountService
在分別仔細介紹着兩個流程之前,瞭解下Vold進程的啓動流程,先把握整體流程再細化。
三、Vold進程啓動流程
Vold進程代碼路徑:system/vold/
主要類的路徑(方便查閱)
system/vold/目錄下
main.cpp————————————system/vold/main.cpp
NetlinkManager.cpp———————–system/vold/NetlinkManager.cpp
NetlinkHandler.cpp————————system/vold/NetlinkHandler.cpp
VoldCommand.cpp————————system/vold/VoldCommand.cpp
VolumeBase.cpp—————————system/vold/VolumeBase.cpp
VolumeManager.cpp———————–system/vold/VolumeManager.cpp
system/core/libsysutils/src/目錄下
SocketListener.cpp————————–system/core/libsysutils/src/SocketListener.cpp
NetlinkListener.cpp————————–system/core/libsysutils/src/NetlinkListener.cpp
FrameworkListener.cpp——————–system/core/libsysutils/src/FrameworkListener.cpp
FrameworkCommand.cpp——————system/core/libsysutils/src//FrameworkCommand.cpp
system/core/include/sysutils/目錄下
對應着system/core/libsysutils/src/目錄的h文件
貼上我繪製的時序圖(縮放瀏覽器可以放大查看或者在新標籤頁打開)
分步驟給大家詳細介紹下:
1.main()
Vold進程代碼位於system/vold/目錄下,從main.cpp開始,後續的截圖我只截取主要的代碼
在main方法裏,主要做以下幾件事情
①初始化VolumeManager ,CommandListener ,NetlinkManager 三個類的實例
②給VolumeManager 和NetlinkManager 設置CommandListener 實例,用作後續監聽兩個Socket,用得是設計模式中的Command(命令)模式
③啓動VolumeManager ,CommandListener ,NetlinkManager
④解析Vold的配置文件fstab
⑤做一次冷啓動
int main(int argc, char** argv) {
......
VolumeManager *vm;
CommandListener *cl;
NetlinkManager *nm;
//創建文件夾/dev/block/vold
mkdir("/dev/block/vold", 0755);
//用於cryptfs檢查,並mount加密的文件系統
klog_set_level(6);
//獲取VolumeManager的單例
if (!(vm = VolumeManager::Instance())) {
LOG(ERROR) << "Unable to create VolumeManager";
exit(1);
}
//獲取NetlinkManager的單例
if (!(nm = NetlinkManager::Instance())) {
LOG(ERROR) << "Unable to create NetlinkManager";
exit(1);
}
//實例化
cl = new CommandListener();
//設置socket監聽對象
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
//啓動VolumeManager
if (vm->start()) {
PLOG(ERROR) << "Unable to start VolumeManager";
exit(1);
}
//解析Vold的配置文件fstab,初始化VolumeManager
if (process_config(vm)) {
PLOG(ERROR) << "Error reading configuration... continuing anyways";
}
//啓動NetlinkManager
if (nm->start()) {
PLOG(ERROR) << "Unable to start NetlinkManager";
exit(1);
}
// 冷啓動,vold錯過了一些uevent,重新觸發。向/sys/block的uevent文件寫入”add\n” 字符觸發內核發送Uevent消息,相當執行了一次熱插拔。
coldboot("/sys/block");
//啓動CommandListener
if (cl->startListener()) {
PLOG(ERROR) << "Unable to start CommandListener";
exit(1);
}
......
}
第④步
如果vold.fstab解析無誤,VolueManager將創建具體的Volume,若vold.fstab解析不存在或者打開失敗,Vold將會讀取Linux內核中的參數,此時如果參數中存在SDCARD(也就是SD的默認路徑),VolumeManager則會創建AutoVolume,如果不存在這個默認路徑那麼就不會創建。
它的格式對應如下:
type———————–掛載命令
lable———————–標籤
mount_point ————掛載點
part ———————–第幾個分區
sysfs_path—————設備的sysfs paths
sysfs_path可以有多個 part指定分區個數,如果是auto沒有分區
第⑤步
coldboot方法會調用do_coldboot方法,往/sys/block目錄寫入add\n事件。
static void do_coldboot(DIR *d, int lvl) {
struct dirent *de;
int dfd, fd;
dfd = dirfd(d);
fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
if(fd >= 0) {
//寫入add\n事件
write(fd, "add\n", 4);
close(fd);
}
2.new CommandListener()
前面有講過,CommandListener是用來監聽Socket的,監聽Vold與Framework層的進程通信。它的關係圖如下:
它繼承自FrameworkListener,FrameworkListener繼承自SocketListener.
路徑:
CommandListener.cpp———————–system/vold/CommandListener.cpp
這一步創建CommandListener的實例,則構造方法被調用
CommandListener::CommandListener() :FrameworkListener("vold", true) {
//註冊多條指令
registerCmd(new DumpCmd());
registerCmd(new VolumeCmd());
registerCmd(new AsecCmd());
registerCmd(new ObbCmd());
registerCmd(new StorageCmd());
registerCmd(new FstrimCmd());
registerCmd(new AppFuseCmd());
}
調用registerCmd方法,註冊一些指令,重寫的是父類的registerCmd方法
void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
//添加元素
mCommands->push_back(cmd);
}
會把指令添加到mCommands 中,mCommands 是FrameworkCommandCollection的實例,在FrameworkListener.h文件中聲明的
class FrameworkListener : public SocketListener {
......
private:
FrameworkCommandCollection *mCommands;
......
}
這裏附上一張NetlinkManager家族的簡單類圖,幫助後續理解
3.vm->start()
啓動VolumeManager
VolumeManager模塊負責管理所有掛載的設備節點以及相關操作的實際執行,
路徑:
VolumeManager.cpp———————–system/vold/VolumeManager.cpp
int VolumeManager::start() {
//卸載所有設備
unmountAll();
//智能指針創建一個VolumeBase實例
mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
new android::vold::EmulatedVolume("/data/media"));
//調用create()方法
mInternalEmulated->create();
return 0;
}
①卸載所有設備
②創建一個VolumeBase實例
③調用VolumeBase的create方法
這裏附上一張VolumeManager家族的簡單類圖,幫助後續理解
看下第③個步驟
3.1 mInternalEmulated->create()
status_t VolumeBase::create() {
mCreated = true;
status_t res = doCreate();
//向VolumeManager發送VolumeCreated命令
notifyEvent(ResponseCode::VolumeCreated,StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
//設置已卸載狀態
setState(State::kUnmounted);
return res;
}
向VolumeManager發送了VolumeCreated的消息,然後設置狀態爲已卸載.
這個notifyEvent的實現如下
3.2 notifyEvent
void VolumeBase::notifyEvent(int event, const std::string& value) {
if (mSilent) return;
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);
}
獲取單例VolumeManager對象,然後獲取到SocketListener對象調用sendBroadcast方法
sendBroadcast方法的實現如下
在safelist列表中添加SocketClient,然後調用sendMsg方法
void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
SocketClientCollection safeList;
//首先添加所有活動的SockClient到安全列表中
safeList.clear();
for (i = mClients->begin(); i != mClients->end(); ++i) {
SocketClient* c = *i;
c->incRef();
//添加
safeList.push_back(c);
}
while (!safeList.empty()) {
/* Pop the first item from the list */
i = safeList.begin();
SocketClient* c = *i;
safeList.erase(i);
//調用SockClient的sendMSg方法發送消息
if (c->sendMsg(code, msg, addErrno, false)) {
SLOGW("Error sending broadcast (%s)", strerror(errno));
}
c->decRef();
}
}
SockClient
路徑:
SockClient.cpp————————system/core/libsysutils/src/SockClient.cpp
調用sendMsg方法經過層層跳轉,到sendDataLockedv方法中,往Socket中寫入信息
3.3 sendDataLockedv
int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {
......
for (;;) {
ssize_t rc = TEMP_FAILURE_RETRY(
writev(mSocket, iov + current, iovcnt - current));
......
}
寫入到Socket之後,SystemServer中的MountService會收到,後續再細講.
4.nm->start()
這一步啓動NetlinkManager
NetlinkManager
路徑:
NetlinkManager.cpp———————–system/vold/NetlinkManager.cpp
NetlinkManager模塊接收從Kernel通過Netlink機制發送過來的Uevent消息,解析轉換成NetlinkEvent對象,再將此NetlinkEvent對象傳遞給VolumeManager處理。
start方法如下
int NetlinkManager::start() {
......
//創建Socket
if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
NETLINK_KOBJECT_UEVENT)) < 0) {
SLOGE("Unable to create uevent socket: %s", strerror(errno));
return -1;
}
//設置Socket的SO_RCVBUFFORCE大小
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;
}
//設置Socket的SO_PASSCRED大小
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;
}
//綁定Socket
if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
SLOGE("Unable to bind uevent socket: %s", strerror(errno));
goto out;
}
//創建一個NetlinkHandler對象,並把創建好的Socket句柄傳給它。
mHandler = new NetlinkHandler(mSock);
//啓動NetlinkHandler
if (mHandler->start()) {
SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
goto out;
}
return 0;
......
}
①創建Socket,爲PF_NETLINK類型
②設置Socket的SO_RCVBUFFORCE(接受緩存區)小
③設置Socket的SO_PASSCRED大小
④創建一個NetlinkHandler對象,並啓動它
主要看第④步
4.1 mHandler->start()
構造函數傳入了mSock,然後調用了NetlinkHandler的start方法
看一下它的實現
路徑:
NetlinkHandler.cpp———————–system/vold/NetlinkHandler.cpp
NetlinkHandler::NetlinkHandler(int listenerSocket) :NetlinkListener(listenerSocket) {
}
int NetlinkHandler::start() {
//startListener由SocketListener實現
return this->startListener();
}
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
VolumeManager *vm = VolumeManager::Instance();
const char *subsys = evt->getSubsystem();
// VolumeManager 調用handleBlockEvent處理事件
if (!strcmp(subsys, "block")) {
vm->handleBlockEvent(evt);
}
}
NetlinkHandler繼承自NetlinkListener,NetlinkListener繼承自SocketListener,三者關係如下
4.2 startListener
會在父類SocketListener中實現,調用startListener方法
路徑:
SocketListener.cpp————————–system/core/libsysutils/src/SocketListener.cpp
SocketListener
int SocketListener::startListener(int backlog) {
......
//創建線程執行函數threadStart
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}
return 0;
}
會開啓一個線程,那麼繼續看threadStart方法
void *SocketListener::threadStart(void *obj) {
SocketListener *me = reinterpret_cast<SocketListener *>(obj);
//調用runListener方法
me->runListener();
pthread_exit(NULL);
return NULL;
}
調用runListener
4.3 runListener
void SocketListener::runListener() {
SocketClientCollection pendingList;
......
while (!pendingList.empty()) {
it = pendingList.begin();
SocketClient* c = *it;
pendingList.erase(it);
//onDataAvailable方法處理有數據發送的socket
if (!onDataAvailable(c)) {
release(c, false);
}
c->decRef();
}
}
}
會調用onDataAvailable方法,改方法由子類NetlinkListener和FrameWorkListener實現,所以這裏要分兩條線4.4和4.5。
4.4 onDataAvailable
由子類FrameWorkListener實現這個方法
bool FrameworkListener::onDataAvailable(SocketClient *c) {
char buffer[CMD_BUF_SIZE];
int len;
//讀取socket消息
len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
.....
int i;
for (i = 0; i < len; i++) {
if (buffer[i] == '\0') {
//根據消息內容 派發命令
dispatchCommand(c, buffer + offset);
offset = i + 1;
}
}
return true;
}
在onDataAvailable方法裏會先讀取Socket消息,然後分發命令
4.4.1 dispatchCommand
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;
}
}
......
}
會調用FrameworkCommand 的runCommand方法,之前在CommandListener的構造方法裏註冊的這些指令,就是FrameWorkCommand類型,如下
FrameworkListener.cpp
void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
//添加元素
mCommands->push_back(cmd);
}
CommandListener.cpp
CommandListener::CommandListener() :FrameworkListener("vold", true) {
//註冊多條指令
registerCmd(new DumpCmd());
registerCmd(new VolumeCmd());
registerCmd(new AsecCmd());
registerCmd(new ObbCmd());
registerCmd(new StorageCmd());
registerCmd(new FstrimCmd());
registerCmd(new AppFuseCmd());
}
這裏以其中一個指令爲VolumeCmd例,會進入到VolumeCmd的runCommand方法
4.4.2 CL.runCommand
CommandListener.cpp
int CommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) {
......
} else if (cmd == "mount" && argc > 2) {
// mount [volId] [flags] [user]
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);
}
//發送應答消息給MountService
return sendGenericOkFail(cli, res);
} else if (cmd == "unmount" && argc > 2) {
// unmount [volId]
std::string id(argv[2]);
auto vol = vm->findVolume(id);
if (vol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
return sendGenericOkFail(cli, vol->unmount());
......
}
}
會根據cmd的不同做相應的處理,在”cmd=mount”操作中,就有
int res = vol->mount();
4.4.3 vol->mount()
vol是VolumeBase的實例,VolumeBase的mount方法由具體的子類EmulatedVolume、PublicVolume、PrivateVolume等實現
執行操作之後會發送應答消息給MountService,這個後續再細講。
這條線結束了。下面介紹另一條線。回到4.3 runListener,往下走到4.5 onDataAvailable
由子類NetlinkListener實現
4.5 onDataAvailable()
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
int socket = cli->getSocket();
ssize_t count;
uid_t uid = -1;
......
NetlinkEvent *evt = new NetlinkEvent();
//解析獲得NetlinkEvent實例
if (evt->decode(mBuffer, count, mFormat)) {
//傳入NetlinkEvent實例
onEvent(evt);
}
......
}
NetlinkListener沒有實現這個方法,由子類NetlinkHandler實現
4.5.1 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")) {
//調用VolumeManager 的handleBlockEvent方法
vm->handleBlockEvent(evt);
}
}
獲取VolumeManager 單例,調用handleBlockEvent方法
4.5.2 vm->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方法
disk->create();
mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));
break;
}
}
break;
}
case NetlinkEvent::Action::kChange: {
......
break;
}
case NetlinkEvent::Action::kRemove: {
......
break;
}
......
}
}
根據NetlinkEvent 的不同Action值做相應處理,有add,change,remove三個值。在add裏調用了disk->create()
4.5.3 disk->create()
路徑:
disk.cpp———————–system/vold/disk.cpp
status_t Disk::create() {
CHECK(!mCreated);
mCreated = true;
//調用notifyEvent方法
notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
readMetadata();
readPartitions();
return OK;
}
在這一步會調用notifyEvent方法通知SockListener.
void Disk::notifyEvent(int event) {
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,getId().c_str(), false);
}
到這一步驟,會發現又回到了3.2 notifyEvent步驟了,之後就跟着3.2繼續走了,這裏就不重複介紹了.
四、Vold進程總結
關於Vold的整體流程的講完了,可以跟着我繪製的時序圖跟着步驟走,來回多看幾次,注意圖文結合.
後面會針對
1.由SystemServer進程發起掛載/卸載請求
2.由Kernel發起掛載/卸載請求
這兩種情況做更具體的分析,分析源碼切記:先整體後局部,不然就沒有頭緒了,只見樹木不見森林.
PS:水平有限,有不對的地方歡迎一起探討