vold的全稱是volume daemon。實際上是負責完成系統的CDROM, USB大容量存儲,MMC卡等擴充存儲的挂載任務自動完成的守護程序。它提供的主要特點是支援這些存儲外設的熱插拔。這裡有GNU/Linux vold的介紹[http://vold.sourceforge.net/]。在Android上的這個vold系統和GNU/Linux的之間存在很大的差異,這裡我們主要是分析Android上的vold系統的處理過程。
Vold處理過程大緻分為三步:
1.建立連結:
在vold作為一個守護程序,一方面接受驅動的資訊,并把資訊傳給應用層;另一方面接受上層的指令并完成相應。是以這裡的連結一共有兩條:
(1)vold socket: 負責vold與應用層的資訊傳遞;
(2)通路udev的socket: 負責vold與底層的資訊傳遞;
這兩個連結都是在程序的一開始完成建立的。
2.引導:
這裡主要是在vold啟動時,對現有外設儲存設備的處理。首先,要加載并解析vold.conf,
并檢查挂載點是否已經被挂載(注:這裡檢查挂載點的用意不是很清楚!); 其次,執行MMC卡挂載; 最後,處理USB大容量存儲。
3.事件處理:
這裡通過對兩個連結的監聽,完成對動态事件的處理,以及對上層應用操作的響應。
我們來具體分析一下代碼過程。我們以帶着mmc卡開機這種情況為例,看看vold的啟動和處理過程。我們從vold的主函數的部分實作開始:
首先是建立兩個socket。首先建立的是vold與上層應用程式的連結。
// Socket to listen on for incomming framework connections
if ((door_sock = android_get_control_socket(VOLD_SOCKET)) < 0) {
LOGE("Obtaining file descriptor socket '%s' failed: %s",
VOLD_SOCKET, strerror(errno));
exit(1);
}
door_sock就是對VOLD_SOCKET監聽的檔案描述符。這裡VOLD_SOCKET就是在init.rc中啟動vold服務的時候建立的 socket裝置檔案。上層的應用層——MountListener,這個服務會連接配接vold socket——作為用戶端,然後通過這個連接配接和vold之間進行資訊和指令的互動。當有client連接配接進這個vold socket時,監聽door_sock的vold就會生成自己的socket描述符fw_sock,并通過它來傳送和接受上層資訊。
然後,建立vold與底層的資訊互動的socket。
if ((uevent_sock = socket(PF_NETLINK,
SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
LOGE("Unable to create uevent socket: %s", strerror(errno));
exit(1);
}
這裡使用 NETLINK_KOBJECT_UEVENT的socket去通路并獲得udev的資訊。關于udev,這是一個Linux核心的裝置管理子產品,網上有詳細資料。這裡我們隻要知道裝置的熱插拔資訊是從這個子產品中獲得的。
下邊介紹引導(Boot Strap)過程。 首先,執行volmgr_bootstrap(),對vold.conf檔案進行解析。這個檔案主要包含了如下的資訊,我們使用模拟器中的預設的 vold.conf為例。
volume_sdcard {
## This is the direct uevent device path to the SD slot on the device
emu_media_path /devices/platform/goldfish_mmc.0/mmc_host/mmc0
media_type mmc
mount_point /sdcard
ums_path /devices/platform/usb_mass_storage/lun0
}
然後把解析之後的資訊儲存在一個全局的結構變量裡,并讀取/proc/mounts資訊,檢查挂載點是否已經被挂載。這裡隻有一個mmc卡槽,是以隻定義了一個挂載點。
接着是mmc_bootstrap()函數。這裡有一連串調用和處理,主要是針對目錄來作的。調用關系如下,
mmc_bootstrap() → mmc_bootstrap_controller() → mmc_bootstrap_card()
經過這幾步之後,在mmc_bootstrap_card()調用中,我們已經進入檔案夾 /sys/devices/platform/goldfish_mmc.0/mmc_host/mmc0/mmc0:e118下邊了。這裡,就是我們目前插槽中的mmc卡存放裝置資訊的地方,然後獲得一些相關的資訊,這些資訊為之後的uevent的生成作準備。我們簡單說一下uevent事件處理系統。下面是代碼中的dispatch_table全局變量。
static struct uevent_dispatch dispatch_table[] = {
{ "switch", handle_switch_event },
{ "battery", handle_battery_event },
{ "mmc", handle_mmc_event },
{ "block", handle_block_event },
{ "bdi", handle_bdi_event },
{ "power_supply", handle_powersupply_event },
{ NULL, NULL }
};
這裡主要存儲了不同的裝置的uevent的不同的處理方式。通過dispatch_event()函數,來獲得相應事件的名字,并找到,執行相應的操作。 dispatch_table全局變量儲存了所有事件與處理句柄的對應。dispatch_event()中,正是周遊這個全局表來完成相應的調用。而 dispatch_event(),需要一個struct uevent*,也就是uevent指針作為參數。這裡,uevent就是事件的結構體,定義如下。
struct uevent {
char *path;
enum uevent_action action;
char *subsystem;
char *param[UEVENT_PARAMS_MAX];
unsigned int seqnum;
};
對 dispatch_event()的調用在vold主要有兩種方式,一種是通過捕捉udev的底層消息,然後執行 process_uevent_message()來執行dispatch_event();另一種就是在Boot strap期間調用的simulate_uevent()函數,開辟記憶體并通過參數生成一個uevent,然後執行dispatch_event()。在我們的mmc卡的引導過程中,一共需要調用若幹次的simulate_uevent()函數。這個函數中,會根據參數,生成并初始化一個uevent執行個體,再把這個執行個體作為參數傳給dispatch_event()函數,來完成事件的執行過程。下面我們回到mmc卡的引導過程,結合這個過程看看 simulate_uevent()函數的工作。
我們把虛拟機的檔案目錄下的配置看一下。
# pwd
/sys/devices/platform/goldfish_mmc.0/mmc_host/mmc0/mmc0:e118
# ls -l
-rw-r--r-- root root 4096 2009-06-09 11:01 uevent
-r--r--r-- root root 4096 2009-06-09 11:10 cid
-r--r--r-- root root 4096 2009-06-09 11:10 csd
-r--r--r-- root root 4096 2009-06-09 11:10 scr
-r--r--r-- root root 4096 2009-06-09 11:10 date
-r--r--r-- root root 4096 2009-06-09 11:10 fwrev
-r--r--r-- root root 4096 2009-06-09 11:10 hwrev
-r--r--r-- root root 4096 2009-06-09 11:10 manfid
-r--r--r-- root root 4096 2009-06-09 11:01 name
-r--r--r-- root root 4096 2009-06-09 11:10 oemid
-r--r--r-- root root 4096 2009-06-09 11:01 serial
-r--r--r-- root root 4096 2009-06-09 11:01 type
lrwxrwxrwx root root 2009-06-09 11:10 subsystem -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/bus/mmc
drwxr-xr-x root root 2009-06-09 11:01 power
lrwxrwxrwx root root 2009-06-09 11:10 driver -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/bus/mmc/drivers/mmcblk
drwxr-xr-x root root 2009-06-09 11:01 block
這裡邊的檔案存放的各種mmc卡的類型,名字等資訊。我們再看一段mmc_bootstrap_card()函數的一段代碼:
// file: mmc.c
static int mmc_bootstrap_card(char *sysfs_path)
{
… ...
sprintf(tmp, "DEVPATH=%s", devpath);
uevent_params[0] = (char *) strdup(tmp);
sprintf(filename, "/sys%s/type", devpath);
p = read_file(filename, &sz);
p[strlen(p) - 1] = '\0';
sprintf(tmp, "MMC_TYPE=%s", p);
free(p);
uevent_params[1] = (char *) strdup(tmp);
sprintf(filename, "/sys%s/name", devpath);
p = read_file(filename, &sz);
p[strlen(p) - 1] = '\0';
sprintf(tmp, "MMC_NAME=%s", p);
free(p);
uevent_params[2] = (char *) strdup(tmp);
uevent_params[3] = (char *) NULL;
if (simulate_uevent("mmc", devpath, "add", uevent_params) < 0) {
LOGE("Error simulating uevent (%m)");
return -errno;
}
… …
}
通過simulate_uevent()的參數,傳入uevent結構體中。這裡,執行的是一個mmc卡的加載資訊。我們看到char* uevent_params[4]中存儲了mmc卡的路徑,類型,名字等資訊。
并将其傳入simulate_uevent()。我們進入simulate_uevent()函數看看。
int simulate_uevent(char *subsys, char *path, char *action, char **params)
{
struct uevent *event;
char tmp[255];
int i, rc;
if (!(event = malloc(sizeof(struct uevent)))) {
LOGE("Error allocating memory (%s)", strerror(errno));
return -errno;
}
memset(event, 0, sizeof(struct uevent));
event->subsystem = strdup(subsys);
if (!strcmp(action, "add"))
event->action = action_add;
else if (!strcmp(action, "change"))
event->action = action_change;
else if (!strcmp(action, "remove"))
event->action = action_remove;
else {
LOGE("Invalid action '%s'", action);
return -1;
}
event->path = strdup(path);
for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
if (!params)
break;
event->param = strdup(params);
}
rc = dispatch_uevent(event);
free_uevent(event);
return rc;
}
我們看到,simulate_uevent()函數生成并根據參數初始化,最後,調用dispatch_uevent()去執行這個模拟事件。
處理函數dispatch_uevent()調用會根據名字,這裡會調用 handle_mmc_event()進行處理。實際上,這個處理過程并沒有加載mmc卡到/sdcard挂載點上。而挂載過程,還在下邊。:-) 我們繼續分析。
處理完這裡之後,mmc_bootstrap_card()過程繼續往下走,
mmc_bootstrap_card() → mmc_bootstrap_block() → mmc_bootstrap_mmcblk() → mmc_bootstrap_mmcblk_partition()
這是執行過程。mmc_bootstrap_mmcblk_partition()函數總共執行了兩次。兩次的差别主要是參數上的不同,第一次的調用參數是:
/sys/devices/platform/goldfish_mmc.0/mmc_host/mmc0/mmc0:e118/block/mmcblk0
執行一次simulate_uevent(),添加block的資訊; 第二次調用的參數是:
/sys/devices/platform/goldfish_mmc.0/mmc_host/mmc0/mmc0:e118/block/mmcblk0/mmcblk0p1
我們來看一下這個函數的實作:
static int mmc_bootstrap_mmcblk(char *devpath)
{
… ...
if ((rc = mmc_bootstrap_mmcblk_partition(devpath))) {
… ...
}
… ...
for (part_no = 0; part_no < 4; part_no++) {
… ...
if (mmc_bootstrap_mmcblk_partition(part_devpath))
… ...
}
}
… ...
}
兩次調用的路徑參數,我們已經給出了。 mmc_bootstrap_mmcblk_partition()函數第一次調用會添加mmc卡的DISK資訊進去,然後建立一個block device把些資訊記錄下來,一個主要的資訊是這個DISK的Partition資訊,這個對後來的挂載起着決定的作用。挂載實際上隻是對 Partition才進行的。mmc_bootstrap_mmcblk_partition()通過調用simulate_uevent()函數進行事件的模拟,最後完成操作。值得一說的是,挂載是通過單獨線程異步執行的。到這裡,關于啟動時的分析就介紹到這兒,具體的代碼調用,比較複雜,大家可以追蹤代碼來分析具體的實作。
由于USB大容量存儲的挂載還沒有實作,ums_bootstrap()是個空函數,是以這一部分可以跳過。還有就是 switch_bootstrap(),這個似乎也是處理USB存儲方面的東西,具體代碼,還沒有仔細的閱讀,以後有更新了,我會繼續update。
這樣我們再次回到vold主函數内部。接下來就是進入while循環阻塞,要對兩個連結描述符進行監聽,并執行各自的請求了,這裡使用了我們熟悉的 select系統調用。當應用層有連結vold socket的請求進來時,這個應用層和vold之間的連結描述符fw_sock就會獲得值
==========
在init.rc中啟動VOLD這個守護線程和建立socket的指令如下:
service vold /system/bin/vold
socket vold stream 0660 root mount
================
有一點需要注意,以前使用mountd的時候,也采用了同樣的原理,也建立了一個socket
現在這些在init.rc中已經被注釋掉了。
#service mountd /system/bin/mountd
# socket mountd stream 0660 root mount
轉載于:https://www.cnblogs.com/yuanfang/archive/2010/12/23/1914909.html