<Android开发> Android vold - 第二篇 vold 的main()函数简介

article/2025/10/2 18:09:12

本系列主要介绍 Android vold,分为以下篇章
<Android开发> Android vold - 第一篇 vold前言简介
<Android开发> Android vold - 第二篇 vold 的main()函数简介
<Android开发> Android vold - 第三篇 vold 的NetLinkManager类简介

继前一边vold简介后,我们来看看具体的代码内容。

4 vold的main()函数
要了解vold服务是干什么的,对直接的方法就是看你源码到底写了什么内容。而vold服务的入口当然就是main()函数了。先把main()的内容贴出来,再对其内容进行讲解。vold服务的main()函数路径和内容如下。

路径:LINUX/android/system/vold/main.cpp
int main(int argc, char** argv) {setenv("ANDROID_LOG_TAGS", "*:v", 1); //设置环境变量android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM)); //初始化log系统LOG(INFO) << "Vold 3.0 (the awakening) firing up";LOG(VERBOSE) << "Detected support for:"<< (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")<< (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")<< (android::vold::IsFilesystemSupported("vfat") ? " vfat" : ""); //输出支持的文件系统类型VolumeManager *vm; //管理volumeCommandListener *cl; //和Framework进行通讯CryptCommandListener *ccl; //运行 和Framework进行通讯 有关命令NetlinkManager *nm; //接收内核消息parse_args(argc, argv); //解析主函数参数sehandle = selinux_android_file_context_handle(); //获取selinux,权限有关if (sehandle) {selinux_android_set_sehandle(sehandle); //设置selinux}//在我们刚刚从init继承的套接字上快速抛出一个CLOEXECfcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);mkdir("/dev/block/vold", 0755); //创建设备节点/*当cryptfs检查并装载加密文件系统时*/klog_set_level(6);/* Create our singleton managers *//*创建我们的单例 管理器*/if (!(vm = VolumeManager::Instance())) {LOG(ERROR) << "Unable to create VolumeManager";exit(1);}if (!(nm = NetlinkManager::Instance())) {LOG(ERROR) << "Unable to create NetlinkManager";exit(1);}//获取属性vold.debugif (property_get_bool("vold.debug", false)) {vm->setDebug(true);}cl = new CommandListener();ccl = new CryptCommandListener();vm->setBroadcaster((SocketListener *) cl);nm->setBroadcaster((SocketListener *) cl);if (vm->start()) { //卸载原有可能存在的所有磁盘。并新建 "/data/media"/DiskPLOG(ERROR) << "Unable to start VolumeManager"; //卷管理器exit(1);}bool has_adoptable;bool has_quota;//根据配置文件/etc/vold.fstab,初始化VolumeManagerif (process_config(vm, &has_adoptable, &has_quota)) {PLOG(ERROR) << "Error reading configuration... continuing anyways";}//启动内核监听if (nm->start()) {PLOG(ERROR) << "Unable to start NetlinkManager";exit(1);}/*现在我们起床了,我们可以响应命令了*/if (cl->startListener()) {PLOG(ERROR) << "Unable to start CommandListener";exit(1);}if (ccl->startListener()) {PLOG(ERROR) << "Unable to start CryptCommandListener";exit(1);}//此调用应在侦听器启动后进行,以避免vold和init之间的死锁(有关详细信息,请参阅b/34278978)property_set("vold.has_adoptable", has_adoptable ? "1" : "0");property_set("vold.has_quota", has_quota ? "1" : "0");//在这里进行冷启动,这样就不会阻止启动,如果我们在Vold启动之前连接了闪存驱动器,也需要冷启动coldboot("/sys/block");  //触发内核sysfs发送uevent事件//最终我们将成为监控线程while(1) {pause();}LOG(ERROR) << "Vold exiting";exit(0);
}

上面是vold服务最开始运行的代码内容,我们来一一看看。

第3行:设置环境变量,其中"ANDROID_LOG_TAGS"环境变量名;“:v" 表示环境变量的值;1表示替换,即将”ANDROID_LOG_TAGS”环境变量的值替换为":v”。

第4行:初始化log系统,这样在Vold中可输出log;
该函数在“LINUX/android/system/core/base/logging.cpp”中实现,这里不在深入讨论log系统。

