【輸出文件】 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訊息。
- 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通訊
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流程中呼叫blkid和fsck等工具時,呼叫/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上報的event中EventPath的值來確定。
以lc1860為例:/dev/block/mmcblk1p1和/devices/platform/comip-mmc.0/mmc_host/mmc1/*均可以代表SD卡的path,但是kernel的event上報的EventPath的為“”[p6]
在NetlinkEvent::Action::kAdd中,VolumeManager主要做了兩件事:
- 通過上報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 來進行區分的。
- 建立disk 物件,強disk新增到mDisks,納入VolumeManager的管理中
- NetlinkEvent::Action::kChange
3.NetlinkEvent::Action::kRemove
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);
}
... ...
}
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.
- 為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();
- 訊息的傳遞
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傳送的訊息
MountService和vold之間是通過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);
}
... ...
}
cl是CommandListener類例項化的一個物件,該物件負責和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 |
|