1. 程式人生 > >【輸出文件】 Android 儲存模組 解析

【輸出文件】 Android 儲存模組 解析

 

【儲存模組培訓文件,很認真寫的,現在轉移到部落格上】

 

                                 Android  儲存模組解析

 

 

1.儲存系統架構

Android 的儲存系統主要由SystemServer程序中的MountService和Vold程序中的VolumeManager組成,它們管理著系統的儲存裝置,執行各種操作,包括 解除安裝(unmount)、掛載(mount)、格式化(format)等。

儲存系統的架構如下圖所示:

 

在Android的儲存系統中,MountService是為應用提供服務的Binder類,執行在SystemServer中,StorageManager是MountService的代理,在使用者程序中被使用。Vold是一個守護程序,負責和底層儲存系統的驅動互動。MountService和Vold之間通過socket進行雙向通訊:MountService向vold下發命令,vold向MountService反饋底層硬體發生的變化。

Vold程序的主體是VolumeManager物件,它管理著系統底層所有的Disk和Volume物件,實現儲存的各種操作。Vold中的CommandListener物件負責和MountService中的NativeDemonConnector物件進行Socket通訊,NetlinkHandler物件負責堅挺kernel層上報的Netlink Socket訊息。

 

  1. Vold守護程序

Vold 是 Volume Daemon的縮寫,主要用來管理Android系統中的:1.USB儲存裝置;2.SD卡裝置。

2.1 vold 初始化

Vold通過init程序啟動,它在init.rc中定義:

service vold /system/bin/vold \

        --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \

        --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0

    class core

    socket vold stream 0660 root mount

    socket cryptd stream 0660 root mount[p1] 

ioprio be 2

//blkid_context 等為linxu下blkid 和 fsck兩個工具的執行環境,這裡主要的規定的是執行時的selinux  環境。具體見注1.

Vold服務屬於core組,系統啟動時會被init程序啟動。此外,這裡還定義了2個socket:

   1)socket vold:用於vold和JAVA層的MountService通訊

   2)socket cryptd:[p2] 

 

Vold的原始碼路徑為:/system/vold , 入口函式main():

int main(int argc, char** argv){

... ...

    VolumeManager *vm;

    CommandListener *cl;

    CryptCommandListener *ccl;

    NetlinkManager *nm;

//檢測vold執行時的selinux環境,具體見注1

parse_args(argc, argv);

    sehandle = selinux_android_file_context_handle();

    if (sehandle) {

        selinux_android_set_sehandle(sehandle);

    }

    //初始化在init.rc中定義的vold和cryptd兩個socket。

    // Quickly throw a CLOEXEC on the socket we just inherited from init

    fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);

    fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);

    //建立 “/dev/block/vold”這個資料夾,此資料夾下記錄著vold守護程序中建立的Disk物件和volume(包括public 和 private)物件

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



    /* For when cryptfs checks and mounts an encrypted filesystem */

    klog_set_level(6);[p3] 

    //初始化VolumeManager 和NetlinkManager物件

    /* Create our singleton managers */

    if (!(vm = VolumeManager::Instance())) {

    ... ...

    }

    if (!(nm = NetlinkManager::Instance())) {

    ... ...

    }



    if (property_get_bool("vold.debug", false)) {

        vm->setDebug(true);

    }

    //CommandListener負責和JAVA層的NativeDeamonConnector的通訊,CryptCommandListener負責[p4] 

    cl = new CommandListener();

    ccl = new CryptCommandListener();

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

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



    if (vm->start()) {

    ... ...

    }



    if (process_config(vm)) {

    ... ...

    }



    if (nm->start()) {

    ... ...

    }

    coldboot("/sys/block");

... ...

//進入迴圈睡眠

    exit(0);

}

 

1

 parse_args這個方法根據init.rc中配置的blkid_context fsck_context等資訊初始化了android::vold::sBlkidContext / sBlkidUntrustedContext / sFsckContext / sFsckUntrustedContext等變數,這些變數影響到了後續vold流程中呼叫blkidfsck等工具時,呼叫/system/vold/Utils::ForkExecvp setexeccon(context) 的結果:

  如果下發BlkidContext,就要求vold處於selinux 已初始化且為enable狀態,否則執行blkid工具時會導致vold程序被強制abort

  BlkidUntrustedContext則對selinux環境無限制。

2.2 process_config