第9~11行:通过IsFilesystemSupported()函数判断支持的文件系统类型,并输出;该函数在”LINUX/android/system/vold/Utils.cpp”中实现;

static const char* kProcFilesystems = "/proc/filesystems";
bool IsFilesystemSupported(const std::string& fsType) {std::string supported;if (!ReadFileToString(kProcFilesystems, &supported)) {PLOG(ERROR) << "Failed to read supported filesystems";return false;}return supported.find(fsType + "\n") != std::string::npos;
}

从该函数可看到, kProcFilesystems 是操作读取的文件,该函数主要是读取文件filesystems,并匹配字符串。
文件内容如下:
在这里插入图片描述
第13行:定义了一个VolumeManager类vm,用来管理volume的;后面会对这个类进行详细解释。

第14行:定义了一个CommandListener类cl,用来同Framework进行通讯的;后面会对这个类进行详细解释。

第15行:定义了一个CryptCommandListener类cll,用来同Framework进行通讯,处理有关cmd的;后面会对这个类进行详细解释。

第16行:定义了一个NetlinkManager类nm,用来接收内核消息的;后面会对这个类进行详细解释。

第18行:函数parse_args()解析主函数的参数,vold.rc文件启动vold服务时传入了参数;内容如下:

路径:LINUX/android/system/vold/main.cpp
static void parse_args(int argc, char** argv) {static struct option opts[] = {{"blkid_context", required_argument, 0, 'b' },{"blkid_untrusted_context", required_argument, 0, 'B' },{"fsck_context", required_argument, 0, 'f' },{"fsck_untrusted_context", required_argument, 0, 'F' },};int c;while ((c = getopt_long(argc, argv, "", opts, nullptr)) != -1) {switch (c) {case 'b': android::vold::sBlkidContext = optarg; break;case 'B': android::vold::sBlkidUntrustedContext = optarg; break;case 'f': android::vold::sFsckContext = optarg; break;case 'F': android::vold::sFsckUntrustedContext = optarg; break;}}CHECK(android::vold::sBlkidContext != nullptr);CHECK(android::vold::sBlkidUntrustedContext != nullptr);CHECK(android::vold::sFsckContext != nullptr);CHECK(android::vold::sFsckUntrustedContext != nullptr);
}
路径:LINUX/android/system/vold/vold.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:s0class coresocket vold stream 0660 root mountsocket cryptd stream 0660 root mountioprio be 2writepid /dev/cpuset/foreground/tasksshutdown critical

option的结构体如下:

struct option
{const char *name;/*has_arg不能是枚举,因为一些编译器抱怨所有假定它是int的代码中的类型不匹配*/int has_arg;int *flag;int val;
};

这参数与selinux有关。

第20行,selinux_android_file_context_handle()获取selinux的handle;

