Android存储系统源码走读(一):StorageManagerService
前言
上文走读了StorageManagerService启动部分的主要逻辑,本篇将从对vold部分代码进行分析。
本文涉及代码:
- system/vold/Android.bp
- system/vold/vold.rc
- system/vold/main.cpp
- system/vold/VoldNativeService.cpp
- system/vold/NetlinkManager.cpp
- system/vold/NetlinkHandler.cpp
- system/vold/VolumeManager.cpp
- system/vold/fs/Ext4.cpp
- system/vold/model/PublicVolume.cpp
存储服务的代码框架
在此之前,我们先看下目前版本(android11)存储服务的代码框架。
对比AndroidO之前版本主要优化了StorageManagerService与vold通信的方式,移除了CommandListener改为了binder通信。旧版本的代码框架可以参考此文:http://gityuan.com/2016/07/23/android-io-arch/
Android没有使用Linux平台下的udev来处理磁盘,于是Google写了一个类似udev功能的vold,充当了kernel与framework之间的桥梁;
vold的启动
Android.bp将vold.rc打包进init.rc
init_rc: [
"vold.rc",
"wait_for_keymaster.rc",
],
vold.rc启动/system/bin/vold
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
ioprio be 2
writepid /dev/cpuset/foreground/tasks
shutdown critical
group root reserved_disk
Vold的主程序在/system/vold目录中,vold(main.cpp)将会启动VoldNativeService以及创建VoldManager和NetlinkManager
- VolumeManager是VoldNativeService功能的执行者
- NetLinkManager用来监听内核的热插拔事件,通知到vold进程USB设备已经接入了
int main(int argc, char** argv) {
...
VolumeManager* vm;
NetlinkManager* nm;
// 初始化VolumeManager,VoldNativeService功能的执行者
if (!(vm = VolumeManager::Instance())) {
LOG(ERROR) << "Unable to create VolumeManager";
exit(1);
}
// 初始化NetLinkManager用来监听内核的热插拔事件,通知到vold进程USB设备已经接入了
if (!(nm = NetlinkManager::Instance())) {
LOG(ERROR) << "Unable to create NetlinkManager";
exit(1);
}
if (vm->start()) {
PLOG(ERROR) << "Unable to start VolumeManager";
exit(1);
}
/**********************************************************************************
**process_config函数用来解析/etc/vold.fstab的配置文件,从代码可以看出,配置文件的参数以空格和
**制表格(Tab键)分隔;系统启动起来,分析该配置文件,挂载相应的分区,相当于Linux系统的/etc/fstab文件
**********************************************************************************/
bool has_adoptable;
bool has_quota;
bool has_reserved;
if (process_config(vm, &has_adoptable, &has_quota, &has_reserved)) {
PLOG(ERROR) << "Error reading configuration... continuing anyways";
}
ATRACE_BEGIN("VoldNativeService::start");
if (android::vold::VoldNativeService::start() != android::OK) {
LOG(ERROR) << "Unable to start VoldNativeService";
exit(1);
}
ATRACE_END();
// 启动VoldNativeService
ATRACE_BEGIN("NetlinkManager::start");
if (nm->start()) {
PLOG(ERROR) << "Unable to start NetlinkManager";
exit(1);
}
ATRACE_END();
// 应用层往/sys/block目录下的uevent文件写"add\n"指令,触发kernel向上发送Uevent消息,获取设备的当前信息
coldboot("/sys/block");
...
}
StorageManager与vold的通信
前文的StorageManagerService在connect时获取的就是VoldNativeService的binder proxy
StorageManager与vold建立通信是通过IVoldListener
// 获取Vold的bp端用于通信
mVold = IVold.Stub.asInterface(binder);
try {
// 关键代码:设置Vold的Listener
mVold.setListener(mListener);
} catch (RemoteException e) {
mVold = null;
Slog.w(TAG, "vold listener rejected; trying again", e);
}
在VoldNativeService.cpp是通过setListener函数实现
binder::Status VoldNativeService::setListener(
const android::sp& listener) {
ENFORCE_SYSTEM_OR_ROOT;
ACQUIRE_LOCK;
VolumeManager::Instance()->setListener(listener);
return Ok();
}
到此IVoldListener这个Binder回调就已经建立好了,VoldNativeService就可以像一般回调一样调用VoldListener来通知java层。
vold与内核的通信
vold通过NetLinkManager来建立socket通道,监听内核上报的uevent事件。
NetLink是Linux下用户进程和kernel进行信息交互的一种机制,借助这种机制,用户进程(如Vold/Netd)可以接收来自kernel的一些消息,同时也可以向kernel发送一些控制命令。NetlinkManager就是基于此设计的。Uevent也跟Linux系统有关,它与Linux 的设备文件系统有一定关系;这里,我们可以简单的认为,Uevent就是一个字符串,它描述了外部存储设备插入/拔出、挂载/卸载的状态信息。Vold通过Netlink机制,可以得到这些信息,并进行外部存储设备的管理、控制。
int NetlinkManager::start() {
struct sockaddr_nl nladdr;
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;
// 关键代码: 创建地址族为PF_NETLINK的socket,与Kernel进行通信
if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) < 0) {
PLOG(ERROR) << "Unable to create uevent socket";
return -1;
}
// When running in a net/user namespace, SO_RCVBUFFORCE will fail because
// it will check for the CAP_NET_ADMIN capability in the root namespace.
// Try using SO_RCVBUF if that fails.
if ((setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) &&
(setsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)) < 0)) {
PLOG(ERROR) << "Unable to set uevent socket SO_RCVBUF/SO_RCVBUFFORCE option";
goto out;
}
if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
PLOG(ERROR) << "Unable to set uevent socket SO_PASSCRED option";
goto out;
}
if (bind(mSock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) {
PLOG(ERROR) << "Unable to bind uevent socket";
goto out;
}
// 通过NetlinkerHandler处理socket的回调
mHandler = new NetlinkHandler(mSock);
if (mHandler->start()) {
PLOG(ERROR) << "Unable to start NetlinkHandler";
goto out;
}
return 0;
out:
close(mSock);
return -1;
}
通过NetlinkerHandler这个类处理socket的回调
void NetlinkHandler::onEvent(NetlinkEvent* evt) {
VolumeManager* vm = VolumeManager::Instance();
const char* subsys = evt->getSubsystem();
if (!subsys) {
LOG(WARNING) << "No subsystem found in netlink event";
return;
}
if (std::string(subsys) == "block") {
vm->handleBlockEvent(evt);
}
}
由VolumeManager处理block相关的event.
驱动设备分为字符设备、块设备、网络设备。对于字符设备按照字符流的方式被有序访问,字符设备也称为裸设备,可以直接读取物理磁盘,不经过系统缓存,例如键盘直接产生中断。而块设备是指系统中能够随机(不需要按顺序)访问固定大小数据片(chunks)的设备,例如硬盘;块设备则是通过系统缓存进行读取。
内核上报的事件由字符串构成,示例如下:
ACTION=add
DEVPATH=/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0
SUBSYSTEM=block
MAJOR=179
MINOR=0
DEVNAME=mmcblk0
DEVTYPE=disk
NPARTS=3
SEQNUM=1357
void VolumeManager::handleBlockEvent(NetlinkEvent* evt) {
std::lock_guard lock(mLock);
if (mDebug) {
LOG(DEBUG) << "----------------";
LOG(DEBUG) << "handleBlockEvent with action " << (int)evt->getAction();
evt->dump();
}
// 从NetlinkEvent中读取几个参数,包括DEVPATH、DEVTYPE、MAJOR、MINOR
std::string eventPath(evt->findParam("DEVPATH") ? evt->findParam("DEVPATH") : "");// 设备路径
std::string devType(evt->findParam("DEVTYPE") ? evt->findParam("DEVTYPE") : "");// 设备类型
if (devType != "disk") return;
// 主次设备号,两者可以描述一个具体设备
int major = std::stoi(evt->findParam("MAJOR"));
int minor = std::stoi(evt->findParam("MINOR"));
// 根据主次设备号创建设备
dev_t device = makedev(major, minor);
switch (evt->getAction()) {
// 设备插入事件
case NetlinkEvent::Action::kAdd: {
for (const auto& source : mDiskSources) {
if (source->matches(eventPath)) {
// 识别是U盘还是SD卡
// For now, assume that MMC and virtio-blk (the latter is
// specific to virtual platforms; see Utils.cpp for details)
// devices are SD, and that everything else is USB
int flags = source->getFlags();
if (major == kMajorBlockMmc || IsVirtioBlkDevice(major)) {
flags |= android::vold::Disk::Flags::kSd;
} else {
flags |= android::vold::Disk::Flags::kUsb;
}
auto disk =
new android::vold::Disk(eventPath, device, source->getNickname(), flags);
// 识别完成后调用handleDiskAdded完成挂载
handleDiskAdded(std::shared_ptr(disk));
break;
}
}
break;
}
case NetlinkEvent::Action::kChange: {
LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";
handleDiskChanged(device);
break;
}
// 设备移除事件
case NetlinkEvent::Action::kRemove: {
handleDiskRemoved(device);
break;
}
default: {
LOG(WARNING) << "Unexpected block event action " << (int)evt->getAction();
break;
}
}
}
识别完是U盘还是sd卡后调用handleDiskAdded进行挂载
有两个场景需要暂时等待:
- 锁屏未解锁,需要等待用户解锁
- 需要等待user0启动,因为我们需要在mount Fuse daemon处理磁盘前启用用户。
void VolumeManager::handleDiskAdded(const std::shared_ptr& disk) {
// 1、锁屏未解锁,需要等待用户解锁
2、需要等待user0启动,因为我们需要在mount Fuse daemon处理磁盘前启用用户。
// For security reasons, if secure keyguard is showing, wait‵‵‵
// until the user unlocks the device to actually touch it
// Additionally, wait until user 0 is actually started, since we need
// the user to be up before we can mount a FUSE daemon to handle the disk.
bool userZeroStarted = mStartedUsers.find(0) != mStartedUsers.end();
if (mSecureKeyguardShowing) {
LOG(INFO) << "Found disk at " << disk->getEventPath()
<< " but delaying scan due to secure keyguard";
mPendingDisks.push_back(disk);
} else if (!userZeroStarted) {
LOG(INFO) << "Found disk at " << disk->getEventPath()
<< " but delaying scan due to user zero not having started";
mPendingDisks.push_back(disk);
} else {
disk->create();
mDisks.push_back(disk);
}
}
Disk::create()
status_t Disk::create() {
CHECK(!mCreated);
mCreated = true;
// 回调VolumeManager的onDiskCreated 通知磁盘已经创建
auto listener = VolumeManager::Instance()->getListener();
if (listener) listener->onDiskCreated(getId(), mFlags);
if (isStub()) {
createStubVolume();
return OK;
}
// 读取磁盘的元数据
readMetadata();
// 读取磁盘的分区信息
readPartitions();
return OK;
}
Disk::readPartitions()
这里就是调用/system/bin/sgdisk工具读取分区信息
sgdisk是Linux下操作GPT分区的工具,就像fdisk是操作MBR分区的工具。
disk对象创建完开始创建volume对象,sd卡USb设备是publicVolume,内置存储是privateVolume
static const char* kSgdiskPath = "/system/bin/sgdisk";
status_t Disk::readPartitions() {
int maxMinors = getMaxMinors();
if (maxMinors < 0) {
return -ENOTSUP;
}
destroyAllVolumes();
// Parse partition table
std::vector cmd;
cmd.push_back(kSgdiskPath);
cmd.push_back("--android-dump");
cmd.push_back(mDevPath);
std::vector output;
// 这里就是调用/system/bin/sgdisk工具读取分区信息
status_t res = ForkExecvp(cmd, &output);
if (res != OK) {
LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
auto listener = VolumeManager::Instance()->getListener();
if (listener) listener->onDiskScanned(getId());
mJustPartitioned = false;
return res;
}
Table table = Table::kUnknown;
bool foundParts = false;
for (const auto& line : output) {
auto split = android::base::Split(line, kSgdiskToken);
auto it = split.begin();
if (it == split.end()) continue;
if (*it == "DISK") {
if (++it == split.end()) continue;
if (*it == "mbr") {‵‵‵
table = Table::kMbr;
} else if (*it == "gpt") {
table = Table::kGpt;
} else {
LOG(WARNING) << "Invalid partition table " << *it;
continue;
}
} else if (*it == "PART") {
foundParts = true;
if (++it == split.end()) continue;
int i = 0;
if (!android::base::ParseInt(*it, &i, 1, maxMinors)) {
LOG(WARNING) << "Invalid partition number " << *it;
continue;
}
dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
if (table == Table::kMbr) {
if (++it == split.end()) continue;
int type = 0;
if (!android::base::ParseInt("0x" + *it, &type)) {
LOG(WARNING) << "Invalid partition type " << *it;
continue;
}
switch (type) {
case 0x06: // FAT16
case 0x07: // HPFS/NTFS/exFAT
case 0x0b: // W95 FAT32 (LBA)
case 0x0c: // W95 FAT32 (LBA)
case 0x0e: // W95 FAT16 (LBA)
case 0x83: // Linux EXT4/F2FS/...
createPublicVolume(partDevice);
break;
}
} else if (table == Table::kGpt) {
if (++it == split.end()) continue;
auto typeGuid = *it;
if (++it == split.end()) continue;
auto partGuid = *it;
if (android::base::EqualsIgnoreCase(typeGuid, kGptBasicData)) {
createPublicVolume(partDevice);
} else if (android::base::EqualsIgnoreCase(typeGuid, kGptAndroidExpand)) {
createPrivateVolume(partDevice, partGuid);
}
}
}
}
// Ugly last ditch effort, treat entire disk as partition
if (table == Table::kUnknown || !foundParts) {
LOG(WARNING) << mId << " has unknown partition table; trying entire device";
std::string fsType;
std::string unused;
if (ReadMetadataUntrusted(mDevPath, &fsType, &unused, &unused) == OK) {
createPublicVolume(mDevice);
} else {
LOG(WARNING) << mId << " failed to identify, giving up";
}
}
auto listener = VolumeManager::Instance()->getListener();
// 通知上层设备开始扫描
if (listener) listener->onDiskScanned(getId());
mJustPartitioned = false;
return OK;
}
Disk::createPublicVolume
void Disk::createPublicVolume(dev_t device) {
auto vol = std::shared_ptr(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();
}
在StorageManagerService接收到哦onVolumeCreated回调后,会调用VoldNativeService::mount进行真正的挂载。挂载前会调用不同文件系统对应的fsck工具进行磁盘校验。