process_config函式首先是讀取fstab.${ro.hardware}檔案的內容,然後根據檔案中的每行定義的儲存器的屬性值來呼叫VolumeManager物件中的方法來建立Disk物件,並規劃到VolumeManager的管理中。

static int process_config(VolumeManager *vm) {

    ... ...

    bool has_adoptable = false;

... ...

//讀取fstab檔案,篩選出<fs_mgr_flags>中有標記voldmanaged

    if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {

      //不支援標有nonremovable的。前面已經說了,vold僅負責管理“可移動”的存               儲裝置

        //初始化DiskSource::sysPattern,被賦值於fstab檔案中的<sys_path>

            std::string sysPattern(fstab->recs[i].blk_device);

            //初始化DiskSource::nickname,被賦值於fstab檔案的中的<fs_mgr_flags>中的flag_vals.label;

            std::string nickname(fstab->recs[i].label);

            int flags = 0;

            [p5] 

            if (fs_mgr_is_encryptable(&fstab->recs[i])) {

                flags |= android::vold::Disk::Flags::kAdoptable;

                has_adoptable = true;

            }

            if (fs_mgr_is_noemulatedsd(&fstab->recs[i])

                    || property_get_bool("vold.debug.default_primary", false)) {

                flags |= android::vold::Disk::Flags::kDefaultPrimary;

            }



            vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(

                    new VolumeManager::DiskSource(sysPattern, nickname, flags)));

    ... ...

    property_set("vold.has_adoptable", has_adoptable ? "1" : "0");

    return 0;

}

 

2.3 fstab檔案

Android系統上由於使用了Vold取代了udev來管理磁碟,自然也要有一個類似於udev的config來配置和管理磁碟的掛載,Android使用的是vold.fstab來實現這一目的。該配置檔案的格式和解析意義如下表:

格式

說明

示例

<sys_path>

裝置實際路徑

/devices/platform/comip-mmc.0/mmc_host/mmc1

<mnt_point>

掛載點

storage/sdcard1

<type>

掛載格式

dev_mount

<mnt_flags and options>

 

 

<fs_mgr_flags>

分割槽的屬性,包括是否加密、是否可以移動(物理解除安裝)

 

需要注意的是:

//TODO 解析fstab檔案的程式碼

在L1860專案上,對應的配置檔案為:/device/leadcore/lte26007/fstab.lc1860

//TODO 具體到18601專案上fstab檔案的特殊性

 

2.4 VolumeManager

VolumeManager的主要作用就是建立並管理Disk和volume物件,並處理又kernel層上報的block_event訊息以及由java層下發的對Disk 和 volume等物件的mount,unmount,format等操作。

2.4.1 start

VolumeManager的建構函式沒什麼可以說的,VolumeManager的物件是在main()中建立的,建立後即呼叫vm->start();

int VolumeManager::start() {

    //每次start VolumeManager之前均unmount所有VolumeManager管理的volume物件

    unmountAll();

    //建立mInternalEmulated物件。這個mInternalEmulated物件就代表我們經常說的“內部儲存”或者“內建SD卡”

    CHECK(mInternalEmulated == nullptr);

    mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(

            new android::vold::EmulatedVolume("/data/media"));

    mInternalEmulated->create();

... ...

關於EmulatedVolume 物件的具體資訊,在後面分析。

 

有上述可以發現,在VolumeManager初始化時,僅僅只是初始化了mInternalEmulated這個volume,沒有外部儲存裝置相關的volume資訊:Vold預設僅僅只有內部儲存一定是存在的。

2.4.2 handleBlockEvent

void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {

    std::lock_guard<std::mutex> lock(mLock);



    std::string eventPath(evt->findParam("DEVPATH"));

    std::string devType(evt->findParam("DEVTYPE"));

    //僅僅只處理disk的訊息

    if (devType != "disk") return;

    int major = atoi(evt->findParam("MAJOR"));

    int minor = atoi(evt->findParam("MINOR"));

    dev_t device = makedev(major, minor);

    switch (evt->getAction()) {

    case NetlinkEvent::Action::kAdd: {

        for (auto source : mDiskSources) {

            if (source->matches(eventPath)) {

                int flags = source->getFlags();

                if (major == kMajorBlockMmc) {

                    flags |= android::vold::Disk::Flags::kSd;

                } else {

                    flags |= android::vold::Disk::Flags::kUsb;

                }



                auto disk = new android::vold::Disk(eventPath, device,

                        source->getNickname(), flags);

                disk->create();

                mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));

                break;

            }

        }

        break;

    }

    case NetlinkEvent::Action::kChange: {

        LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";

        for (auto disk : mDisks) {

            if (disk->getDevice() == device) {

                disk->readMetadata();

                disk->readPartitions();

            }

        }

        break;

    }

    case NetlinkEvent::Action::kRemove: {

        auto i = mDisks.begin();

        while (i != mDisks.end()) {

            if ((*i)->getDevice() == device) {

                (*i)->destroy();

                i = mDisks.erase(i);

            } else {

                ++i;

            }

        }

        break;

    }

    default: {

        LOG(WARNING) << "Unexpected block event action " << (int) evt->getAction();

        break;

    }

    }

}

 

    HandleBlockEvent 是NetlinkHandler在接收到kernel上報的block_event後呼叫的,NetlinkHandler在後面會進行詳細的分析。

