构建根文件系统

构建根文件系统

根文件系统的基本概念

在Linux中,是以树状结构管理所有目录、文件,其他分区挂接在某个目录上,这个目录被称为挂接点或者安装点,然后就可以通过这个目录来访问这个分区上的文件了;

在一个分区上存储文件时需要遵循一定的格式,这种格式称为文件系统类型,比如fat16、fat32、ext2、ext3、jffs2、yaffs等,除了这些实实在在的存储分区的文件系统类型外,Linux还有几种虚拟的文件系统类型,比如proc、sysfs等,它们的文件并不存储在实际的设备上,而是在访问它们 时由内核临时生成,比如proc文件系统下的uptime文件,读取它时可以得到两个时间值(用来表示系统启动后运行的秒数、空闲的秒数),每次读取都是由内核实时生成,每次读取到的结果都不一样;

init进程和用户程序启动过程

内核启动的最后一步就是启动init进程,代码在init/main.c文件中,会调用init_post()函数;

init进程是由内核启动的第一个(也是唯一的一个)用户进程(进程ID为1),它根据配置文件决定启动哪些程序,比如执行某些脚本、启动shell或者运行用户指定的程序等;

init进程的执行程序通常是/sbin/init,也可以自己编写/sbin/init程序,或者通过bootloader传入命令行参数"init=xxxxx"指定某个程序作为init进程运行;

内核启动init进程的过程如下:

static int noinline init_post(void)
{
    free_initmem();
    unlock_kernel();
    mark_rodata_ro();
    system_state = SYSTEM_RUNNING;
    numa_default_policy();

    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
        printk(KERN_WARNING "Warning: unable to open an initial console.\n");

    (void) sys_dup(0);
    (void) sys_dup(0);

    if (ramdisk_execute_command) {
        run_init_process(ramdisk_execute_command);
        printk(KERN_WARNING "Failed to execute %s\n",
                ramdisk_execute_command);
    }

    /*
     * We try each of these until one succeeds.
     *
     * The Bourne shell can be used instead of init if we are
     * trying to recover a really broken machine.
     */
    if (execute_command) {
        run_init_process(execute_command);
        printk(KERN_WARNING "Failed to execute %s.  Attempting "
                    "defaults...\n", execute_command);
    }
    run_init_process("/sbin/init");
    run_init_process("/etc/init");
    run_init_process("/bin/init");
    run_init_process("/bin/sh");

    panic("No init found.  Try passing init= option to kernel.");
}
  • 打开标准输入、标准输出和标准错误设备;

  • 如果ramdisk_execute_command变量制定了要运行的程序,则启动它;

    ramdisk_execute_command变量的取值分3种情况:

    1. 如果命令行参数制定了“rdinit=xxxx”,则ramdisk_execute_command等于这个参数指定的程序;
    2. 如果/init程序存在,ramdisk_execute_command就等于"/init";
    3. 否则,ramdisk_execute_command为空;
  • 如果execute_command变量指定了要运行的程序,则启动它;

    execute_command变量的参数就是命令行参数中传入的"init=xxxx",即运行这个参数指定的程序,否则为空;

  • 依次尝试执行:/sbin/init、/etc/init、/bin/init、/bin/sh等;

其中run_init_process函数使用它的参数所指定的程序来创建一个用户进程,需要注意,一旦run_init_process函数创建进程成功,它将不会返回;

Busybox init进程的启动过程

在Linux系统中有两种init程序,分别是BSD init和System V init,这两种init程序各有优缺点,现在绝大多数的Linux的发行版本使用的是System V init,但是在嵌入式领域,通常使用Busybox集成init程序;

init程序的处理包括:

  • 读取配置文件(/etc/inittab);
  • 解析配置文件;
  • 根据配置解析执行用户程序;

如果存在/etc/inittab文件,Busybox init程序解析它,然后按照它的指示创建各种子进程,否则使用默认的配置创建子进程;/etc/inittab文件的相关文档和示例代码可以参考Busybox的examples/inittab文件;

配置编译Busybox

操作步骤:

make menuconfig  /* 执行后会创建一个.config文件 */
make             /* 编译busybox,编译之前确保Makefile种的CROSS_COMPILE已对应修改 */
make install     /* 或者执行“make CONFIG_PREFIX=/path/from/root install” 
                  * 实际执行"make CONFIG_PREFIX=/work/nfs_root/fs_mini install" 前提是                         * 在/work目录下已经创建了/nfs_root/fs_mini目录
                  */