路径:
LINUX/android/external/selinux/libselinux/src/android/android_platform.c
static const struct selinux_opt seopts_file_split[] = {
{ SELABEL_OPT_PATH, "/system/etc/selinux/plat_file_contexts" },
{ SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_file_contexts" }
};
static const struct selinux_opt seopts_file_rootfs[] = {
{ SELABEL_OPT_PATH, "/plat_file_contexts" },
{ SELABEL_OPT_PATH, "/nonplat_file_contexts" }
};struct selabel_handle* selinux_android_file_context_handle(void)
{if (selinux_android_opts_file_exists(seopts_file_split)) {return selinux_android_file_context(seopts_file_split,ARRAY_SIZE(seopts_file_split));} else {return selinux_android_file_context(seopts_file_rootfs,ARRAY_SIZE(seopts_file_rootfs));}
}
static struct selabel_handle* selinux_android_file_context(const struct selinux_opt *opts, unsigned nopts)
{struct selabel_handle *sehandle;struct selinux_opt fc_opts[nopts + 1];memcpy(fc_opts, opts, nopts*sizeof(struct selinux_opt));fc_opts[nopts].type = SELABEL_OPT_BASEONLY;fc_opts[nopts].value = (char *)1;sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, ARRAY_SIZE(fc_opts));if (!sehandle) {selinux_log(SELINUX_ERROR, "%s: Error getting file context handle (%s)\n",__FUNCTION__, strerror(errno));return NULL;}if (!compute_file_contexts_hash(fc_digest, opts, nopts)) { //selabel_close(sehandle);return NULL;}selinux_log(SELINUX_INFO, "SELinux: Loaded file_contexts\n");return sehandle;
}

设备log如下图:
在这里插入图片描述
第21~24行: 在前一句获取到selinux的handle后,则设置android的selinux;

路径:LINUX/android/external/selinux/libselinux/src/android/android_platform.c
void selinux_android_set_sehandle(const struct selabel_handle *hndl)
{fc_sehandle = (struct selabel_handle *) hndl;
}

因为selinux要详细讲解的话 能单独出一篇文章了。这里就不深入探索selinux,这里我们只需知道,vold根据vold.rc传入的参数可获得对应的权限,然后在selinux的handle里能使用对应权限的资源即可。

第26~27行: fcntl()针对(文件)描述符提供控制。参数fd是被参数cmd操作(如下面的描述)的描述符。针对cmd的值,fcntl能够接受第三个参数int arg;
第1个参数:其中android_get_control_socket(“vold”):获取对应socket的fd;F_SETFD
第2个参数:对描述符操作功能

  1. 复制一个现有的描述符(cmd=F_DUPFD).
  2. 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
  3. 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
  4. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
  5. 获得/设置记录锁(cmd=F_GETLK , F_SETLK或F_SETLKW).

第3个参数:cmd值的F_GETFD和F_SETFD:

F_GETFD: 取得与文件描述符fd联合的close-on-exec标志,类似FD_CLOEXEC。如果返回值和FD_CLOEXEC进行与运算结果是0的话,文件保持交叉式访问exec(),否则如果通过exec运行的话,文件将被关闭(arg 被忽略)

F_SETFD: 设置close-on-exec标志,该标志以参数arg的FD_CLOEXEC位决定,应当了解很多现存的涉及文件描述符标志的程序并不使用常数 FD_CLOEXEC,而是将此标志设置为0(系统默认,在exec时不关闭)或1(在exec时关闭)

第28行:创建目录文件“/dev/block/vold”,并给予0755的权限;

第30行:设置klog的等级,即kernel的log等级。

路径:LINUX/android/system/core/libcutils/klog.cpp
static int klog_level = KLOG_INFO_LEVEL;
//设置klog的级别
void klog_set_level(int level) {klog_level = level;
}static int __open_klog(void) {static const char kmsg_device[] = "/dev/kmsg";int ret = android_get_control_file(kmsg_device);if (ret >= 0) return ret;return TEMP_FAILURE_RETRY(open(kmsg_device, O_WRONLY | O_CLOEXEC));
}#define LOG_BUF_MAX 512void klog_writev(int level, const struct iovec* iov, int iov_count) {if (level > klog_level) return;static int klog_fd = __open_klog();if (klog_fd == -1) return;TEMP_FAILURE_RETRY(writev(klog_fd, iov, iov_count));
}void klog_write(int level, const char* fmt, ...) {if (level > klog_level) return;char buf[LOG_BUF_MAX];va_list ap;va_start(ap, fmt);vsnprintf(buf, sizeof(buf), fmt, ap);va_end(ap);buf[LOG_BUF_MAX - 1] = 0;struct iovec iov[1];iov[0].iov_base = buf;iov[0].iov_len = strlen(buf);klog_writev(level, iov, 1);
}