● handleBlockEvent  會分析上報event的device的major num 和minor num,通過呼叫makedev來生成對應的device。以L1860位例,evt中上報的device num 為45952,轉化為2進製為 1011001110000000,取後8位為minor,其餘的前8位為major,則major =179,minor =129,所以device為”179,129”。

   ●從kernel層傳送的訊息分為3大類:NLActionAdd,NLActionRemove,NLActionChange.分別對應

1.NetlinkEvent::Action::kAdd

    handleBlockEvent  會分析上報的event中eventPath的值,這個值代表event事件中的裝置所處於kernel層的<sys_path> 。當這個值和我們在vold的main:main()中呼叫process_config 讀取fstab.${ro.hardware}生成的DiskSource物件的<sys_path> 相同時,handleBlockEvent 才會繼續處理這個event。

AndroidFramework層原生是適配SD卡的,但是有些時候我們發現,即使kernel已經能正常的識別SD裝置,Framework層仍然不會去掛載SD卡。原因就在於kernel上報的這個event中的eventPath和我們在fstab.${ro.hardware}中配置的<sys_path>不匹配,HandleBlockEvent 主動跳過了這個event

因此,我們在確定fstab.${ro.hardware}檔案時中<sys_path>的值時,需要根據kernel上報的eventEventPath的值來確定。

lc1860為例:/dev/block/mmcblk1p1/devices/platform/comip-mmc.0/mmc_host/mmc1/*均可以代表SD卡的path,但是kernelevent上報的EventPath的為“”[p6] 

  

在NetlinkEvent::Action::kAdd中,VolumeManager主要做了兩件事:

  1. 通過上報device 的major num判斷裝置的型別,major =179的確定為sdcard裝置,將此裝置對應的Disk物件的flags中新增android::vold::Disk::Flags::kSd位;其餘的均為USB儲存裝置,在其對應的Disk物件的flags中新增 android::vold::Disk::Flags::kUsb。

對於Android的儲存系統來說,Sdcard 和USB 儲存裝置是兩種不同的儲存裝置,兩者的掛載流程,掛載路徑,掛載後的許可權以及對兩裝置中檔案的讀寫許可權的限制都會不同,大部分時候都是通過Disk::Flags 來進行區分的。

  1. 建立disk 物件,強disk新增到mDisks,納入VolumeManager的管理中
  1. NetlinkEvent::Action::kChange

    [p7] 

3.NetlinkEvent::Action::kRemove

[p8] 

tip 1 device的major和minor number

    Linux系的/dev目錄下面的的裝置檔案是用來表示外設的,如/dev/sda1表示第一塊硬碟的第一個分割槽。但是這個/dev/sda1僅僅是方便使用者觀察,linux核心中表示不同的裝置是通過major 和minor number實現的,通過major和minor Number來載入相應的驅動程式。

major number:表示不同的裝置型別,主裝置號

minor number:表示同一個裝置的的不同分割槽,次裝置號

Linux 2.6 核心中,表示 major minor 的資料結構是一個32bit的欄位 dev_t type (defined in <linux/types.h>),表示major minor 的位數分別是 12 20 bits

 

2.5 Disk 物件

Disk物件定義在/system/vold/disk.cpp中,建構函式如下:

Disk::Disk(const std::string& eventPath, dev_t device,

        const std::string& nickname, int flags) :

        mDevice(device), mSize(-1), mNickname(nickname), mFlags(flags), mCreated(

                false), mJustPartitioned(false) {

    mId = StringPrintf("disk:%u,%u", major(device), minor(device));

    mEventPath = eventPath;

    mSysPath [p9] = StringPrintf("/sys/%s", eventPath.c_str());

    mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str());

    CreateDeviceNode(mDevPath, mDevice);

}

 

其中:

mEventPath /eventPath:disk物件對應裝置在kernel層的實際路徑;

mDevPath :在lc1860中為Disk:179,129。Vold層管理的裝置記錄在/dev/block/vold檔案下

device:disk物件對應裝置的實際裝置號,在lc1860中為179,129

nickname:disk物件對應裝置的標誌;

flags:disk物件的flags,具體定義在Disk.h中:   

enum Flags {

        /* Flag that disk is adoptable */

        kAdoptable = 1 << 0,

        /* Flag that disk is considered primary when the user hasn't

         * explicitly picked a primary storage location */

        kDefaultPrimary = 1 << 1,

        /* Flag that disk is SD card */

        kSd = 1 << 2,

        /* Flag that disk is USB disk */

        kUsb = 1 << 3,

        /* Flag that disk is EMMC internal */

        kEmmc = 1 << 4,

};

 

