说明:本分析基于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专门完成初始挂载点的创建工作。



Logo

更多推荐