Android下Vold守護進程分析

來源:互聯網
上載者:User

原文地址::http://www.chineselinuxuniversity.net/freesky/viewthread.php?tid=41

 

 

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 -> ../../../../../../bus/mmc
drwxr-xr-x root     root              2009-06-09 11:01 power
lrwxrwxrwx root     root              2009-06-09 11:10 driver -> ../../../../../../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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.