2.5.1 CreateDeviceNode

     定義在/system/vold/Utils.cpp下,主要的工作是呼叫了 mknod(cpath, mode, dev)  函式。其中:
     cpath為Disk::mDevPath,在1860專案中為 /dev/block/vold/Disk:179,129;

     dev:為Disk::mDevice,在1860中為179,129這個device;

     mknod 命令是建立一個目錄項和一個特殊檔案的對應索引節點CreateDeviceNode函式的作用就是將dev/block/vold/Disk:179,129這個資料夾作為了179,129 這個裝置的索引點,之前在2.4 VolumeManager中說明,179,129這個device實際代表kernel層中外接SD卡的對應的資料夾,因此,現/dev/block/vold/Disk:179,129就代表了外接SD卡的資料夾索引點[p10] 。

2.5.2 Disk::readMetadata()

元資料(Metadata),又稱中介資料、中繼資料,為描述資料的資料(data about data),主要是描述資料屬性(property)的資訊,用來支援如指示儲存位置、歷史資料、資源查詢、檔案記錄等功能。元資料算是一種電子式目錄,為了達到編制目錄的目的,必須在描述並收藏資料的內容或特色,進而達成協助資料檢索的目的。

具體函式如下:

status_t Disk::readMetadata() {

    mSize = -1;

    mLabel.clear();

    ... ...

    switch (major(mDevice)) {

    case kMajorBlockScsiA: case kMajorBlockScsiB:case ... ...

    //插入SD卡或者USB儲存裝置時都不會滿足條件,忽略

    ... ...: {

    ... .. ..

    }

    //kMajorBlockMmc = “179”,代表裝置屬於mmc儲存裝置

    case kMajorBlockMmc: {

        std::string path(mSysPath + "/device/manfid");

        std::string tmp;

        if (!ReadFileToString(path, &tmp)) {

            PLOG(WARNING) << "Failed to read manufacturer from " << path;

            return -errno;

        }

        uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);[p11] 

        switch (manfid) {

        case 0x000003: mLabel = "SanDisk"; break;

        case 0x00001b: mLabel = "Samsung"; break;

        case 0x000028: mLabel = "Lexar"; break;

        case 0x000074: mLabel = "Transcend"; break;

        }

        break;

    }

    ... ...

    }

    //向java層傳送DiskSizeChanged,DiskLabelChanged,DiskSysPathChanged的Event

    notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRId64, mSize));

    notifyEvent(ResponseCode::DiskLabelChanged, mLabel);

    notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);

    return OK;

}

簡單來說,Disk::readMetadata函式,主要就是為了檢查Disk物件所對應的儲存裝置的各種屬性資訊:

Dis::mLabel : 對應裝置的manfid,即生產廠商;

Disk::mSize : disk的代表的儲存裝置的總容量[p12] 

Disk::mSysPath :  Device path under sysfs

並通知JAVA層的MountService更新對應Disk的相應屬性值。

2.5.3 Disk::readPartitions()

//TODO code explain