klog_write(0函数调用时,打开(创建文件) /dev/kmsg,并写入log内容。
第34行:函数VolumeManager::Instance()的内容可知,new VolumeManager()并返回给到vm;路径:LINUX/android/system/vold/VolumeManager.cpp;后面会对这个类进行详解;
如果new不成功对报如下错误Log:
LOG(ERROR) << “Unable to create VolumeManager”;

第39行:函数NetlinkManager::Instance()的内容可知,new NetlinkManager()并返回给到nm;路径:LINUX/android/system/vold/NetlinkManager.cpp;后面会对这个类进行详解;
如果new不成功对报如下错误Log:
LOG(ERROR) << “Unable to create NetlinkManager”;

第44行:函数property_get_bool()是用来获取属性值的,这里是要获取"vold.debug"这个属性值,且期待的返回值是false;如果返回true,则调用vm->setDebug(true);设置为Debug。

路径:LINUX/android/system/core/libcutils/properties.cpp
int8_t property_get_bool(const char *key, int8_t default_value) {if (!key) {return default_value;}int8_t result = default_value;char buf[PROPERTY_VALUE_MAX] = {'\0'};int len = property_get(key, buf, "");if (len == 1) {char ch = buf[0];if (ch == '0' || ch == 'n') {result = false;} else if (ch == '1' || ch == 'y') {result = true;}} else if (len > 1) {if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {result = false;} else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {result = true;}}return result;
}
路径:LINUX/android/system/vold/VolumeManager.cpp
int VolumeManager::setDebug(bool enable) {mDebug = enable;return 0;
}

第48行:new 一个 CommandListener()类给cl,后续分析该类;
第49行:new 一个 CryptCommandListener()类给cll,后续分析该类;
第50行:设置vm的广播为cl;在类定义里只有
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }

第51行:设置nm的广播为cl;在类定义里只有
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }

第54行:vm start,开始管理volume。后面详解。

第61行:process_config函数是根据配置文件/etc/vold.fstab 或/vendor/etc/fstab.xx 配置vm ;

路径:LINUX/android/system/vold/main.cpp
static int process_config(VolumeManager *vm, bool* has_adoptable, bool* has_quota) {fstab = fs_mgr_read_fstab_default();  //读配置文件 if (!fstab) {PLOG(ERROR) << "Failed to open default fstab";return -1;}/*遍历条目,查找vold管理的条目*/*has_adoptable = false;*has_quota = false;for (int i = 0; i < fstab->num_entries; i++) {if (fs_mgr_is_quota(&fstab->recs[i])) {*has_quota = true;}if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {if (fs_mgr_is_nonremovable(&fstab->recs[i])) {LOG(WARNING) << "nonremovable no longer supported; ignoring volume";continue;}std::string sysPattern(fstab->recs[i].blk_device);std::string nickname(fstab->recs[i].label);int flags = 0;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)));}}return 0;
}
路径:LINUX/android/system/core/fs_mgr/fs_mgr_fstab.cpp
/*尝试加载默认fstab 来自/odm/etc、/vendor/etc或/ <的硬件>文件。加载找到的第一个条目,并合并从设备树传入的fstab条目*/
struct fstab *fs_mgr_read_fstab_default()
{std::string hw;std::string default_fstab;//分别使用不同的fstab路径进行正常引导和恢复引导if (access("/sbin/recovery", F_OK) == 0) {default_fstab = "/etc/recovery.fstab";} else if (fs_mgr_get_boot_config("hardware", &hw)) {  // normal bootfor (const char *prefix : {"/odm/etc/fstab.","/vendor/etc/fstab.", "/fstab."}) {default_fstab = prefix + hw;if (access(default_fstab.c_str(), F_OK) == 0) break;}} else {LWARNING << __FUNCTION__ << "(): failed to find device hardware name";}//将从设备树传入的fstab条目与从defaultfstab文件中找到的条目进行组合struct fstab *fstab_dt = fs_mgr_read_fstab_dt();struct fstab *fstab = fs_mgr_read_fstab(default_fstab.c_str());return in_place_merge(fstab_dt, fstab);
}

加载fstab文件,并与dts中的fstab进行合并,最终保存到fstab,这其中主要遍历几个fstab的位置,包括:/etc/recovery.fstab,/odm/etc/fstab., /vendor/etc/fstab., /fstab.,我们的方案中实际会使用/vendor/etc/fstab.
fstab是fstab类型,即std::vector,fstab文件最终解析结果会存放到fstab;
我们可以看到fstab_rec的结构如下,它用于保存fstab中的一条记录

路径:LINUX/android/system/core/fs_mgr/include_fstab/fstab/fstab.h
/*条目必须按照在fstab中看到的相同顺序保存。除非明确要求,否则装载点上的查找应始终返回第一个*/
struct fstab {int num_entries;struct fstab_rec* recs;char* fstab_filename;
};
struct fstab_rec {char* blk_device;char* mount_point;char* fs_type;unsigned long flags;char* fs_options;int fs_mgr_flags;char* key_loc;char* key_dir;char* verity_loc;long long length;char* label;int partnum;int swap_prio;int max_comp_streams;unsigned int zram_size;uint64_t reserved_size;unsigned int file_contents_mode;unsigned int file_names_mode;unsigned int erase_blk_size;unsigned int logical_blk_size;
};

