说明:本分析基于Linux2.6内核和Android2.3版本,其他版本仅供参考。
Android2.3及Linux2.6.29内核模拟器版本编译与调试
一、前言
从前边Linux内核启动之根文件系统挂载分析一文我们分析到Linux内核启动之后的根文件系统要么是rootfs(ramdisk释放到rootfs后,其根目录存在init的情况下),要么是磁盘等文件系统;系统根目录要么是rootfs的根目录,要么是磁盘的根目录。
但我们一直特别关心的设备文件系统、proc文件系统,还有就是Linux2.6内核引入的与设备驱动息息相关的sysfs文件系统都是怎样挂载到系统的根目录的?
下边我们就通过sysfs文件系统的创建、挂载到自己根目录,以及最后又如何挂载到系统/sys目录的过程做简单分析;其他文件系统的挂载类似,就不再做分析。
二、sysfs文件系统创建和挂载到自己的根目录
跟Linux内核启动之根文件系统挂载分析中一样,我们先从Linux内核启动代码看起:
kernel/init/main.c
asmlinkage void __init start_kernel(void) { setup_arch(&command_line);//解析uboot命令行,实际文件系统挂载需要 parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, &unknown_bootoption); vfs_caches_init(num_physpages); #ifdef CONFIG_PROC_FS //proc文件系统的创建 proc_root_init(); #endif rest_init(); /* static int __init kernel_init(void * unused); do_basic_setup(); //加载内核静态模块 do_initcalls(); mm/shmem.c module_init(init_tmpfs); //dev设备文件系统的创建 register_filesystem(&tmpfs_fs_type); static struct file_system_type tmpfs_fs_type = { .owner = THIS_MODULE, .name = "tmpfs", .get_sb = shmem_get_sb, .kill_sb = kill_litter_super, }; */ }kernel/fs/dcache.c
void __init vfs_caches_init(unsigned long mempages) { mnt_init(); bdev_cache_init(); //块设备文件创建 chrdev_init();//字符设备文件创建 }kernel/fs/namespace.c
void __init mnt_init(void) { err = sysfs_init(); //本节主要分析这一步 init_rootfs(); //向内核注册rootfs init_mount_tree();//重要!!!rootfs根目录的建立以及rootfs文件系统的挂载;设置系统current根目录和根文件系统为rootfs }kernel/fs/sysfs/mount.c
int __init sysfs_init(void) { err = register_filesystem(&sysfs_fs_type);//向内核注册sysfs文件系统 sysfs_mount = kern_mount(&sysfs_fs_type);//将sysfs文件系统挂载到自己的根目录 } static struct file_system_type sysfs_fs_type = { .name = "sysfs", .get_sb = sysfs_get_sb, .kill_sb = kill_anon_super, };
1.sysfs文件系统如何将自己挂载到自己的根目录?
这部分内容其实和Linux内核启动之根文件系统挂载分析中rootfs挂载自己到自己的根目录很类似;过程如下:
kernel/include/linux/fs.h
#define kern_mount(type) kern_mount_data(type, NULL)
kernel/fs/super.c
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data) { return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data); } struct vfsmount * vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data) { struct vfsmount *mnt; mnt = alloc_vfsmnt(name); //建立并填充vfsmount error = type->get_sb(type, flags, name, data, mnt);//为文件系统建立并填充超级块(主要是其dentry和inode),建立sysfs根目录 mnt->mnt_mountpoint = mnt->mnt_root;//文件系统挂载点目录,其实就是刚才建立的”/”目录。挂载点就是自己!!!! mnt->mnt_parent = mnt;////父对象是自己!!!! }
我们还是主要分析下超级块及根目录的建立过程:
kernel/fs/sysfs/mount.c
static int sysfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt); }kernel/fs/super.c
int get_sb_single(struct file_system_type *fs_type, int flags, void *data, int (*fill_super)(struct super_block *, void *, int), struct vfsmount *mnt) { //在内存中分配一个超级块 s = sget(fs_type, compare_single, set_anon_super, NULL); //执行回调函数,填充该超级块,并建立根目录项及对应i节点 error = fill_super(s, data, flags & MS_SILENT ? 1 : 0); //关联超级块(包含目录项dentry和i节点inode)和vfsmount return simple_set_mnt(mnt, s); }kernel/fs/sysfs/mount.c
static int sysfs_fill_super(struct super_block *sb, void *data, int silent) { //i节点 struct inode *inode; //根目录项 struct dentry *root; //超级块 sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = SYSFS_MAGIC; sb->s_op = &sysfs_ops; sb->s_time_gran = 1; sysfs_sb = sb; //创建i节点,这里边就是具体ops的操作;有兴趣可以细看下,在此不再分析 inode = sysfs_get_inode(&sysfs_root); //建立上处i节点的目录项 root = d_alloc_root(inode); //关联超级块和目录项 sb->s_root = root; return 0; }kernel/fs/namespace.c
int simple_set_mnt(struct vfsmount *mnt, struct super_block *sb) { printk("TK-------_>>>>>>>namespace.c>>>>simple_set_mnt\n");//add by tankai mnt->mnt_sb = sb; //对 mnt_sb超级块指针附值 mnt->mnt_root = dget(sb->s_root); //对mnt_root指向的根目录赋值 return 0; }
2.驱动加载过程中就可以在操作如上sysfs,但记住此时sysfs、dev等文件系统并没有和系统current根文件系统和根目录有任何关联、这时用户空间程序是访问不到这些文件系统的。
三、sysfs文件系统挂载到系统current目录树的过程sysfs文件系统挂载到系统current目录树是在用户空间init进程中完成的;如下代码片段:
Android/system/core/init/init.c
int main(int argc, char **argv) { mkdir("/dev", 0755); mkdir("/proc", 0755); mkdir("/sys", 0755); //设备文件系统的挂载 mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755"); mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); mount("devpts", "/dev/pts", "devpts", 0, NULL); //proc文件系统的挂载 mount("proc", "/proc", "proc", 0, NULL); //sysfs文件系统的挂载 mount("sysfs", "/sys", "sysfs", 0, NULL); }在看看init.rc中关于其他文件系统的挂载:
# Backward compatibility symlink /system/etc /etc symlink /sys/kernel/debug /d # Right now vendor lives on the same filesystem as system, # but someday that may change. symlink /system/vendor /vendor # create mountpoints mkdir /mnt 0775 root system mkdir /mnt/sdcard 0000 system system # Create cgroup mount point for cpu accounting mkdir /acct mount cgroup none /acct cpuacct mkdir /acct/uid # Backwards Compat - XXX: Going away in G* symlink /mnt/sdcard /sdcard mkdir /system mkdir /data 0771 system system mkdir /cache 0770 system cache mkdir /config 0500 root root # Directory for putting things only root should see. mkdir /mnt/secure 0700 root root # Directory for staging bindmounts mkdir /mnt/secure/staging 0700 root root # Directory-target for where the secure container # imagefile directory will be bind-mounted mkdir /mnt/secure/asec 0700 root root # Secure container public mount points. mkdir /mnt/asec 0700 root system mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000 # Filesystem image public mount points. mkdir /mnt/obb 0700 root system mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000 write /proc/sys/kernel/panic_on_oops 1 write /proc/sys/kernel/hung_task_timeout_secs 0 write /proc/cpu/alignment 4 write /proc/sys/kernel/sched_latency_ns 10000000 write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000 write /proc/sys/kernel/sched_compat_yield 1 write /proc/sys/kernel/sched_child_runs_first 0 # Create cgroup mount points for process groups mkdir /dev/cpuctl mount cgroup none /dev/cpuctl cpu chown system system /dev/cpuctl chown system system /dev/cpuctl/tasks chmod 0777 /dev/cpuctl/tasks write /dev/cpuctl/cpu.shares 1024 mkdir /dev/cpuctl/fg_boost chown system system /dev/cpuctl/fg_boost/tasks chmod 0777 /dev/cpuctl/fg_boost/tasks write /dev/cpuctl/fg_boost/cpu.shares 1024 mkdir /dev/cpuctl/bg_non_interactive chown system system /dev/cpuctl/bg_non_interactive/tasks chmod 0777 /dev/cpuctl/bg_non_interactive/tasks # 5.0 % write /dev/cpuctl/bg_non_interactive/cpu.shares 52 on fs # mount mtd partitions # Mount /system rw first to give the filesystem a chance to save a checkpoint mount yaffs2 mtd@system /system mount yaffs2 mtd@system /system ro remount mount yaffs2 mtd@userdata /data nosuid nodev mount yaffs2 mtd@cache /cache nosuid nodev on post-fs # once everything is setup, no need to modify / mount rootfs rootfs / ro remount
四、sysfs、rootfs与系统current根文件系统的问题
通过以上分析,结合Linux内核启动之根文件系统挂载分析;有人可能会问为什么不用sysfs替代rootfs功能(也就是直接设置sysfs为系统current的根文件系统),这样、不就可以不需要rootfs了吗?Linux这方面的设计可能是出于安全和效率方面的考虑;不用sysfs作为系统current的根文件系统,而是增加一个rootfs专门完成初始挂载点的创建工作。