Android Vold(Volume Daemon)
負責大量儲存裝置掛載和刪除的守護進程。
服務在init.rc中被開啟:
service vold /system/bin/vold<br /> socket vold stream 0660 root mount
本文主要分為兩個部分:
·Vold 的架構分析
·Vold的功能總結
1.Vold的架構分析
Android Vold ,一方面負責接受核心發送的關於外部存放裝置載入和刪除的資訊,然後將資訊發送給framework層的MountService;另一方面負責執行MountService發送的命令。
這些cmd和mes的傳遞主要是通過Socket通訊來實現(Socket的通訊的具體細節這裡不再贅述)。
下面從代碼的角度簡要分析這一過程的實現:
1.1在vold.c中,首先建立和framework層的通訊:
if ((door_sock = android_get_control_socket(VOLD_SOCKET)) < 0) {<br /> LOGE("Obtaining file descriptor socket '%s' failed: %s",<br /> VOLD_SOCKET, strerror(errno));<br /> exit(1);<br /> }<br /> if (listen(door_sock, 4) < 0) {<br /> LOGE("Unable to listen on fd '%d' for socket '%s': %s",<br /> door_sock, VOLD_SOCKET, strerror(errno));<br /> exit(1);<br /> } |
其中,VOLD_SOCKET在init進程中建立,android_get_control_socket()主要是擷取VOLD_SOCKET的檔案描述符。
Listern()主要用於監聽來自其他framework層的Socket串連請求。
當framewok層,MountService開啟之後,會建立一個新的線程。在class MountService 的構造方法中:
if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {<br /> Thread thread = new Thread(mListener, MountListener.class.getName());<br /> thread.start();<br /> }
class MountListener 實現了Runnable介面,在它繼承的run()方法中建立了一個無限迴圈:
while (true) {<br /> listenToSocket();<br /> } |
在listenToSocket()方法中:
socket = new LocalSocket();<br /> LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET,<br /> LocalSocketAddress.Namespace.RESERVED);<br /> socket.connect(address);<br /> InputStream inputStream = socket.getInputStream();<br /> mOutputStream = socket.getOutputStream();<br /> ····································<br /> while (true) {<br /> int count = inputStream.read(buffer);<br /> if (count < 0) break;<br /> int start = 0;<br /> for (int i = 0; i < count; i++) {<br /> if (buffer[i] == 0) {<br /> String event = new String(buffer, start, i - start);<br /> handleEvent(event);<br /> start = i + 1;<br /> }<br /> }<br /> } |
首先,執行個體化一個本地的LocalSocket 用於與Vold的通訊,其次建立與VOLD_SOCKET的串連。然後再建立一個檔案輸入資料流,用於儲存Vold傳來的mes到buff中。最後,在一個無線迴圈中讀取buff的內容,並執行handleEvent()。
在handleEvent()中通過if else語句對傳來的事件做相應處理
if (event.equals(VOLD_EVT_UMS_ENABLED)) {<br /> ................<br /> } else if (event.equals(VOLD_EVT_UMS_DISABLED)) {<br /> ................<br /> } else if (event.equals(VOLD_EVT_EXTERNAL_UMS_CONNECTED)) {<br /> ...............<br /> mService.notifyUmsConnected(path);<br /> .....................<br /> } else if (event.equals(VOLD_EVT_UMS_CONNECTED)) {<br /> ...........................<br /> mService.notifyUmsConnected(path);<br /> } else if (event.equals(VOLD_EVT_EXTERNAL_UMS_DISCONNECTED)) {<br /> .........................
對於其中一個事件的處理,例如.notifyUmsConnected():
最後,將處理之後需要執行的命令發送給vold。
到現在為止,我們就建立了vold 與MountService之間的通訊。
1.2建立與核心的socket通訊
....................<br />Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);<br /> mContext.sendBroadcast(intent);<br />.................... |
if ((uevent_sock = socket(PF_NETLINK,<br /> SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {<br />........<br /> }<br /> if (setsockopt(uevent_sock, SOL_SOCKET, SO_RCVBUFFORCE, &uevent_sz,<br /> sizeof(uevent_sz)) < 0) {<br />.......<br /> }<br /> if (bind(uevent_sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {<br /> ......<br /> } |
建立一個uevent_sock,建立與核心的通訊。setsockopt()函數主要用於設定uevent_sock的選項,bind()用於將核心的socket與uevent_sock進行地址的綁定。
1.3掛載現有存放裝置
volmgr_bootstrap()函數首先解析設定檔vold.conf;
最後會將需要掛載的裝置資訊放在一個全域變數的鏈表中val_root
static volume_t *vol_root = NULL;
1.4掛載mmc/sdcard卡
volmgr_bootstrap();<br />simulate_uevent()確定uevent的action是'add','remove'還是'change';<br />if ((rc = volmgr_readconfig("/system/etc/vold.conf")) < 0) {<br /> LOGE("Unable to process config");<br /> return rc;<br /> }
dipatch_uevent():根據uevent->subsystem確定uevent處理的控制代碼。
struct uevent {<br /> const char *action;<br /> const char *path;<br /> const char *subsystem;<br /> const char *firmware;<br /> int major;<br /> int minor;<br />};
最終的掛載操作在MountService開啟之後實現。
1.5 usb大型存放區的處理ums_bootstrap()
1.6 switch_bootstrap()
1.7主服務(死迴圈)
struct uevent_dispatch {<br /> char *subsystem;<br /> int (* dispatch) (struct uevent *);<br />};
while(1) {<br /> ········<br /> FD_ZERO(&read_fds);//初始設定檔案描述集合<br /> FD_SET(door_sock, &read_fds);//將door_sock加入檔案描述集<br /> if (door_sock > max)<br /> max = door_sock;<br /> FD_SET(uevent_sock, &read_fds);//將event_sock加入檔案描述集<br /> if (uevent_sock > max)<br /> max = uevent_sock;<br /> if (fw_sock != -1) {<br /> FD_SET(fw_sock, &read_fds);//將fw_sock加入檔案描述集<br /> if (fw_sock > max)<br /> max = fw_sock;<br /> }<br /> //當所有的檔案描述符都沒改變時,阻塞線程<br /> if ((rc = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) {<br /> LOGE("select() failed (%s)", strerror(errno));<br /> sleep(1);<br /> continue;<br /> }<br /> if (!rc) {<br /> continue;<br /> }<br /> //檢測如果是door_sock,檢測與framework的串連,並發送msg<br /> if (FD_ISSET(door_sock, &read_fds)) {<br /> struct sockaddr addr;<br /> socklen_t alen;<br /> alen = sizeof(addr);<br /> if (fw_sock != -1) {<br /> LOGE("Dropping duplicate framework connection");<br /> int tmp = accept(door_sock, &addr, &alen);<br /> close(tmp);<br /> continue;<br /> }<br /> if ((fw_sock = accept(door_sock, &addr, &alen)) < 0) {<br /> LOGE("Unable to accept framework connection (%s)",<br /> strerror(errno));<br /> }<br /> LOG_VOL("Accepted connection from framework");<br />/* for iNand */<br />volmgr_usb_bootstrap();<br /> if ((rc = volmgr_send_states()) < 0) {<br /> LOGE("Unable to send volmgr status to framework (%d)", rc);<br /> }<br /> }<br /> //如果是fw_sock,執行framework傳來的命令<br /> if (FD_ISSET(fw_sock, &read_fds)) {<br /> if ((rc = process_framework_command(fw_sock)) < 0) {<br /> if (rc == -ECONNRESET) {<br /> LOGE("Framework disconnected");<br /> close(fw_sock);<br /> fw_sock = -1;<br /> } else {<br /> LOGE("Error processing framework command (%s)",<br /> strerror(errno));<br /> }<br /> }<br /> }<br /> //如果是uevent_sock,產生一個uevent事件<br /> if (FD_ISSET(uevent_sock, &read_fds)) {<br /> if ((rc = process_uevent_message(uevent_sock)) < 0) {<br /> LOGE("Error processing uevent msg (%s)", strerror(errno));<br /> }<br /> }<br /> } // while<br /> |
2.Vold的功能總結
1)建立串連:
在vold作為一個守護進程,一方面接受驅動的資訊,並把資訊傳給應用程式層;另一方面接受上層的命令並完成相應操作。
所以這裡的串連一共有兩條:
·vold socket: 負責vold與應用程式層的資訊傳遞;
·訪問udev的socket: 負責vold與底層的資訊傳遞;
這兩個串連都是在進程的一開始完成建立的。
2)引導:
這裡主要是在vold啟動時,對現有外設存放裝置的處理。
·首先,要載入並解析vold.conf,並檢查掛載點是否已經被掛載;
·其次,執行MMC卡掛載;
·最後,處理USB大型存放區。
3)事件處理:
這裡通過對兩個串連的監聽,完成對動態事件的處理,以及對上層應用操作的響應。
補充:
Telechips比較google原生Android 針對Vold的移植主要在以下兩方面:
1.Telchips支援多種存放裝置,例如nand,sata,scsi,mmc/sd;從代碼角度而言,主要增加了對這些裝置事件的處理控制代碼;
2.Telchips加入了對ntfs的支援,主要增加了三個處理函數:ntfs_check(),ntfs_identify()和ntfs_mount();