process_config函数中for循环将遍历fstab中被解析的每一条记录,为每条记录创建DiskSource,通过vm->addDiskSource存放到mDiskSources。
在这里插入图片描述
第65行:nm start,开始启动监听kernel的uevent事件。后面详解。

第71行:cl的startListener()函数是创建监听FW的socket线程的;后续详解;

第76行:ccl的startListener()函数也是创建监听FW的socket线程的;后续详解;

第82~83行:设置属性"vold.has_adoptable" 和 “vold.has_quota”;
对于has_adoptable 和has_quota 的值,取决于获取fatab配置;
-> process_config(vm, &has_adoptable, &has_quota)
-> if (fs_mgr_is_quota(&fstab->recs[i])) {
*has_quota = true;
}
-> return fstab->fs_mgr_flags & MF_QUOTA;

Quota是Linux的一个重要工具,使用Quota能对某一分区下指定用户或用户组进行磁盘限额。这里要说明的是,限额不是针对用户主目录,而是针对这个分区下的用户或用户组。Quota通过限制用户的blocks或者inodes起到限额的作用。

->if (fs_mgr_is_encryptable(&fstab->recs[i])) {
flags |= android::vold::Disk::Flags::kAdoptable;
*has_adoptable = true;
}
->return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);

LINUX/android/system/core/fs_mgr/fs_mgr_priv.h
该 .h文件对fstab的格式内容进行说明,如下:
/*fstab具有以下格式:
*任何以#开头的行都是注释,将被忽略 *忽略任何空行
*所有其他行必须采用以下格式:
*<mount_point><fs_type><moount_flags><fs_options><fs_mgr_options>
*
*<mount_flags>是一个逗号分隔的标志列表,可以传递给mount命令。该列表包括noatime、nosuid、nodev、nodiratime、ro、rw、remount和defaults。
*<fs_options>是一个逗号分隔的选项列表,由正在装载的文件系统接受。它直接传递到mount而不进行分析
*
*<fs_mgr_options>是一个逗号分隔的标志列表,用于控制fs_mgr程序的操作。该列表包括“wait”(等待)和“check”(检查),前者将等待文件存在,后者要求fs_mgr在安装文件系统之前在上运行fscheck程序。如果在只读文件系统上指定了检查,则忽略该检查。此外,“可加密”意味着文件系统可以被加密。“可加密”标志_MUST_后跟一个=和一个字符串,该字符串是加密密钥的位置。它可以是包含密钥的文件或分区的路径,也可以是表示密钥位于包含文件系统的分区的最后16K字节中的单词“footer”。
*
*当fs_mgr被请求挂载所有文件系统时,它将首先挂载所有没有_NOT_指定check的文件系统(包括只读文件系统并指定check,因为在这种情况下会忽略check),然后它将检查并挂载标记为check的文件系统。
*/

第86行:触发内核sysfs发送uevent事件;在这里进行冷启动,这样就不会阻止启动,如果我们在Vold启动之前连接了闪存驱动器(如U盘等),也需要冷启动;

路径:LINUX/android/system/vold/main.cpp
static void coldboot(const char *path) {DIR *d = opendir(path); //打开目录if(d) {do_coldboot(d, 0); //冷启动该目录closedir(d); //关闭目录}
}
递归扫描/sys目录下的uevent节点,然后写入字符串“add”,强制触发内核uevent事件
static void do_coldboot(DIR *d, int lvl) {struct dirent *de;int dfd, fd;dfd = dirfd(d);fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);if(fd >= 0) {//往/sys/block目录写入add\n事件//往/sys/block发送add uevent事件write(fd, "add\n", 4);  close(fd);
}
// 递归调用do_coldboot(),扫描uevent节点while((de = readdir(d))) {DIR *d2;if (de->d_name[0] == '.')continue;if (de->d_type != DT_DIR && lvl > 0)continue;fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);if(fd < 0)continue;d2 = fdopendir(fd);if(d2 == 0)close(fd);else {do_coldboot(d2, lvl + 1);closedir(d2);}}
}