status_t Disk::readPartitions() {

    int8_t maxMinors = getMaxMinors();[p13] 

    if (maxMinors < 0) {

        return -ENOTSUP;

    }

    // read代表重新讀取Disk中個partition資訊並生產資訊的volume物件。所以需要destroy all。

    destroyAllVolumes();

    //1860中,下發的命令為/system/bin/blkid --android-dump /dev/block/vold/Disk:179,128”

    std::vector<std::string> cmd;

    cmd.push_back(kSgdiskPath);

    cmd.push_back("--android-dump");

    cmd.push_back(mDevPath);



    std::vector<std::string> output;

    status_t res = ForkExecvp(cmd, output);

    ... ...

    //解析返回的結果

    for (auto line : output) {

    ... ...

        if (!strcmp(token, "DISK")) {

            //解析disk資訊,確定記錄Disk的檔案系統的是MBR表還是gpt表

            const char* type = strtok(nullptr, kSgdiskToken);

            if (!strcmp(type, "mbr")) {

                table = Table::kMbr;

            } else if (!strcmp(type, "gpt")) {

                table = Table::kGpt;

            }

        } else if (!strcmp(token, "PART")) {

            //解析Disk下的partition資訊

            foundParts = true;

            int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);

            ... ...

            //根據的disk的Major_num和Minor_num 來確定其中個partition的          device_num。Major_num代表裝置的型別,不變;Minor_num 由disk的Minor_num加此分割槽的分割槽num

            dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);



            if (table == Table::kMbr) {

                const char* type = strtok(nullptr, kSgdiskToken);



                switch (strtol(type, nullptr, 16)) {

                case 0x06: // FAT16

                case 0x0b: // W95 FAT32 (LBA)

                case 0x0c: // W95 FAT32 (LBA)

                case 0x0e: // W95 FAT16 (LBA)

                    createPublicVolume(partDevice);

                    break;

                }

            } else if (table == Table::kGpt) {

                const char* typeGuid = strtok(nullptr, kSgdiskToken);

                const char* partGuid = strtok(nullptr, kSgdiskToken);



                if (!strcasecmp(typeGuid, kGptBasicData)) {

                    createPublicVolume(partDevice);

                } else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {

                    createPrivateVolume(partDevice, partGuid);

                }

... ...

}

 

[p14] 

    readPartition()的主要作用是呼叫linux原生的blkid工具,下發

“/system/bin/blkid --android-dump /dev/block/vold/Disk:179,128”命令,讀取disk對應的儲存裝置的partition分割槽資訊,根據反饋的資訊,決定建立PublicVolume物件還是PrivateVolume物件。

 

2.5.4 Disk::partitionPublic()

status_t Disk::partitionPublic() {

... ...

    //清楚當前disk下所有的volume物件

    destroyAllVolumes();

... ...

//對disk裝置下發 /system/bin/sgdisk --zap-all /dev/block/vold/disk:179,128命令

//擦出disk裝置的分割槽表

    std::vector<std::string> cmd;

    cmd.push_back(kSgdiskPath);

    cmd.push_back("--zap-all");

    cmd.push_back(mDevPath);

... ...

    if ((res = ForkExecvp(cmd)) != 0) {

        LOG(WARNING) << "Failed to zap; status " << res;

    }



   //更新diskinfo

    int rc = apply_disk_config(&dinfo, 0);

//



    return rc;

}

2.5.5 Disk::partitionPrivate()

程式碼較多,其實就是對diks的儲存裝置下發了一系列命令,已1860為例,下發了:

/system/bin/sgdisk 

--new=0:0:+16M

--typecode=0:19A710A2-B3CA-11E4-B026-10604B889DCF

--change-name=0:android_meta

--new=0:0:-0

--typecode=0:193D1EA4-B3CA-11E4-B075-10604B889DCF

--partition-guid=0:aae61afb6d099da2a04cf52ce5b5d01a

--change-name=0:android_expand

/dev/block/vold/disk:179,128

 

此命令的作用就是將/dev/block/vold/disk:179,128 對應的儲存裝置分為2個partitions,其中

 

1.一個應該為PART 1,地址為 179:129(這個partition應該暫時無用,作為後續擴充套件什麼的用途);

Part1 的註釋:   

 // Define a metadata partition which is designed for future use; there

    // should only be one of these per physical device, even if there are

    // multiple private volumes.
  1. PART2,地址為179:130.用於作為儲存卡用途的partition

 

實際下發的命令結果:

 

 

2.6 volume物件

2.6.1 VolumeBase

1.建構函式:

VolumeBase::VolumeBase(Type type) :

        mType(type), mMountFlags(0), mMountUserId(-1), mCreated(false), mState(

                State::kUnmounted), mSilent(false) {

}

 

2.具體成員   

enum class Type {

        //PublciVolume,公共儲存vol,對於與外接SD卡

        kPublic = 0,

        //PrivateVolume,私有儲存vol,對應於adoptable devices volumes

        kPrivate,

        //EmulatedVolume,內部儲存vol,對應於/data/media

        kEmulated,

        //

        kAsec,

        //

        kObb,

    };

 

 

 

private:  

  /* ID that uniquely references volume while alive */

    //volume對應的ID,一般是根據Disk的ID 生成,與disk的major_num相同,代表屬於同種型別是儲存裝置,minor_num是在Disk的minor_num基礎上計算得出,一般Disk分化為幾個分割槽,第N個分割槽對應的minor_num = disk_minor_num + N,具體見

    std::string mId;



    /* ID that uniquely references parent disk while alive */

    //生成volume的Disk的ID

    std::string mDiskId;

    /* Partition GUID of this volume */

    std::string mPartGuid;

    /* Volume type */

    Type mType;

    /* Flags used when mounting this volume */

    int mMountFlags;



    /* User that owns this volume, otherwise -1 */

    //多使用者狀態下會涉及到,每個Volume對於與特定的user

    userid_t mMountUserId;



    /* Flag indicating object is created */

    bool mCreated;

   

    /* Current state of volume */

    State mState;



    /* Path to mounted volume */

    // volume 實際掛載路徑,對於public來說,就是"storge" + Fs_UUID

    std::string mPath;



    /* Path to internal backing storage */

    std::string mInternalPath;

    /* Flag indicating that volume should emit no events */

    bool mSilent;



    /* Volumes stacked on top of this volume */

    std::list<std::shared_ptr<VolumeBase>> mVolumes;



    void setState(State state);



    DISALLOW_COPY_AND_ASSIGN(VolumeBase);

};

    2.6.1.1 volume狀態

  

  enum class State {

        //被解除安裝狀態

        kUnmounted = 0,

        //正處於被檢查狀態,一般處於此狀態時,代表volume正處於被fsck檢查檔案系統的狀態,fsck會根據Volume對於的Disk所對應的emmc裝置的MBR表檢測emmc裝置是否正常,然後檢查emmc裝置的檔案系統是否正常,如果以上出現問題,會先嚐試修復,修復失敗後才返回-1

        kChecking,

        //掛載狀態

        kMounted,

        //掛載狀態,且只能讀,不可寫

        kMountedReadOnly,

        //正處於被格式化狀態

        kFormatting,

        //

        kEjecting,

        //無法掛載狀態

        kUnmountable,

        //emmc裝置被移除狀態,一般是接受到底層emmc驅動上報的訊息後才會處於此種狀態

        kRemoved,

        //非法移除狀態,USB OTG熱插拔

        kBadRemoval,

    };

