Android启动之后,系统的分区工作已经完成,但是分区是在哪来进行的?
一个大的系统启动不可能每个分区都要去手动挂载,添加,肯定有一个自动加载的工作,Android 本身也是一个Linux系统,我们先从Linux的分区开始了解。
Linux系统分区
linux 开机时会
自动加载分区,只不过要先配置好分区文件fstab (/etc/fstab),如下:
# /etc/fstab
/dev/hda9 swap swap defaults 0 0
/dev/hda1 / ext2 defaults 1 1
/dev/hda5 /home ext2 defaults 1 1
/dev/hda6 /usr ext2 defaults 1 1
/dev/hda7 /usr/local ext2 defaults 1 1
/dev/hda8 /var ext2 defaults 1 1
/dev/hdb /cdrom iso9660 noauto,user 0 0
none /proc proc defaults 0 0
none /dev/pts devpts gid=5,mode=620 0 0
fstab文件的作用
文件/etc/fstab存放的是系统中的文件系统信息。当正确的设置了该文件,则可以通过"mount /directoryname"命令来加载
一个文件系统,每种文件系统都对应一个独立的行,每行中的字段都有空格或tab键分开。同时fsck、 mount、umount的等命令都
利用该程序。
fstab文件格式
下面是/etc/fatab文件的一个示例行:
fs_spec fs_file fs_type fs_options fs_dump fs_pass
/dev/hda1 / ext2 defaults 1 1
fs_spec - 该字段定义希望加载的文件系统所在的设备或远程文件系统,对于一般的本地块设备情况来说:IDE设备一般描述为
/dev/hdaXN,X是IDE设备通道 (a, b, or c),N代表分区号;SCSI设备一描述为/dev/sdaXN。对于NFS情况,格式一般为:
例 如:`knuth.aeb.nl:/'。对于procfs,使用`proc'来定义。
fs_file - 该字段描述希望的文件系统加载的目录点,对于swap设备,该字段为none;对于加载目录名包含空格的情况,用40来
表示空格。
fs_type - 定义了该设备上的文件系统,一般常见的文件类型为ext2 (linux设备的常用文件类型)、vfat(Windows系统的fat32格
式)、NTFS、iso9600等。
fs_options - 指定加载该设备的文件系统是需要使用的特定参数选项,多个参数是由逗号分隔开来。对于大多数系统使用"defaults"
就可以满足需要。其他常见的选项包括:
选项含义
ro 以只读模式加载该文件系统
sync 不对该设备的写操作进行缓冲处理,这可以防止在非正常关机时情况下破坏文件系统,但是却降低了计算机速度
user 允许普通用户加载该文件系统
quota 强制在该文件系统上进行磁盘定额限制
noauto 不再使用mount -a命令(例如系统启动时)加载该文件系统
fs_dump - 该选项被"dump"命令使用来检查一个文件系统应该以多快频率进行转储,若不需要转储就设置该字段为0
fs_pass - 该字段被fsck命令用来决定在启动时需要被扫描的文件系统的顺序,根文件系统"/"对应该字段的值应该为1,其他文件系统应该为2。若该文件系统无需在启动时扫描则设置该字段为0
Android系统分区文件
android系统也有自己的系统分区文件fstab.${ro.hardware} ,一般为fstab.muji , fstab.monet, .....
例如:fstab.muji文件
# Android fstab file.
#<src> <mnt_point> <type> <mnt_flags> <fs_mgr_flags>
# The filesystem that contains the filesystem checker binary (typically /system) cannot
# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK
/dev/block/platform/mstar_mci.0/by-name/system /system ext4 ro wait
/dev/block/platform/mstar_mci.0/by-name/cache /cache ext4 noatime,nosuid,nodev wait,block_validity,nodiscard,data=ordered,journal_checksum
/dev/block/platform/mstar_mci.0/by-name/userdata /data ext4 noatime,nosuid,nodev wait,block_validity,nodiscard,data=ordered,journal_checksum
/dev/block/platform/mstar_mci.0/by-name/tvservice /tvservice ext4 ro wait
/dev/block/platform/mstar_mci.0/by-name/tvconfig /tvconfig ext4 noatime,nosuid,nodev wait,block_validity,nodiscard,data=ordered,journal_checksum
/dev/block/platform/mstar_mci.0/by-name/tvdatabase /tvdatabase ext4 noatime,nosuid,nodev wait,block_validity,nodiscard,data=ordered,journal_checksum
/dev/block/platform/mstar_mci.0/by-name/tvcustomer /tvcustomer ext4 noatime,nosuid,nodev wait,block_validity,nodiscard,data=ordered,journal_checksum
/dev/block/platform/mstar_mci.0/by-name/usersdcard /usersdcard vfat noatime,nosuid,nodev wait,block_validity,nodiscard,data=ordered,journal_checksum
/dev/block/platform/mstar_mci.0/by-name/factory /factory ext4 noatime,nosuid,nodev wait,block_validity,nodiscard,data=ordered,journal_checksum
/dev/block/mmcblk0boot0 /boot1 emmc defaults defaults
/dev/block/mmcblk0boot1 /boot2 emmc defaults defaults
/dev/block/platform/mstar_mci.0/by-name/MBOOT /MBOOT emmc defaults defaults
/dev/block/platform/mstar_mci.0/by-name/MPOOL /MPOOL emmc defaults defaults
/dev/block/platform/mstar_mci.0/by-name/misc /misc emmc defaults defaults
/dev/block/platform/mstar_mci.0/by-name/recovery /recovery emmc defaults defaults
/dev/block/platform/mstar_mci.0/by-name/boot /boot emmc defaults defaults
/dev/block/platform/mstar_mci.0/by-name/tee /tee emmc defaults defaults
/dev/block/platform/mstar_mci.0/by-name/RTPM /RTPM emmc defaults defaults
/dev/block/platform/mstar_mci.0/by-name/dtb /dtb emmc defaults defaults
这里描述了系统所有分区位置,挂载点,类型,一些挂载参数
分区启动方式
从源码看,目前发现有两处地方有进行分区的动作:1. init脚本 ; 2. vold服务程序 3. fs_mgr
init脚本
on fs
mkdir /tvservice
mkdir /tvconfig
mkdir /tvdatabase
mkdir /tvcustomer
mount_all /fstab.monet
mount_all 命令在system\core\init\keywords.h中注册
KEYWORD(mount_all, COMMAND, 1, do_mount_all)
接着会调用init进程的do_mount_all (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]);
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;
}
......
return ret;
}
fs_mgr_read_fstab完整fstab.muji 的解析,fs_mgr_mount_all完成所有目录的挂载
vold服务
该服务主程序源码位于system\vold\main.cpp,通过函数process_config 调用fs_mgr_read_fstab完成文件的解析,注册的CommandListener在接收到mountall指令之后
完成所有分区注册:
int CommandListener::StorageCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
/* Guarantied to be initialized by vold's main() before the CommandListener is active */
extern struct fstab *fstab;
dumpArgs(argc, argv, -1);
if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
return 0;
}
if (!strcmp(argv[1], "mountall")) {
if (argc != 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: mountall", false);
return 0;
}
fs_mgr_mount_all(fstab);
cli->sendMsg(ResponseCode::CommandOkay, "Mountall ran successfully", false);
return 0;
}
if (!strcmp(argv[1], "users")) {
DIR *dir;
struct dirent *de;
if (argc < 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument: user <mountpoint>", false);
return 0;
}
if (!(dir = opendir("/proc"))) {
cli->sendMsg(ResponseCode::OperationFailed, "Failed to open /proc", true);
return 0;
}
while ((de = readdir(dir))) {
int pid = Process::getPid(de->d_name);
if (pid < 0) {
continue;
}
char processName[255];
Process::getProcessName(pid, processName, sizeof(processName));
if (Process::checkFileDescriptorSymLinks(pid, argv[2]) ||
Process::checkFileMaps(pid, argv[2]) ||
Process::checkSymLink(pid, argv[2], "cwd") ||
Process::checkSymLink(pid, argv[2], "root") ||
Process::checkSymLink(pid, argv[2], "exe")) {
char msg[1024];
snprintf(msg, sizeof(msg), "%d %s", pid, processName);
cli->sendMsg(ResponseCode::StorageUsersListResult, msg, false);
}
}
closedir(dir);
cli->sendMsg(ResponseCode::CommandOkay, "Storage user list complete", false);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
}
return 0;
}
我们可以看到在fs_mgr_mount_all(fstab);之后所有客户端。
fs_mgr 进程
该服务源码位于system\core\fs_mgr\fs_mgr_main.c,该模块是解析分区文件,并完成挂载任务的最终工作者。