ANDROID中的VOLD分析

轉自:http://blog.csdn.net/datangsoc/archive/2010/10/08/5928132.aspx

 

現在可能很少有人會用mknod這個命令了,也很少有使用它的機會,但就在幾年前,這還是一項linux工程師的必備技能,在製作文件系統前或加載新的驅動前,我們必須小心翼翼的創建設備節點。

不需要使用mknod並不是他消失了,而是我們有了更好更智能的方法。

linux對於熱插拔的支持並不是生來就有的,而是經歷了一個複雜而有戲劇性的過程,全球linux愛好者用腳投出了他們保貴的一票,udev最終成爲事實上的標準。

在android中,取代udev的是vold,我們這裏不去過多的討論爲什麼android不繼續使用udev,但要知道vold的機制和udev是一樣的,理解了udev,也就理解了vold。android一出生就沒有尊守傳統linux的許多標準,當然也不能指望udev能很好的服務於android。android社區的選擇是別起爐竈,爲android定做一套udev,這就是vold了。

無論是udev還是vold,都是基於sysfs的,sysfs爲內核與用戶層的通訊提供了一種全新的方式,並將這種方式加以規範。

kernel層能檢測到有新的設備接入,並能爲之加載相應的驅動,但如何通知用戶層呢?這就是sysfs的工作,內核中的sysfs機制要求當有新的驅動加載時給用戶層發送相應的event.但這些event只盡告知的義務,具體怎麼處理,這就是vold(或者udev)的事了。

對於用戶層而言,我們無需關心sysfs的細節,只要知道sysfs能向用戶層提供什麼就行了。

首先,我們要知道如何接收來自內核的event.

Netlink socket大家應該不會陌生吧,socket這套東西不僅能用於網絡間的通訊,也用能用於進程間的通訊,像這種內核態與用戶溝通的活,自然也少不了它。

下面的內容摘自vold(NetlinkManager.cpp)

 

 

 

 

if ((mSock = socket(PF_NETLINK,

SOCK_DGRAM,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 options: %s", strerror(errno));

return -1;

}

if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {

SLOGE("Unable to bind uevent socket: %s", strerror(errno));

return -1;

}

 

 

 

 

 

 

這就是監聽sysfs的uevent的socket的關鍵設置,有網絡編程背影的人很容易理解上面這段代碼。

下面緊接着的問題就是這個socket通路會給我們什麼消息:

我們進入/sys/block/mmcblk0(也可以是/sys/block下的其它目錄),執行:

cat *

 

 

 

 

MAJOR=179

MINOR=0

DEVNAME=mmcblk0

DEVTYPE=disk

PHYSDEVPATH=/class/mmc_host/mmc0/mmc0:1234

PHYSDEVBUS=mmc

PHYSDEVDRIVER=mmcblk

NPARTS=1

......

 

 

 

 

 

 

我們通過socket從內核處到的envent中所包含的信息也與此相似,是一個包含這些信息的文本,可能是如下格式的

 

 

 

 

add@/block/PHYSDEVDRIVER=mmcblk

PHYSDEVPATH=/class/mmc_host/mmc0/mmc0:1234

DEVNAME=mmcblk0

MAJOR=179

MINOR=0

PHYSDEVDRIVER=mmcblk

......

 

 

 

 

 

 

sysfs傳上來的是一個多行的文本(這點要特別注意,並不只是add@/block/PHYSDEVDRIVER=mmcblk

這一行),vold要對這個多行文檔進行解析,然後決定怎麼做。vold會把解協出來的消息再通可別一個vold的socket傳到其它的進程,同時接收其它進程的反饋。

向sysfs目錄(或子目錄)下面的uevent文件寫入”add/n”字符也會觸發內核上發這些uevent,相當於重新執行了一次熱插拔。

例:echo "add" > /sys/block/mmcblk0/uevent

系統啓動時vold錯過了的消息可以用這個特性重新觸發。

clip_image002

分析vold的源碼,要有一定的C++的基礎和設計模式的知識,習慣過程式設計的程序員在讀vold時會有很大的困難,不過幸好vold代碼不多。另外,與vold相關的大量機密都在libsysutils中,千萬不要漏掉這個庫。

先看看下圖SocketListener這套架構,監聽sysfs與其它進程的消息,全仰仗這套框架。

clip_image004

在netLinkListener中,VOLD的重點是OnEvent這個虛接口的實現,而CommandSistener中,VOLD處理的重點則是分發VoldCommand類。VoldCommand是由FrameworkCommand派生出的,而VolumeCmdShareCmd等子類則是各種操作的封裝。

先看看對 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")) {

vm->handleBlockEvent(evt);

} else if (!strcmp(subsys, "switch")) {

vm->handleSwitchEvent(evt);

} else if (!strcmp(subsys, "battery")) {

} else if (!strcmp(subsys, "power_supply")) {

}

}

 