2.6.2 PublicVolume物件

由Disk::readPatitions() 中呼叫Disk::createPublicVolume()建立。公共訪問儲存區,對應於外接SD卡,USB OTG。

void Disk::createPublicVolume(dev_t device) {

    auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));

    if (mJustPartitioned) {

        LOG(DEBUG) << "Device just partitioned; silently formatting";

        vol->setSilent(true);

        vol->create();

        vol->format("auto");

        vol->destroy();

        vol->setSilent(false);

    }



    mVolumes.push_back(vol);

    vol->setDiskId(getId());

    vol->create();

}

2.6.3 PrivateVolume物件

由Disk::readPatitions() 中呼叫Disk::createPrivateVolume()建立。

void Disk::createPrivateVolume(dev_t device, const std::string& partGuid) {

    std::string normalizedGuid;

    if (NormalizeHex(partGuid, normalizedGuid)) {

        LOG(WARNING) << "Invalid GUID " << partGuid;

        return;

    }



    std::string keyRaw;

    if (!ReadFileToString(BuildKeyPath(normalizedGuid), &keyRaw)) {

        PLOG(ERROR) << "Failed to load key for GUID " << normalizedGuid;

        return;

    }



    LOG(DEBUG) << "Found key for GUID " << normalizedGuid;



    auto vol = std::shared_ptr<VolumeBase>(new PrivateVolume(device, keyRaw));

    if (mJustPartitioned) {

        LOG(DEBUG) << "Device just partitioned; silently formatting";

        vol->setSilent(true);

        vol->create();

        vol->format("auto");

        vol->destroy();

        vol->setSilent(false);

    }



    mVolumes.push_back(vol);

    vol->setDiskId(getId());

    vol->setPartGuid(partGuid);

    vol->create();

}