第88~90行:至此,当前main函数的功能就完成,剩下的或都交给了vm、nm、cl、cll了,main()则转为监控线程,就是不干活,但又不能退出;除非异常。
接下来将会以 U盘设备插入、读取数据、拔出等内容,对vold的流程进行详细的解析。

vold 模块的介绍的第二篇主要讲解了 vold模块 main()函数具体实现了哪些内容。后续我们再对具体的借口、功能、实现类方法进行详细解析。


http://chatgpt.dhexx.cn/article/fmhAPjRR.shtml

相关文章

Android外部存储设备管理——vold挂载大容量存储设备

一、简介 Vold(volume Daemon)&#xff0c;即Volume守护进程&#xff0c;用来管理Android中存储类(USB-Storage&#xff0c;包含U盘和SD卡&#xff09;的热拔插事件&#xff0c;处于Kernel和Framework之间&#xff0c;是两个层级连接的桥梁。Vold在系统中以守护进程存在&#x…

Android Vold 架构简析

这篇文章中主要是分析一下&#xff0c;android系统里面的Vold——Vold是andorid系统的设备管理器&#xff0c;扮演着linux里面的udev的角色。它通过监听uevent的端口&#xff0c;取得 uevent事件&#xff0c;dispatch到 相应的Listener&#xff0c;执行相应的动作。 UEvent 在…

Android Vold架构

1. 总体架构 2. 流程概览 2.1 开启Vold 2.2 引导Uevent 2.3 处理事件 Vold - Volume Daemon存储类的守护进程&#xff0c;作为Android的一个本地服务&#xff0c;负责处理诸如SD、USB等存储类设备的插拔等事件。 1. 总体架构 Vold服务由volumeManager统一管控&#xff0c…

Vold 流程介绍

文章目录 前言框架MountService 流程Vold 流程 前言 印象中是参考 《深入理解 Android 卷 1 》 追的流程&#xff0c;差不多供参考吧 基于安卓 4.4 框架 MountService 流程 /*【初始化流程总结】&#xff1a; SystemServerinitAndLoop()// 创建 MountService 服务对象mountS…

ANDROID中的VOLD分析

现在可能很少有人会用mknod这个命令了&#xff0c;也很少有使用它的机会&#xff0c;但就在几年前&#xff0c;这还是一项linux工程师的必备技能&#xff0c;在制作文件系统前或加载新的驱动前&#xff0c;我们必须小心翼翼的创建设备节点。 不需要使用mknod并不是他消失了&am…

Android -- Vold机制简要分析

Android -- Vold机制简要分析 Vold是用于管理和控制Android外部存储介质的后台进程&#xff0c;这里说的管控&#xff0c;主要包括SD卡的插拔、挂载/卸载和格式化等&#xff1b;它是Android平台外部存储系统的管控枢纽。 Vold的整个控制模块主要由三个类模块构成&#xff1a;Ne…

Android Vold简介(一)

Vold(volume Daemon)&#xff0c;即Volume守护进程&#xff0c;用来管理Android中存储类的热拔插事件&#xff0c;处于Kernel和Framework之间&#xff0c;是两个层级连接的桥梁。先来看一下Vold在Android系统的整体架构。 该图主要包含了Framework和Vold进程的&#xff0c;Ke…

vold

一、Vold工作机制 Vold是Volume Daemon的缩写&#xff0c;它是Android平台中外部存储系统的管控中心&#xff0c;是管理和控制Android平台外部存储设备的后台进程。其功能主要包括&#xff1a;SD卡的插拔事件检测、SD卡挂载、卸载、格式化等。 如上图所示&#xff0c;Vold中的…

Vold工作流程分析学习

一 Vold工作机制分析 vold进程&#xff1a;管理和控制Android平台外部存储设备&#xff0c;包括SD插拨、挂载、卸载、格式化等&#xff1b; vold进程接收来自内核的外部设备消息。 Vold框架图如下&#xff1a; Vold接收来自内核的事件&#xff0c;通过netlink机制。 Netlink 是…

Android vold介绍

