Android 9.0 Vold 挂载流程分析
在Android 系统中所有的热插拔设备都是通过Vold 进程挂载的。通过kernel–>vold–>StorageManagerService这样的架构去逐级上报热插拔事件。
一、Vold 入口--> /system/vold/main.c
int main(int argc, char** argv) {
atrace_set_tracing_enabled(false);
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
LOG(INFO) << "Vold 3.0 (the awakening) firing up";
ATRACE_BEGIN("main");
LOG(VERBOSE) << "Detected support for:"
<< (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")
<< (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")
<< (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "");
VolumeManager *vm;
NetlinkManager *nm;
parse_args(argc, argv);
sehandle = selinux_android_file_context_handle();
if (sehandle) {
selinux_android_set_sehandle(sehandle);
}
mkdir("/dev/block/vold", 0755);
klog_set_level(6);
if (!(vm = VolumeManager::Instance())) {
LOG(ERROR) << "Unable to create VolumeManager";
exit(1);
}
if (!(nm = NetlinkManager::Instance())) {
LOG(ERROR) <setDebug(true);
}
if (vm->start()) {
PLOG(ERROR) << "Unable to start VolumeManager";
exit(1);
}
bool has_adoptable;
bool has_quota;
bool has_reserved;
//解析fstab 文件
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();
LOG(DEBUG) <start()) {
PLOG(ERROR) <joinThreadPool();
LOG(INFO) << "vold shutting down";
exit(0);
}
main 函数里面 做以下几件事:
1、创建NetLinkManager对象
2、创建VolumeManager对象
3、process_config 函数里面解析 fstab 文件
NetLinkManager 是接受Uevent 事件的上报,最终干活的是NetlinkHandler。
三、VolumeManagerVoulemanager 是处理所有挂载事件的,统一在这里根据事件的类型进行分发。
四、解析fstab 文件4.1、fstab 文件是存放的是系统中的文件系统信息的,一般位于源码中device 目录下面,在设备的vendor/etc/fstab.xxx 下。如下截图的这两行是usb 设备的挂载信息。
class="lazy" data-src 表示 待挂载的设备节点路径
mount point表示 挂载点,即 被挂载的目录
filesystem type表示 所挂载磁盘的文件系统类型
mount flags parameters表示 指定所挂载的文件系统的一些参数,如下
#
/devices/platform/passthrough/5b0d0000.usb/ci_hdrc.0
*has_adoptable = false;
*has_quota = false;
*has_reserved = false;
for (int i = 0; i num_entries; i++) {
auto rec = &fstab_default->recs[i];
if (fs_mgr_is_quota(rec)) {
*has_quota = true;
}
if (rec->reserved_size > 0) {
*has_reserved = true;
}
if (fs_mgr_is_voldmanaged(rec)) {
if (fs_mgr_is_nonremovable(rec)) {
LOG(WARNING) <blk_device);
std::string nickname(rec->label);
int flags = 0;
if (fs_mgr_is_encryptable(rec)) {
flags |= android::vold::Disk::Flags::kAdoptable;
*has_adoptable = true;
}
if (fs_mgr_is_noemulatedsd(rec)
|| android::base::GetBoolProperty("vold.debug.default_primary", false)) {
flags |= android::vold::Disk::Flags::kDefaultPrimary;
}
vm->addDiskSource(std::shared_ptr(
new VolumeManager::DiskSource(sysPattern, nickname, flags)));
}
}
return 0;
}
4.3、跟踪fs_mgr_read_fstab_default() 函数可以看到 解析的fstab 文件的路径是:"/etc/recovery.fstab"
-->system/core/fs_mgr/fs_mgr_fstab.cpp
struct fstab *fs_mgr_read_fstab_default()
{
std::string default_fstab;
// Use different fstab paths for normal boot and recovery boot, respectively
if (access("/sbin/recovery", F_OK) == 0) {
default_fstab = "/etc/recovery.fstab";
} else { // normal boot
default_fstab = get_fstab_path();
}
...
get_fstab_path 函数:
static std::string get_fstab_path()
{
for (const char* prop : {"hardware", "hardware.platform"}) {
std::string hw;
if (!fs_mgr_get_boot_config(prop, &hw)) continue;
for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
std::string fstab_path = prefix + hw;
if (access(fstab_path.c_str(), F_OK) == 0) {
return fstab_path;
}
}
}
return std::string();
}
4.4、接着回到解析函数 process_config,这里判断voldmanaged 属性
if (fs_mgr_is_voldmanaged(rec)) {
if (fs_mgr_is_nonremovable(rec)) {
LOG(WARNING) <blk_device);
std::string nickname(rec->label);
//这里加个log打印出fastab 文件每行的属性标签
LOG(DEBUG) << "sysPattern="<blk_device<<",nickname="<label<<",mountPoint="<mount_point;
int flags = 0;
if (fs_mgr_is_encryptable(rec)) {
flags |= android::vold::Disk::Flags::kAdoptable;
*has_adoptable = true;
}
if (fs_mgr_is_noemulatedsd(rec)
|| android::base::GetBoolProperty("vold.debug.default_primary", false)) {
flags |= android::vold::Disk::Flags::kDefaultPrimary;
}
vm->addDiskSource(std::shared_ptr(
new VolumeManager::DiskSource(sysPattern, nickname, flags)));
}
打印的log如下,可以看到nickname 对应的fastab文件中的voldmanaged=usb就是设备类型了,这里是usb ,有的还有usbotg类型,vold里面会区分不同处理。
04-23 16:03:26.405 1582 1582 D vold :sysPattern=/devices/platform/passthrough/5b0d0000.usb/ci_hdrc.0
pendingList.clear();
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
SocketClient* c = *it;
// NB: calling out to an other object with mClientsLock held (safe)
int fd = c->getSocket();
if (FD_ISSET(fd, &read_fds)) {
pendingList.push_back(c);
c->incRef();
}
}
pthread_mutex_unlock(&mClientsLock);
while (!pendingList.empty()) {
it = pendingList.begin();
SocketClient* c = *it;
pendingList.erase(it);
if (!onDataAvailable(c)) {
release(c, false);
}
c->decRef();
}
}
}
5.3、SocketListener 的父类NetlinkListener 会重写onDataAvailable 处理uevent 事件,这里会封装成NetlinkEvent 对象,通过decode 方法解析完该事件的类型之后,调用NetlinkListener 的父类 NetlinkHandler onEvent 继续处理
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
int socket = cli->getSocket();
ssize_t count;
uid_t uid = -1;
bool require_group = true;
if (mFormat == NETLINK_FORMAT_BINARY_UNICAST) {
require_group = false;
}
count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,
mBuffer, sizeof(mBuffer), require_group, &uid));
if (count decode(mBuffer, count, mFormat)) {
onEvent(evt);
} else if (mFormat != NETLINK_FORMAT_BINARY) {
// Don't complain if parseBinaryNetlinkMessage returns false. That can
// just mean that the buffer contained no messages we're interested in.
SLOGE("Error decoding NetlinkEvent");
}
delete evt;
return true;
}
5.4、NetlinkHandler 的onEvent 处理实际上是传递给VolumeManager 的 handleBlockEvent 处理,这里也说明了VolumeManager 的角色,不管什么事件都到它这里统一分发到各个volume 处理。
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
VolumeManager *vm = VolumeManager::Instance();
const char *subsys = evt->getSubsystem();
if (!subsys) {
LOG(WARNING) <handleBlockEvent(evt);
}
5.5、VolumeManager 根据NetlinkEvent 的事件不同action 类型去做不同的处理,这里U盘插入事件action 值是1,U盘拔出 action 事件是2 。同时可以从NetlinkEvent 中获取设备类型和设备路径。主从设备号major minor 可以唯一标识一个设备,这里根据这个信息去区分是U盘设备还是sdcard 设备,进而设置flags 值。这里我们分析U盘插入事件handleDiskAdded
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
std::lock_guard lock(mLock);
if (mDebug) {
LOG(VERBOSE) << "----------------";
LOG(VERBOSE) << "handleBlockEvent with action " <getAction();
evt->dump();
}
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)) {
// For now, assume that MMC and virtio-blk (the latter is
// emulator-specific; see Disk.cpp for details) devices are SD,
// and that everything else is USB
int flags = source->getFlags();
if (major == kMajorBlockMmc
|| (android::vold::IsRunningInEmulator()
&& major >= (int) kMajorBlockExperimentalMin
&& major getNickname(), flags);
LOG(DEBUG) << "VolumeManager::handleBlockEvent="<<eventPath<<", nickname="<getNickname();
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 " <getAction();
break;
}
}
}
5.6、VolumeManager 的handleBlockEvent 在处理U盘插入事件时会创建一个disk 对象,通过该对象readMetadata 和readPartitions去读取U盘中 数据和分析信息。最后会创建PublicVolume 对象去处理U盘的 具体业务逻辑,包括和StorageManagerService 的通信。
void VolumeManager::handleDiskAdded(const std::shared_ptr& disk) {
// For security reasons, if secure keyguard is showing, wait
// until the user unlocks the device to actually touch it
if (mSecureKeyguardShowing) {
LOG(INFO) << "Found disk at " <getEventPath()
<create();
mDisks.push_back(disk);
}
}
-->system/vold/model/Disk.cpp
status_t Disk::create() {
LOG(DEBUG) <getListener();
if (listener) listener->onDiskCreated(getId(), mFlags);
readMetadata();
readPartitions();
return OK;
}
....
判断该U盘的文件系统格式是否支持,进入创建 PublicVolume
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)
createPublicVolume(partDevice);
break;
}
5.7、PublicVolume 就是具体处理U盘 挂载,卸载,格式化这些具体业务并且和framework 层的StorageManagerService 进行通信,这里是通过Bind跨进程实现的。之前做一个一个区分双U盘的问题。可以根据NetlinkEvent 时间的设备路径去进行区分,设备路径是唯一区分不同设备的方式。
六、结语本篇博客到这里就结束了,下次画个时序图加上。后续会继续分析 framework 层的StorageManagerSevice 的处理逻辑。
作者:安时光Mrsongs
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341