2.6.4 EmulatedVolume物件

 

 在VolumeManager中建立:   

mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(

            new android::vold::EmulatedVolume("/data/media"));

    mInternalEmulated->create();

 

  1. 訊息的傳遞

3.1 監聽驅動發出的訊息

3.1.1 NetlinkManager物件

NetLinkManager物件的主要作用是監聽驅動發出的uevent訊息。main()函式中呼叫NetlinkManager類的靜態函式Instance()裡建立NetlinkManager物件:

/system/vold/NetlinkManager.java

NetlinkManager *NetlinkManager::Instance() {

    if (!sInstance)

        sInstance = new NetlinkManager();

    return sInstance;

}

Istance()函式建立了NetlinkManager物件並通過靜態變數sInstance來應用,這就意味著vold程序中只會有一個NetlinkManager物件。 

 

建構函式:

NetlinkManager::NetlinkManager() {

    mBroadcaster = NULL;

}     

                                                      

NetlinkManager的建構函式只是對變數mBroadcaster進行初始化。main()函式中會呼叫NetlinkManager的setBroadcaster()函式給變數mBroadcaster重新賦值:

 int main(int argc, char** argv) {

... ...

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

... ...

   if (nm->start()){

... ...

}

 

/system/vold/NetlinkManager.cpp

int NetlinkManager::start() {

struct sockaddr_nl nladdr;

//設定64K 的緩衝區

    int sz = 64 * 1024;

    int on = 1;

    memset(&nladdr, 0, sizeof(nladdr));

    nladdr.nl_family = AF_NETLINK;

    nladdr.nl_pid = getpid();

    nladdr.nl_groups = 0xffffffff;

    if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,

            NETLINK_KOBJECT_UEVENT)) < 0) {

    ... ...

    }

    if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {

    ... ...

    }

    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {

    ... ...

    }

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

    ... ...

    }

    mHandler = new NetlinkHandler(mSock);

    if (mHandler->start()) {

    ... ...

}

 

其中:NETLINK_KOBJECT_UEVENT代表kernel的核心訊息的Uevent事件。

 

 

3.1.2 NetlinkHandler

void NetlinkHandler::onEvent(NetlinkEvent *evt) {

    VolumeManager *vm = VolumeManager::Instance();

const char *subsys = evt->getSubsystem();

... ...

    if (!strcmp(subsys, "block")) {

        vm->handleBlockEvent(evt);

    }

}

僅僅只會監聽“block”型別的uevent事件。這個uevent事件會下發到VolumeManager::handleBlockEvent中進行處理。

 

 

         

3.1.*  socket   

int socket(int domain, int type, int protocol);

第一個引數指定應用程式使用的通訊協議的協議族,對於TCP/IP協議族,該引數置AF_INET;

第二個引數指定要建立的套接字型別,流套接字型別為SOCK_STREAM、資料報套接字型別為SOCK_DGRAM、原始套接字SOCK_RAW(WinSock介面並不適用某種特定的協議去封裝它,而是由程式自行處理資料包以及協議首部);

兩個重要的型別是 SOCK_STREAM 和 SOCK_DGRAM。 SOCK_STREAM表明資料象字元流 一樣通過 socket 。而 SOCK_DGRAM 則表明資料將是資料報(datagrams)的形式。

     

3.2 處理MountService傳送的訊息

MountServicevold之間是通過CommandListener物件進行通訊的,在main函式中,對其進行初始化:

/system/vold/main.cpp

main() {

... ...

  cl = new CommandListener();

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

... ...

  if (cl->startListener()) {

     PLOG(ERROR) << "Unable to start CommandListener";

     exit(1);

  }

... ... 

}

 

clCommandListener類例項化的一個物件,該物件負責和Framework進行通訊。

CommandListener>FrameworkListener>SocketLisenter

3.2.1 CommandListener