目录 1. 前言2. vold概述3. vold初始化|- -vm->start()|- -process_config|- -hardware::configureRpcThreadpool|- -vold::VoldNativeService::start|- -nm->start() 4. StorageManagerService|- -SM与vold建立关联|- - -startService(serviceClass) |- -StorageManager…

<Android开发> Android vold - 第一篇 vold前言简介

本系列主要介绍 Android vold&#xff0c;分为以下篇章 &#xff1c;Android开发&#xff1e; Android vold - 第一篇 vold前言简介 &#xff1c;Android开发&#xff1e; Android vold - 第二篇 vold 的main()函数简介 &#xff1c;Android开发&#xff1e; Android vold - 第…

Vold原理介绍

一、 Vold简介 Android中Vold是volume Daemon&#xff0c;即Volume守护进程&#xff0c;用来管理Android中存储类的热拔插事件。这里的热插拔涉及的场景如&#xff1a; 1. 手机usb以MTP或者传输照片方式插拔PC端后磁盘数据的挂卸载&#xff1b; 2. 设备开关机过程中存储设备各分…

C语言Switch....case用法

概述 C语言switch语句通常用于多个条件判断&#xff0c;根据不同情况执行不同的代码块。它的使用形式如下&#xff1a; switch&#xff08;表达式&#xff09; { case 常量表达式1&#xff1a; 语句序列1 break; case 常量表达式2&#xff1a; 语句序列2 break; ...... default…

MySQL 入门:Case 语句很好用

引言 MySQL CASE 是一个 MySQL 语句查询关键字&#xff0c;它定义了处理循环概念以执行条件集并使用 IF ELSE 返回匹配案例的方式。 MySQL 中的 CASE 是一种控制语句&#xff0c;它验证条件案例集&#xff0c;并在第一个案例满足 else 值时显示值并退出循环。 如果没有找到 T…

Shell 编程之 case 语句

一、case 语句 1、case 语句概述 (1)case 语句的作用 使用 case 语句改写 if 多分支可以使脚本结构更加清晰、层次分明。针对变量的不同取 值&#xff0c;执行不同的命令序列。 2、case 语句的结构: case 变量值 in 模式 1) 命令序列 1 ;; 模式 2) 命令序列 2 ;; *…

【shell】case实现简单的系统工具箱

case实现简单的系统工具箱 case实现简单的系统工具箱 #!/usr/bin/bash #system manage #by racon 2020-04-19menu() {cat <<-EOF########################################## h. help ## f. disk partition ## d. filesystem mount ## m. memory ## u. system lo…

SQL CASE语句的使用

SQL CASE语句的使用 CASE是一个控制流语句&#xff0c;其作用与IF-THEN-ELSE语句非常相似&#xff0c;可根据数据选择值。 CASE语句遍历条件并在满足第一个条件时返回值。 因此&#xff0c;一旦条件成立&#xff0c;它将短路&#xff0c;从而忽略后面的子句并返回结果。 正如我…

数据库 case 用法

【转载】:数据库中case when 的用法 CASE WHEN 及 SELECT CASE WHEN的用法Case具有两种格式。简单Case函数和Case搜索函数。 1.简单Case函数 CASE sex WHEN 1 THEN 男 WHEN 2 THEN 女 ELSE 其他 END 2.Case搜索函数 CASE WHEN sex 1 THEN 男 WHEN sex 2 THEN 女 ELSE 其他 …

SQL之CASE WHEN用法详解

简单CASE WHEN函数&#xff1a; CASE SCORE WHEN A THEN 优 ELSE 不及格 END CASE SCORE WHEN B THEN 良 ELSE 不及格 END CASE SCORE WHEN C THEN 中 ELSE 不及格 END 等同于&#xff0c;使用CASE WHEN条件表达式函数实现&#xff1a; CASE WHEN SCORE A THEN 优WHEN SCORE…

switch case语法

文章目录 switch case组合不要拿青龙偃月刀去削苹果case的作用是什么&#xff1f;break的作用是什么&#xff1f;case后面的值有什么要求吗&#xff1f;case语句的排列顺序问题default语句相关问题使用case语句的一些注意事项 switch case组合 基本语法结构 switch(整型常量/…