说明:本分析基于Linux2.6内核和Android2.3版本,其他版本仅供参考。
一、前言
从前边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专门完成初始挂载点的创建工作。