Android磁碟管理-之vold源碼分析(2)

來源:互聯網
上載者:User

作者:gzshun. 原創作品,轉載請標明出處!

Vold是Android系統處理磁碟的核心部分,取代了原來Linux系統中的udev,主要用來處理Android系統的熱插拔存放裝置。在Android2.2以後的系統中,vold源碼已經移到了system目錄下,vold目錄包含以下源碼:
├── Android.mk
├── Asec.h
├── CleanSpec.mk
├── CommandListener.cpp
├── CommandListener.h
├── Devmapper.cpp
├── Devmapper.h
├── DirectVolume.cpp
├── DirectVolume.h
├── Fat.cpp
├── Fat.h
├── hash.h
├── logwrapper.c
├── Loop.cpp
├── Loop.h
├── main.cpp
├── NetlinkHandler.cpp
├── NetlinkHandler.h
├── NetlinkManager.cpp
├── NetlinkManager.h
├── Process.cpp
├── Process.h
├── ResponseCode.cpp
├── ResponseCode.h
├── vdc.c
├── VoldCommand.cpp
├── VoldCommand.h
├── Volume.cpp
├── Volume.h
├── VolumeManager.cpp
├── VolumeManager.h
├── Xwarp.cpp
└── Xwarp.h

先簡要說明一下類的繼承關係,vold中比較重要的有以下幾個類:
三大管理類:VolumeManager,CommandListener,NetlinkManager
其他處理類:Volume,DirectVolume,NetlinkHandler,Fat,ResponseCode
其他相關的類:NetlinkListener,SocketListener

1.VolumeManager管理Volume類;
2.DirectVolume類繼承於Volume類,儲存著磁碟資訊與操作函數;
3.NetlinkManager類負責與核心uevent事件通訊,期間,使用到了NetlinkListener和SocketListener類的函數;
4.Fat是格式化sd卡的函數;
5.ResponseCode儲存著vold向framework反饋的值。

本文講解main.cpp檔案的原始碼:

int main() {/************************************************************************************以下三個類聲明三個指標對象:**VolumeManager:管理所有存放裝置(volume對象);**CommandListener:監聽Framework下發的訊息,並分析命令,調用響應的操作函數;**NetlinkManager  :監聽Linux核心的熱插拔事件,uevent事件**********************************************************************************/VolumeManager *vm;CommandListener *cl;NetlinkManager *nm;SLOGI("Vold 2.1 (the revenge) firing up");/************************************************************************************在Linux系統,如scsi硬碟,隨身碟的裝置節點預設產生在/dev/目錄下,Android把這些裝置**節點改到了/dev/block/目錄下。但隨著熱插拔事件的產生,裝置節點(如sda,sdb)經常變換,**對於vold來說,可能有點麻煩,所以在/dev/block/下建立了一個名為vold的目錄,存放sda,**sdb對應的裝置節點,形如"8:0"。**eg:sda 的主次裝置號分別為8,0,於是vold就會在vold目錄下建立名為"8:0"的節點,基於主次裝置號**命名,便於程式操作,增加了靈活性。**********************************************************************************/mkdir("/dev/block/vold", 0755);/************************************************************************************執行個體化vm對象,VolumeManager類調用自身的Instance函數,new了一個對象給vm。**源碼:VolumeManager *VolumeManager::Instance() {if (!sInstance)sInstance = new VolumeManager();return sInstance;}**********************************************************************************/if (!(vm = VolumeManager::Instance())) {SLOGE("Unable to create VolumeManager");exit(1);};/************************************************************************************執行個體化nm對象,NetlinkManager類調用自身的Instance函數,new了一個對象給nm。**源碼:NetlinkManager *NetlinkManager::Instance() {if (!sInstance)sInstance = new NetlinkManager();return sInstance;}**********************************************************************************/if (!(nm = NetlinkManager::Instance())) {SLOGE("Unable to create NetlinkManager");exit(1);};/************************************************************************************執行個體化cl對象;**vm->setBroadcaster((SocketListener *) cl);**setBroadcaster函數將VolumeManager的成員變數mBroadcaster設定成cl,這兩個變數都是**SocketListener的指標類型,命令執行狀態廣播函數就會調用這個SocketListener指標來調用**SocketListener類的廣播函數;**為什麼SocketListener類能強制轉換CommandListener類呢?**原因:繼承關係:CommandListener(子類) --> FrameworkListener(子類) --> SocketListener(父類)**將子類強制轉換為父類是沒錯的。**********************************************************************************/cl = new CommandListener();vm->setBroadcaster((SocketListener *) cl);nm->setBroadcaster((SocketListener *) cl);/************************************************************************************調用start函數啟動存放裝置的管理類,看了源碼,這函數沒幹什麼事,估計去哪打醬油了。**源碼:int VolumeManager::start() {return 0;}**********************************************************************************/if (vm->start()) {SLOGE("Unable to start VolumeManager (%s)", strerror(errno));exit(1);}/************************************************************************************process_config函數用來解析/etc/vold.fstab的設定檔,從代碼可以看出,設定檔的參數**以空格和製表格(Tab鍵)分隔;系統啟動起來,分析該設定檔,掛載相應的分區,相當於**Linux系統的/etc/fstab檔案。**********************************************************************************/if (process_config(vm)) {SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));}/************************************************************************************nm對象調用start函數開啟了一個線程,用來監聽底層的uevent事件;這start函數乾的事就**多了,主要是開啟一個udp通訊端,迴圈監聽底層事件。線程裡面使用了Select函數來處理**通訊端,這設計到fd_set結構體等等的使用;**當捕獲到uevent事件,vold會將該事件通知給Framework層,Framework進行判斷,然後再**下發操作命令。**********************************************************************************/if (nm->start()) {SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));exit(1);}coldboot("/sys/block");/************************************************************************************下面是判斷Android系統是否處於ums狀態,ums是大型存放區的意思,這是Android系統**的OTG功能。OTG是on-the-go的簡稱,主要提供與pc機的串連;**notifyUmsConnected函數將ums的狀態通知給Framework層,於是Framework與UI配合,彈出**一個與pc機串連的互動介面。**********************************************************************************/{FILE *fp;char state[255];if ((fp = fopen("/sys/devices/virtual/switch/usb_mass_storage/state","r"))) {if (fgets(state, sizeof(state), fp)) {if (!strncmp(state, "online", 6)) {vm->notifyUmsConnected(true);} else {vm->notifyUmsConnected(false);}} else {SLOGE("Failed to read switch state (%s)", strerror(errno));}fclose(fp);} else {SLOGW("No UMS switch available");}}/************************************************************************************上面的準備工作已做完,現在是vold比較重要的一個處理線程;**startListener是CommandListener類的父類的函數,該函數用於開啟監聽線程,監聽**Framework層下發給vold的命令,然後調用相應的命令操作存放裝置。**********************************************************************************/if (cl->startListener()) {SLOGE("Unable to start CommandListener (%s)", strerror(errno));exit(1);}/************************************************************************************進入一個迴圈,讓vold保持守護進程的狀態;**vold的主要工作是由:nm->start()和cl->startListener()兩個線程共同完成;這兩個處理線程**中間需要Framework來充當橋樑與boss的身份,Framework是管理這些磁碟的boss。**********************************************************************************/while(1) {sleep(1000);}SLOGI("Vold exiting");exit(0);}/************************************************************************************以下這兩個函數不重要,也就是開啟/sys/block目錄處理一些事情;這倆函數用來給vold打雜,**社會階級比較低,o(∩_∩)o 哈哈。**裡面有幾個函數是bionic庫提供的,用得比較少。**********************************************************************************/static void do_coldboot(DIR *d, int lvl){struct dirent *de;int dfd, fd;dfd = dirfd(d);fd = openat(dfd, "uevent", O_WRONLY);if(fd >= 0) {write(fd, "add\n", 4);close(fd);}while((de = readdir(d))) {DIR *d2;if (de->d_name[0] == '.')continue;if (de->d_type != DT_DIR && lvl > 0)continue;fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);if(fd < 0)continue;d2 = fdopendir(fd);if(d2 == 0)close(fd);else {do_coldboot(d2, lvl + 1);closedir(d2);}}}static void coldboot(const char *path){DIR *d = opendir(path);if(d) {do_coldboot(d, 0);closedir(d);}}/************************************************************************************該函數用來解析/etc/vold.fstab設定檔,文本的處理;**可能不同的源碼版本,有點差異;**strsep是字串的分割函數,可以看出該函數是以" \t"來分割(\t前面有一空格),分割空格**或製表格,所以設定檔裡面空格與tab鍵來分割都行;**strsep不是ANSI C的函數,但它用來取代strtok函數,strtok是線程不安全的函數。**********************************************************************************/static int process_config(VolumeManager *vm) {FILE *fp;int n = 0;char line[255];if (!(fp = fopen("/etc/vold.fstab", "r"))) {return -1;}while(fgets(line, sizeof(line), fp)) {char *next = line;char *type, *label, *mount_point;n++;line[strlen(line)-1] = '\0';if (line[0] == '#' || line[0] == '\0')continue;if (!(type = strsep(&next, " \t"))) {SLOGE("Error parsing type");goto out_syntax;}if (!(label = strsep(&next, " \t"))) {SLOGE("Error parsing label");goto out_syntax;}if (!(mount_point = strsep(&next, " \t"))) {SLOGE("Error parsing mount point");goto out_syntax;}if (!strcmp(type, "dev_mount")) {DirectVolume *dv = NULL;char *part, *sysfs_path;if (!(part = strsep(&next, " \t"))) {SLOGE("Error parsing partition");goto out_syntax;}if (strcmp(part, "auto") && atoi(part) == 0) {SLOGE("Partition must either be 'auto' or 1 based index instead of '%s'", part);goto out_syntax;}/************************************************************************************如果設定檔指定為auto,則為自動掛載存放裝置,在執行個體化DirectVolume的對象,傳遞-1**進去,否則將分區序數part傳進去;**********************************************************************************/if (!strcmp(part, "auto")) {dv = new DirectVolume(vm, label, mount_point, -1);} else {dv = new DirectVolume(vm, label, mount_point, atoi(part));}while((sysfs_path = strsep(&next, " \t"))) {/************************************************************************************將存放裝置在/sys/對應的路徑添加進PathCollection容器,該容器為“char *”類型;**在/sys/裡面可以擷取到存放裝置的熱插拔事件,所以DirectVolume類的主要工作就是針對**這裡去擷取uevent事件的;**DirectVolume::handleBlockEvent(NetlinkEvent *evt)函數去得到這些事件,主要還是**NetlinkListener類從核心捕獲到的。**********************************************************************************/if (dv->addPath(sysfs_path)) {SLOGE("Failed to add devpath %s to volume %s", sysfs_path, label);goto out_fail;}}/************************************************************************************如果在設定檔有找到正確的掛載參數,那麼就會將DirectVolume的對象添加到VolumeCollection**容器中,該容器存放著Volume*類型的資料,VolumeManager的對象vm是用來管理這些存放裝置的;**一Block Storage裝置就會執行個體化一個Volume對象,但對於手機來說,一般只能識別到一張SD卡。**********************************************************************************/vm->addVolume(dv);} else if (!strcmp(type, "map_mount")) {} else {SLOGE("Unknown type '%s'", type);goto out_syntax;}}fclose(fp);return 0;/************************************************************************************從這個main函數的出錯處理可以看出,系統源碼經常使用到這種高效性的goto技巧,goto在**系統中的出錯處理用得很頻繁,可以說幾乎每個檔案都使用到了goto跳轉函數;**很多文章或者教材,經常反面性的批判goto的不規則,但從這些外國的開原始碼可以看出,**那些牛人都很喜歡用goto,利用了goto來處理出錯情況的技巧,顯得很漂亮;**我覺得,要從實用性的角度來評論這些語言的優缺點,並不能用否認的說法來解釋,這樣才能**不斷地進步;**所以,如果在出錯處理非常多的情況下,使用goto是使代碼更可讀,減少重複的出錯判斷的**代碼量。**********************************************************************************/out_syntax:SLOGE("Syntax error on config line %d", n);errno = -EINVAL;out_fail:fclose(fp);return -1; }

下篇文章開始從main函數的進入點深入分析流程。。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.