编译,安装后,会在对应路径文件夹下(即/work/nfs_root/fs_mini种)有:/bin、/linuxrc、/sbin、/usr等;

构建根文件系统

构建一个最小的根文件系统需要包含如下部分内容:

  • /dev/console、/dev/null;

    在/dev目录下:
    mknod console c  主设备号  次设备号 /* c 意思是字符设备文件 */        
    mknod null c  主设备号  次设备号

    上述操作属于静态创建各种节点(即设备文件);

    mdev是udev的简化版本,也是通过读取内核信息来创建设备文件;

    mdev的用途主要有两个:

    • 初始化/dev目录;
    • 动态更新(不仅是更新/dev目录,还支持热插拔,即接入、卸下设备时执行某些动作);

    要使用mdev,需要内核支持sysfs文件系统,为了减少对Flash的读写,还要支持tmpfs文件系统,所以先确保内核已经设置了CONFIG_SYSFS和CONFIG_TMPFS配置项;

    要在内核启动时,自动运行mdev,需要修改两个文件:

    • /etc/fstab来自动挂载文件系统;

      #device    mount_point    type    options    dump    fsck order
       proc       /proc         proc     defaults   0         0
       tmpfs      /tmp          tmpfs    defaults   0         0
       sysfs      /sys          sysfs    defaults   0         0
       tmpfs      /dev          tmpfs    defaults   0         0
    • /etc/init.d/rcS加入自动运行的命令;

      mount -a
      mkdir /dev/pts
      mount -t devpts devpts /dev/pts
      echo /sbin/mdev > /proc/sys/kernel/hotplug
      mdev -s

    另外需要注意:mdev是通过init进程来启动的,在使用mdev构造/dev目录之前,init进程至少要用到设备文件/dev/console、/dev/null,所以要手动创建这两个设备文件:

    mkdir -p /nfs_root/mini_rootfs/dev /* 构建的文件系统目录下创建/dev目录 */
    cd /nfs_root/mini_rootfs/dev
    sudo mknod console c 5 1
    sudo mknod null c 1 3
  • init -> busybox(busybox编译后就有);

  • /etc/inittab;

    创建/etc目录,并进入此目录创建配置文件inittab;

    在内核中当前有哪些应用程序在运行,这些信息在内核提供的一个虚拟文件系统proc,可执行命令ps查看;

    当内核启动后挂载了根文件系统后,进入命令行模式,在根目录下创建一个目录/proc,然后执行如下命令:

    mount -t proc none /proc  /* 将虚拟文件系统proc挂接在/proc目录下 */
    ps                        /* 之后执行ps命令后,回去/proc目录下查看有哪些程序 */

    如果不想手动挂载,可以在配置文件中修改,改为自动挂载;

    比如在/etc/inittab配置文件中增加一条脚本:

    ::sysinit:/etc/init.d/rcS

    在/etc/init.d目录下增加rcS文件,内容为:

    mount -t proc none /proc
    chmod +x /etc/init.d/rcS    /* 增加可运行的属性 */

    还可以按照如下做法:

    在/etc/init.d目录下增加rcS文件,文件的内容为:

    mount -a

    然后在/etc目录下新增文件fstab,意思就是当执行了"mount -a"命令后,会读出/etc/fstab文件,根据这个配置文件中的内容来进行挂载文件系统;

    其中,fstab中的内容格式如下:

    #device     mount-point    type     options   dump   fsck order
     proc          /proc       proc     default    0        0  

    格式说明:

    • device 要挂接的设备,对于proc文件系统这个字段没有意义,可以是任意值;
    • mount-point 挂接点;
    • type 文件系统类型,比如proc、jffs2、yaffs等,或者auto自动检测文件类型;
    • options 挂接参数,以逗号隔开;
  • 配置文件指定的程序;

  • C库;

    在制作交叉编译工具链时,已经生成了glibc库(位置在/gcc-3.4.5-glibc-2.3.6/arm-linux/lib,这个目录下并非都属于glibc库,比如ctrl.o、libstdc++.a等文件时GCC工具本身生成的),可以直接使用它来构建根文件系统;

    在开发板上只需要加载器和动态库,假设要构建的根文件系统目录为/nfs_root/mini_rootfs,操作如下:

    mkdir -p /nfs_root/mini_rootfs/lib
    cd /gcc-3.4.5-glibc-2.3.6/arm-linux/lib
    cp *.so* /nfs_root/mini_rootfs/lib -d /* 加上-d作用就是原来是链接文件,拷贝过来仍然是链接文件,                                          否则会将链接指向的文件拷贝过来,导致文件过大 */
  • 构架其他目录

    其他目录可以是空目录,比如proc、mnt、tmp、sys等;