在CommandLisenter中,定義了6個類,這6個類都繼承了VoldCommand類,VoldCommand類繼承了FrameworkCommand類:

VoldCommand—>FrameworkCommand



class CommandListener : public FrameworkListener {

public:

    CommandListener();

    virtual ~CommandListener() {}



private:

    static void dumpArgs(int argc, char **argv, int argObscure);

    static int sendGenericOkFail(SocketClient *cli, int cond);



    class DumpCmd : public VoldCommand {

    ... ...

    class VolumeCmd : public VoldCommand {

    ... ...

    class AsecCmd : public VoldCommand {

    ... ...

    class ObbCmd : public VoldCommand {

    ... ...

    class StorageCmd : public VoldCommand {

    ... ...

class FstrimCmd : public VoldCommand {

... ...

};

在這個類中,VoldCommand主要寫了一個純虛擬函式runCommand,宣告如下:
virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
為了就是在CommandListener類中的實現,這裡總共實現了6個版本的runCommand函式:

 

CommandLisenter在/system/vold/main.cpp::main()中被例項化,然後呼叫 cl->startListener()

我們現在講startListener函式:

/system/core/libsysutils/src/SocketListenercpp :: startListener



int SocketListener::startListener() {

    return startListener(4);

}



int SocketListener::startListener(int backlog) {

... ...

//初始化很多變數,其中傳入的backlog = 4,代表sock可以同時處理的最大連線請求數量為4。

if (mListen && listen(mSock, backlog) < 0){

... ...

//將此socket新增到client端

mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));

... ...

if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {

... ...



} 





threadStart最後呼叫runListener啟動SocketListener:



void SocketListener::runListener() {

  While(1) {

  ... ...

    for (it = mClients->begin(); it != mClients->end(); ++it) {

         SocketClient* c = *it;

         int fd = c->getSocket();

         if (FD_ISSET(fd, &read_fds)) {

             pendingList.push_back(c);

             c->incRef();

         }

    }

  ... ...

     while (!pendingList.empty()) {

         /* Pop the first item from the list */

         it = pendingList.begin();

         SocketClient* c = *it;

         pendingList.erase(it);

         /* Process it, if false is returned, remove from list */

         if (!onDataAvailable(c)) {

             release(c, false);

         }

         c->decRef();

     }

  ... ...

  }

}

 

runListener 會在一個死迴圈環中一直遍歷mClients佇列中的socket物件,呼叫onDaraAvailable()函式來處理。onDataAvailable函式是類的純虛擬函式,在FrameworkLisenter類中實現了該函式:

/system/core/libsysutils/src/FrameworkListenser.cpp::onDataAvailable()

bool FrameworkListener::onDataAvailable(SocketClient *c) {

   ... ...

   len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));

   ... ...

    for (i = 0; i < len; i++) {

        if (buffer[i] == '\0') {

            /* IMPORTANT: dispatchCommand() expects a zero-terminated string */

            dispatchCommand(c, buffer + offset);

            offset = i + 1;

        }

    }

    ... ...

}

 

 

onDataAvailable()函式從socket中讀取資料,這些資料是一個或多個以0結尾的字串,每個字串都是一條命令。然後呼叫dispatchCommand()函式來分命令:

void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {

... ...

    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;

        }

    }

... ...

}

dispatchCommand()函式先檢查命令的格式是否都正確,然後再檢查mCommands中是否有和FrameworkCommand匹配的物件,然後再呼叫runCommand()函式,在這之前:

 

CommandListener::CommandListener() :

                 FrameworkListener("vold", true) {

    registerCmd(new DumpCmd());

    registerCmd(new VolumeCmd());

    registerCmd(new AsecCmd());

    registerCmd(new ObbCmd());

    registerCmd(new StorageCmd());

    registerCmd(new FstrimCmd());

}  

      

 

CommandListener的建構函式中,通過registerCmd()函式註冊了包括 DumpCmd,VolumeCmd等多個cmd命令。             

 

3.2.2 CommandListener::runCommand()

關於CommandListener中的cmd,總共有VolumeCmd,AsecCmd,ObbCmd,StorageCmd,XwarpCmd,cryptfscmd,FstrimCmd, 共7個cmd:

 

CmdType

cmd

explain

DumpCmd

 

 

 

 

 

 

 

 

 

 

VolumeCmd

reset

 

shutdown

 

debug