這裏主要是通過基類解析的uevent消息分別調用不同的處理。如果看了上面的圖還有人問NetlinkHandler::onEvent是在什麼時候調用的,那就要補一下C++了,這不是一兩句話能說得清楚的。

 

 

 

 

 

 

VoldCommand主要是實現對runcommand動作的封裝,在 FrameworkListener會根據收到的消息選擇相應的派生類。

 

 

 

 

bool FrameworkListener::onDataAvailable(SocketClient *c) {

char buffer[255];

int len;

if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) {

SLOGE("read() failed (%s)", strerror(errno));

return errno;

} else if (!len)

return false;

int offset = 0;

int i;

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) {

FrameworkCommandCollection::iterator i;

int argc = 0;

char *argv[FrameworkListener::CMD_ARGS_MAX];

char tmp[255];

char *p = data;

char *q = tmp;

bool esc = false;

bool quote = false;

int k;

memset(argv, 0, sizeof(argv));

memset(tmp, 0, sizeof(tmp));

while(*p) {

if (*p == '//') {

if (esc) {

*q++ = '//';

esc = false;

} else

esc = true;

p++;

continue;

} else if (esc) {

if (*p == '"')

*q++ = '"';

else if (*p == '//')

*q++ = '//';

else {

cli->sendMsg(500, "Unsupported escape sequence", false);

goto out;

}

p++;

esc = false;

continue;

}

if (*p == '"') {

if (quote)

quote = false;

else

quote = true;

p++;

continue;

}

*q = *p++;

if (!quote && *q == ' ') {

*q = '/0';

argv[argc++] = strdup(tmp);

memset(tmp, 0, sizeof(tmp));

q = tmp;

continue;

}

q++;

}

argv[argc++] = strdup(tmp);

#if 0

for (k = 0; k < argc; k++) {

SLOGD("arg[%d] = '%s'", k, argv[k]);

}

#endif

if (quote) {

cli->sendMsg(500, "Unclosed quotes error", false);

goto out;

}

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;

}

}

cli->sendMsg(500, "Command not recognized", false);

out:

int j;

for (j = 0; j < argc; j++)

free(argv[j]);

return;

}

 
 
 

clip_image006

 

 

 

 

 

Volume定義了各種磁盤的操作,屬於工具類。

clip_image008

要將這些類是如何組織在一起的呢,關鍵是下面兩個工廠類。

 

 

 

 

 
 

clip_image010

 
 

clip_image012

 

 

 

 

 

從上圖可以看出,VolumeManager和NetlinkManager將整 個系統組織在一 起,NetlinkManager負責翻譯sysfs的uevent事件並傳遞給其它的進程,VolumeManager則負責接收其它進程反饋的消息,並分發給VoldCommand類作相應的處理。

最後,我們分析一下vold是如何初始化這些類的:

 

 

 

 

int main() {

VolumeManager *vm;

CommandListener *cl;

NetlinkManager *nm;

SLOGI("Vold 2.1 (the revenge) firing up");

mkdir("/dev/block/vold", 0755);

/* Create our singleton managers */

if (!(vm = VolumeManager::Instance())) {//實例化

SLOGE("Unable to create VolumeManager");

exit(1);

};

if (!(nm = NetlinkManager::Instance())) {//實例化

SLOGE("Unable to create NetlinkManager");

exit(1);

};

cl = new CommandListener(); //創建vold socket,用於向其它進程轉發解析的sysfs event,並接收其進程的命令。

vm->setBroadcaster((SocketListener *) cl);

nm->setBroadcaster((SocketListener *) cl);

if (vm->start()) {

SLOGE("Unable to start VolumeManager (%s)", strerror(errno));

exit(1);

}

if (process_config(vm)) { //解析配置

SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));

}

if (nm->start()) {//創建監聽sysfs的socket

SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));

exit(1);

}

coldboot("/sys/block"); //冷啓動,vold錯過了一些uevent,重新觸發。向sysfs的uevent文件寫入”add/n” 字符也可以觸發sysfs事件,相當執行了一次熱插拔。

/*

* Switch uevents are broken.

* For now we manually bootstrap

* the ums switch

*/

{

FILE *fp;

char state[255];

/*

* Now that we're up, we can respond to commands

*/

if (cl->startListener()) { //監聽上層的反饋

SLOGE("Unable to start CommandListener (%s)", strerror(errno));

exit(1);

}

// Eventually we'll become the monitoring thread

while(1) {

sleep(1000);

}

SLOGI("Vold exiting");

exit(0);

}

 

 

 

 

 

 

etc/ vold.fstab的配置文件

例:(每一行的結束不能有空格等任何字符,vold對這個地方的處理有bug.)

 

 

 

 

dev_mount sdcard /data/disk auto /block/sda

 

 

 

 

 

 

格式是:

type label mount_point part sysfs_path sysfs_path

sysfs_path可以有多個,但最後不要有空格,否則會解析錯誤

part指定分區個數,如果是auto則只有一個分區

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章