android分区挂载fstab
2017-01-17 14:58
621 查看
SoC : RK3288
Platform : Android 5.1
本文简述android启动后分区是如何挂载,简述fstab的作用。
一、fstab
Android 5.1分区挂载配置文件:fstab.xxxx.xxxx,不同于android4.4(vold.fstab),在5.1后分区配置文件发生了变化。fstab定义如下:
mount flags parameters文件系统的参数:
async/sync : 设置是否为同步方式运行
auto/noauto : 当下载mount -a 的命令时,此文件系统是否被主动挂载。默认为auto
rw/ro : 是否以以只读或者读写模式挂载
exec/noexec : 限制此文件系统内是否能够进行”执行”的操作
user/nouser : 是否允许用户使用mount命令挂载
suid/nosuid : 是否允许SUID的存在
usrquota : 启动文件系统支持磁盘配额模式
grpquota : 启动文件系统对群组磁盘配额模式的支持
defaults : 同时具有rw,suid,dev,exec,auto,nouser,async等默认参数的设置
fstab有何作用呢?
通过配置fstab,vold服务通过函数process_config调用fs_mgr_read_fstab来完成对分区文件的解析。
fs_mgr_read_fstab—>fs_mgr进程,该服务源码位于system/core/fs_mgr/fs_mgr_main.c,主要是解析分区文件,并完成挂载任务的最终工作者。
接下来,我们来看看fstab是如何被init获取的。
二、init
kernel加载完后第一个执行的就是init进程,init进程会根据init.rc的规则启动进程或者服务,init.rc通过
import /init.${ro.hardware}.rc导入平台的规则。device/rockchip/common/init.rk30board.rc中:
device/rockchip/common/init.rk30board.bootmode.emmc.rc中:
mount_all是一条命令,fstab.rk30board是传入的参数,在system/core/init/keywords.h中,定义了mount_all命令:
从以上定义中看出,mount_all命令对应的是do_mount_all函数,fstab.rk30board是do_mount_all函数的参数。do_mount_all()位于system/core/init/builtins.c中:
上述函数最终调用fs_mgr_read_fstab(args[1])加载分区挂载文件的内容到fstab结构体中,最后通过 fs_mgr_mount_all(fstab)挂载分区。args[1]就是传给do_mount_all的参数fstab.rk30board
这里又产生一个疑问?传入的参数是fstab.rk30board,为什么不是fstab.rk30board.bootmode.emmc呢?
在out/target/product/rk3288_box/root/下没有发现fstab.rk30board文件,但是在系统启动后,adb shell下根目录中可以看到有fstab.rk30board文件,该文件和fstab.rk30board.bootmode.emmc文件的内容相同。因此do_mount_all打开文件fstab.rk30board是OK的。那么,fstab.rk30board文件是何时被创建的呢?在adb shell下输入:
fstab.rk30board是链接到fstab.rk30board.bootmode.emmc的。搜索源码,最终在init.c中找到,简单分析下init,在main()中,调用process_kernel_cmdline()函数,获取/proc/cmdline
这里的/proc/cmdline参数和parameters中的并不相同,只是在parameters内容的后面加了一部分参数,其中就包括androidboot.mode=emmc,指定了bootmode=emmc,后续讲述。调用export_kernel_boot_props()函数
再调用symlink_fstab()函数
因此,就是在这里将fstab.rk30board.bootmode.emmc软链接到fstab.rk30board,在后面调用do_mount_all时打开fstab.rk30board解析分区内容。
有关解析分区文件并挂载的fs_mgr源码,有待分析。
三、bootmode
前文讲述到在/proc/cmdline中加入了参数androidboot.mode=emmc,build.prop重没有ro.bootmode,又是如何获取到androidboot.mode并添加的呢?
其实ro.bootmode的属性是通过ro.boot.mode来设置的,而ro.boot.mode这个属性是读取/proc/cmdlinec参数,最终在import_kernel_nv函数中设置的,init.c中:
同样的,ro.boot.hardware和ro.boot.serialno也是在/proc/cmdline中设置通过调用import_kernel_nv函数设置的:
实际上/proc/cmdline所显示的androidboot.mode=emmc并不是parameter参数传递的,而是在kernel中指定的,这就是前文所述。位于kernel/block/partitions/rk.c文件中:
这里的androidboot.mode=charger是充电图标显示的模式。
本文只是简单讲述fstab,bootmode等的来龙去脉,更深层次的知识有待研究。
Platform : Android 5.1
本文简述android启动后分区是如何挂载,简述fstab的作用。
一、fstab
Android 5.1分区挂载配置文件:fstab.xxxx.xxxx,不同于android4.4(vold.fstab),在5.1后分区配置文件发生了变化。fstab定义如下:
<src> <mount point> <filesystem type> <mount flags parameters> <fs_mgr_flags> /dev/.. /mnt/internal_sd vfat defaults voldmanaged=internal_sd:14,nomulated
mount flags parameters文件系统的参数:
async/sync : 设置是否为同步方式运行
auto/noauto : 当下载mount -a 的命令时,此文件系统是否被主动挂载。默认为auto
rw/ro : 是否以以只读或者读写模式挂载
exec/noexec : 限制此文件系统内是否能够进行”执行”的操作
user/nouser : 是否允许用户使用mount命令挂载
suid/nosuid : 是否允许SUID的存在
usrquota : 启动文件系统支持磁盘配额模式
grpquota : 启动文件系统对群组磁盘配额模式的支持
defaults : 同时具有rw,suid,dev,exec,auto,nouser,async等默认参数的设置
fstab有何作用呢?
通过配置fstab,vold服务通过函数process_config调用fs_mgr_read_fstab来完成对分区文件的解析。
fs_mgr_read_fstab—>fs_mgr进程,该服务源码位于system/core/fs_mgr/fs_mgr_main.c,主要是解析分区文件,并完成挂载任务的最终工作者。
接下来,我们来看看fstab是如何被init获取的。
二、init
kernel加载完后第一个执行的就是init进程,init进程会根据init.rc的规则启动进程或者服务,init.rc通过
import /init.${ro.hardware}.rc导入平台的规则。device/rockchip/common/init.rk30board.rc中:
import init.${ro.hardware}.bootmode.{ro.bootmode}.rc -----> init.rk30board.bootmode.emmc.rc
device/rockchip/common/init.rk30board.bootmode.emmc.rc中:
on fs mount_all fstab.rk30board
mount_all是一条命令,fstab.rk30board是传入的参数,在system/core/init/keywords.h中,定义了mount_all命令:
KEYWORD(mount_all, COMMAND, 1, do_mount_all)
从以上定义中看出,mount_all命令对应的是do_mount_all函数,fstab.rk30board是do_mount_all函数的参数。do_mount_all()位于system/core/init/builtins.c中:
/* * This function might request a reboot, in which case it will * not return. */ int do_mount_all(int nargs, char **args) { pid_t pid; int ret = -1; int child_ret = -1; int status; const char *prop; struct fstab *fstab; if (nargs != 2) { return -1; } /* * Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and * do the call in the child to provide protection to the main init * process if anything goes wrong (crash or memory leak), and wait for * the child to finish in the parent. */ pid = fork(); if (pid > 0) { /* Parent. Wait for the child to return */ int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); if (wp_ret < 0) { /* Unexpected error code. We will continue anyway. */ NOTICE("waitpid failed rc=%d, errno=%d\n", wp_ret, errno); } if (WIFEXITED(status)) { ret = WEXITSTATUS(status); } else { ret = -1; } } else if (pid == 0) { /* child, call fs_mgr_mount_all() */ klog_set_level(6); /* So we can see what fs_mgr_mount_all() does */ fstab = fs_mgr_read_fstab(args[1]); //解析分区文件fstab child_ret = fs_mgr_mount_all(fstab); fs_mgr_free_fstab(fstab); if (child_ret == -1) { ERROR("fs_mgr_mount_all returned an error\n"); } _exit(child_ret); } else { /* fork failed, return an error */ return -1; } if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) { property_set("vold.decrypt", "trigger_encryption"); } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) { property_set("ro.crypto.state", "encrypted"); property_set("vold.decrypt", "trigger_default_encryption"); } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { property_set("ro.crypto.state", "unencrypted"); /* If fs_mgr determined this is an unencrypted device, then trigger * that action. */ action_for_each_trigger("nonencrypted", action_add_queue_tail); } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) { /* Setup a wipe via recovery, and reboot into recovery */ ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n"); ret = wipe_data_via_recovery(); /* If reboot worked, there is no return. */ } else if (ret > 0) { ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret); } /* else ... < 0: error */ return ret; }
上述函数最终调用fs_mgr_read_fstab(args[1])加载分区挂载文件的内容到fstab结构体中,最后通过 fs_mgr_mount_all(fstab)挂载分区。args[1]就是传给do_mount_all的参数fstab.rk30board
这里又产生一个疑问?传入的参数是fstab.rk30board,为什么不是fstab.rk30board.bootmode.emmc呢?
在out/target/product/rk3288_box/root/下没有发现fstab.rk30board文件,但是在系统启动后,adb shell下根目录中可以看到有fstab.rk30board文件,该文件和fstab.rk30board.bootmode.emmc文件的内容相同。因此do_mount_all打开文件fstab.rk30board是OK的。那么,fstab.rk30board文件是何时被创建的呢?在adb shell下输入:
ls -l fstab.rk30board lrwxrwxrwx root root 2016-11-28 07:22 fstab.rk30board -> /fstab.rk30board.bootmode.emmc
fstab.rk30board是链接到fstab.rk30board.bootmode.emmc的。搜索源码,最终在init.c中找到,简单分析下init,在main()中,调用process_kernel_cmdline()函数,获取/proc/cmdline
static void process_kernel_cmdline(void) { /* don't expose the raw commandline to nonpriv processes */ chmod("/proc/cmdline", 0440); /* first pass does the common stuff, and finds if we are in qemu. * second pass is only necessary for qemu to export all kernel params * as props. */ import_kernel_cmdline(0, import_kernel_nv); if (qemu[0]) import_kernel_cmdline(1, import_kernel_nv); /* now propogate the info given on command line to internal variables * used by init as well as the current required properties */ export_kernel_boot_props(); }
这里的/proc/cmdline参数和parameters中的并不相同,只是在parameters内容的后面加了一部分参数,其中就包括androidboot.mode=emmc,指定了bootmode=emmc,后续讲述。调用export_kernel_boot_props()函数
static void export_kernel_boot_props(void) { char tmp[PROP_VALUE_MAX]; int ret; unsigned i; struct { const char *src_prop; const char *dest_prop; const char *def_val; } prop_map[] = { #ifdef TARGET_BOARD_PLATFORM_SOFIA3GR { "ro.boot.serialno", "ro.serialno", "", }, #endif { "ro.boot.mode", "ro.bootmode", "unknown", }, { "ro.boot.baseband", "ro.baseband", "unknown", }, { "ro.boot.bootloader", "ro.bootloader", "unknown", }, }; for (i = 0; i < ARRAY_SIZE(prop_map); i++) { ret = property_get(prop_map[i].src_prop, tmp); if (ret > 0) property_set(prop_map[i].dest_prop, tmp); else property_set(prop_map[i].dest_prop, prop_map[i].def_val); } ret = property_get("ro.boot.console", tmp); if (ret) strlcpy(console, tmp, sizeof(console)); /* save a copy for init's usage during boot */ property_get("ro.bootmode", tmp); strlcpy(bootmode, tmp, sizeof(bootmode)); /* if this was given on kernel command line, override what we read * before (e.g. from /proc/cpuinfo), if anything */ ret = property_get("ro.boot.hardware", tmp); if (ret) strlcpy(hardware, tmp, sizeof(hardware)); property_set("ro.hardware", hardware); snprintf(tmp, PROP_VALUE_MAX, "%d", revision); property_set("ro.revision", tmp); /* TODO: these are obsolete. We should delete them */ if (!strcmp(bootmode,"factory")) property_set("ro.factorytest", "1"); else if (!strcmp(bootmode,"factory2")) property_set("ro.factorytest", "2"); else property_set("ro.factorytest", "0"); #ifdef NAND_EMMC sysmlink_nand_emmc_fstab(); #endif symlink_fstab(); }
再调用symlink_fstab()函数
static void symlink_fstab() { char fstab_path[255] = "/fstab."; char fstab_default_path[50] = "/fstab."; int ret = -1; // fstab.rk30board.bootmode.unknown strcat(fstab_path, hardware); //fstab.rk30board strcat(fstab_path, ".bootmode."); //fstab.rk30board.bootmode. strcat(fstab_path, bootmode); //fstab.rk30board.bootmode.emmc strcat(fstab_default_path, hardware); //fstab.rk30board //symlink /fstab.rk30board.bootmode.emmc /fstab.rk30board ret = symlink(fstab_path, fstab_default_path); if (ret < 0) { ERROR("%s : failed", __func__); } }
因此,就是在这里将fstab.rk30board.bootmode.emmc软链接到fstab.rk30board,在后面调用do_mount_all时打开fstab.rk30board解析分区内容。
有关解析分区文件并挂载的fs_mgr源码,有待分析。
三、bootmode
前文讲述到在/proc/cmdline中加入了参数androidboot.mode=emmc,build.prop重没有ro.bootmode,又是如何获取到androidboot.mode并添加的呢?
其实ro.bootmode的属性是通过ro.boot.mode来设置的,而ro.boot.mode这个属性是读取/proc/cmdlinec参数,最终在import_kernel_nv函数中设置的,init.c中:
static void import_kernel_nv(char *name, int for_emulator) { char *value = strchr(name, '='); int name_len = strlen(name); if (value == 0) return; *value++ = 0; if (name_len == 0) return; if (for_emulator) { /* in the emulator, export any kernel option with the * ro.kernel. prefix */ char buff[PROP_NAME_MAX]; int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name ); if (len < (int)sizeof(buff)) property_set( buff, value ); return; } if (!strcmp(name,"qemu")) { strlcpy(qemu, value, sizeof(qemu)); } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) { //androidboot.mode=emmc --->androidboot.是12字节 const char *boot_prop_name = name + 12; // boot_prop_name指向mode位置处 char prop[PROP_NAME_MAX]; int cnt; cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name); if (cnt < PROP_NAME_MAX) property_set(prop, value); } }
同样的,ro.boot.hardware和ro.boot.serialno也是在/proc/cmdline中设置通过调用import_kernel_nv函数设置的:
cat /proc/cmdline vmalloc=496M console=ttyFIQ0 androidboot.hardware=rk30board androidboot.console=ttyFIQ0 board.ap_has_alsa=0 init=/init mtdparts=rk29xxnand:0x00002000@0x00002000(uboot),0x00002000@0x00004000(misc),0x00008000@0x00006000(resource),0x00008000@0x0000e000(kernel), 0x00010000@0x00016000(boot),0x00010000@0x00026000(recovery),0x0001a000@0x00036000(backup),0x00040000@0x00050000(cache),0x00002000@0x00090000(kpanic), 0x00100000@0x00092000(system),0x00002000@0x00192000(metadata),0x00200000@0x00194000(userdata),0x00020000@0x00394000(radical_update), -@0x003B4000(user) storagemedia=emmc uboot_logo=0x02000000@0x7dc00000 loader.timestamp=2015-07-17_14:37:15 androidboot.serialno=11112222333344445555 <NULL> androidboot.mode=emmc
实际上/proc/cmdline所显示的androidboot.mode=emmc并不是parameter参数传递的,而是在kernel中指定的,这就是前文所述。位于kernel/block/partitions/rk.c文件中:
static void rkpart_bootmode_fixup(void) { const char mode[] = " androidboot.mode=emmc"; const char charger[] = " androidboot.charger.emmc=1"; char *new_command_line; size_t saved_command_line_len = strlen(saved_command_line); if (strstr(saved_command_line, "androidboot.mode=charger")) { new_command_line = kzalloc(saved_command_line_len + strlen(charger) + 1, GFP_KERNEL); sprintf(new_command_line, "%s%s", saved_command_line, charger); } else { new_command_line = kzalloc(saved_command_line_len + strlen(mode) + 1, GFP_KERNEL); sprintf(new_command_line, "%s%s", saved_command_line, mode); } saved_command_line = new_command_line; }
这里的androidboot.mode=charger是充电图标显示的模式。
本文只是简单讲述fstab,bootmode等的来龙去脉,更深层次的知识有待研究。
相关文章推荐
- Android 重新挂载分区mount remount
- mqd-mount一个分区,并在/etc/fstab写入了挂载信息,后卸载并删除硬盘,导致无法开机
- Android 重新挂载分区mount remount
- UBUNTU 永久性挂载分区——修改分区文件/etc/fstab
- Linux系统攻略 用UUID在Fstab中挂载分区
- android 新增分区以及挂载方法
- Linux系统fstab挂载分区不建议使用device ID
- Android下手动挂载分区
- Linux系统攻略 用UUID在Fstab中挂载分区
- Linux fstab自动挂载分区教程
- Linux系统攻略 用UUID在Fstab中挂载分区
- linux启动自动挂载分区和/etc/fstab简单修复
- (转)Linux系统攻略 用UUID在Fstab中挂载分区
- fstab挂载硬盘及分区
- ubuntu14.04用fstab挂载分区写入权限问题
- Linux系统攻略 用UUID在Fstab中挂载分区
- mount挂载分区,和/etc/fstab开机自动挂载
- Linux常见设备及相应/dev/xxx文件名、Mount Point、挂载点、Mount命令、fstab、挂载分区
- 修改fstab配置项, 自动挂载分区