上面构建根文件系统的步骤有些乱,在这总结下:

  • 在Busybox配置、编译、安装之后,在安装目录下会有bin、linuxrc、sbin、usr等;

配置部分:

  1. TAB键自动补全

    Busybox Settings ->
       Busybox Library Tuning ->
           [*] Tab completion
  2. 要使用可加载模块

    Linux Module Utilities ->
       [*] insmod
       [*] rmmod
       [*] lsmod
       [*] modprobe
  • 创建etc/inittab文件

    内容如下:

    #/etc/inittab
    ::sysinit:/etc/init.d/rcS
    console::askfirst:-/bin/sh
    ::ctrlaltdel:/sbin/reboot
    ::shutdown:/bin/umount -a -r
  • 创建etc/init.d/rcS文件

    内容如下:

    #!/bin/sh
    ifconfig eth0 192.168.3.10
    mount -a
    mkdir /dev/pts
    mount -t devpts devpts /dev/pts
    echo /sbin/mdev > /proc/sys/kernel/hotplug
    mdev -s

    注意:要执行chmod +x etc/init.d/rcS,加上可执行权限;

  • 创建etc/fstab文件

    在执行脚本文件rcS中的"mount -a"时会按照etc/fstab中的内容进行挂接;

    内容如下:

    #device    mount_point    type    options    dump    fsck order
     proc       /proc         proc     defaults   0         0
     tmpfs      /tmp          tmpfs    defaults   0         0
     sysfs      /sys          sysfs    defaults   0         0
     tmpfs      /dev          tmpfs    defaults   0         0      
  • 构建dev目录

    mkdir -p dev /* 构建的文件系统目录下创建/dev目录 */
    cd dev
    sudo mknod console c 5 1
    sudo mknod null c 1 3
  • 构建其他目录

    mkdir proc mnt tmp sys 
  • 构建lib目录

    内容如下:

    mkdir -p lib
    cd ./lib
    cp /work/s3c2440/tools/gcc-3.4.5-glibc-2.3.6/arm-linux/lib/*.so* ./ -d
  • 至此最小根文件系统已经基本构建完,可以使用某些工具将根文件系统制作成镜像文件烧写到Flash中,也可以设置NFS挂载;

根文件系统的使用

对于根文件系统,开发板可以将它作为网络根文件系统直接启动或者将它制作为一个文件(映像文件)烧入开发板;

网络文件系统可以使用NFS,需要注意两点:

  1. 确保服务器允许那个目录可被挂载;
  2. 单板启动后去挂载;

具体操作如下:

  • 修改NFS服务的配置文件

    vi /etc/exports
    /* 将需要可被挂载的文件系统目录增加到配置文件中 */
    sudo /etc/init.d/nfs-kernel-server restart /* 重启NFS服务 */
    sudo mount -t nfs 服务器地址:/可被挂载的目录 /mnt /* 可先在电脑上检验下是否可以挂载 */
  • 手动挂载(从Flash上启动根文件系统,再用命令挂接NFS)

    ifconfig eth0 up
    ifconfig eht0 192.168.3.11
    mkdir /mnt
    mount -t nfs -o nolock 服务器地址:根文件系统所在的目录 /mnt
  • 可以直接从NFS启动

    需要设置启动参数,设置格式具体可参考linux内核/Documentation目录下的nfsroot.txt文档;

    具体设置格式如下:

    /* 格式:noinitrd root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf> */
    set bootargs noinitrd root=/dev/nfs nfsroot=192.168.3.5:/work/nfs_root/mini_rootfs ip=192.168.3.10:192.168.3.5:192.168.3.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0

转载于:https://www.cnblogs.com/jasontian996/p/11416109.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值