嵌入式Linux根文件系统制作

2019-07-12 15:12发布


1. 引言

根文件系统作为类unix系统至关重要的一部分,服务于用户层和操作系统,相信很多接触这类操作系统的工程师知道这个概念的存在,并且随时在和它打交道,但相当一部分人对它知之甚少。本文通过目录说明、busybox工具集、初始化脚本、镜像制作等几部分简单说明一下根文件系统的组成及挂载后的系统初始化过程。

实验环境:

主机系统--ubuntu 12.04 busybox版本—1.25.0 arm交叉编译工具链—arm-linux-gnueabi 目标平台--qemu模拟arm平台vexpress-a9开发板

2. linux根文件系统目录概述

linux文件系统中每个目录都有自己的使命,对它们的定义和使用规范来自于FHS(filesystem hierarchy standard),该文档是根文件系统基本指导思想,这里不做过多描述。本文主要是应用方面的说明,我在linux的用户手册里摘抄并翻译了这些目录的描述,部分常见目录的描述如下: / 整个目录树的起点,根目录 /boot 包含bootloader需要的静态文件。该目录只包含在系统引导过程中需要的文件,映射安装工具和配置文件应该在/sbin和/etc目录中。 /dev 特定文件或设备文件,比如物理设备文件 /etc 包含本机的配置文件。比较大的软件包,例如X11,也可以在该目录下创建单独的子目录。 所有的配置文件可能在该目录或是/usr/etc目录。但是程序应该在该目录定位配置文件,你可以链接/usr/etc中的文件到该目录中。 /home 直接或间接的存放不同机器不同用户的家目录。该目录结构取决于本地系统管理员的决定。 /lib 该目录下存放共享库和可加载驱动程序,共享库用于启动系统。运行根文件系统中的可执行程序 /media 该目录包含可移动存储设备的挂载点,例如CD、DVD或USB设备 /mnt 该目录是一个用于临时挂载的文件系统的挂载点,在一些linux发行版中,含有若干子目录,目的是为不同的临时文件系统提供挂载点。 /opt/ 该目录应该存放包含静态文件的软件包。 /proc proc文件系统的挂载点,proc文件系统提供了运行中的进程和内核的信息。 /root root用户的家目录。 /sbin 类似于/bin,包含系统引导所需要的命令,这些命令普通用户不能执行。 /tmp 该目录用于存储临时性的文件,可能是不需要通知用户所删除的文件,常规性处理或系统引导时所产生的文件。 /usr 该目录通常挂载一个单独的分区,他应该只包含可分享的,只读的的数据,这样才能让运行linux的不同机器挂载。 /usr/bin 该目录是可执行程序的主目录。大多数程序用于普通用户执行,这些程序不用于引导系统或是修复系统,而且也不是本地安装的。 /usr/etc 分享到全站的配置文件可能存储在这个目录。然后,所有命令都应该在/etc目录中索引这些配置文件,在/etc/中的链接文件应该指向该目录中的配置文件 /usr/include C编译器所用的头文件 /usr/lib 该目录存放目标库,包括动态库或不直接调用的增强程序。复杂程序可能有单独的子目录。 /usr/local 用于系统管理员安装软件的目录 /usr/local/bin 存储本地的二进制程序 /usr/local/doc 存储本地文档 /usr/local/etc 存储本地安装程序的配置文件 /usr/local/lib 存储本地安装程序的相关库文件 /usr/local/include 本地C编译器的头文件 /usr/local/info 本地安装程序的信息页 /usr/local/man 本地安装程序的用户手册 /usr/local/sbin 系统管理员安装系统程序到这个目录 /usr/local/share 本地应用数据,用于不同架构相同系统共享数据 /usr/sbin 该目录包含用于系统管理的二进制可执行程序,但不是用于引导、修复系统的基础程序 /usr/share 该目录存储特定应用程序数据的子目录,可用于不同架构相同os的机器之前实现分享。 /var 用于存储可能会有大小变化的文件,例如spool或log文件 /var/cache 程序数据缓存 /var/lib 程序的可变的状态信息 /var/lock 用于存储lock文件 /var/log 存储各种各样的log /var/run 运行时可变文件,像存储进程PID或者是记录用户信息的文件 /var/spool 存储不同程序的spooled文件   更详细的说明请参考https://linux.die.net/man/7/hier

3.  创建文件系统必要的目录

创建根目录 mkdir rootfs cd rootfs 创建系统目录 mkdir bin boot  dev  etc lib  mnt  proc root  sbin  sys tmp  usr  var 创建一些常用二级目录 mkdir etc/init.d etc/rc.d mkdir usr/sbin usr/bin usr/lib usr/modules mkdir var/lib var/lock var/run /tmp

4. busybox工具集

busybox下载 wgethttp://www.busybox.Net/downloads/busybox-1.25.0.tar.bz2 –no-check-certificate 编译器安装 sudo apt-get install gcc-arm-linux-gnueabi 环境变量设置 export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabi-   另外还可以使用下面的方法设置,只针对busybox设置,不影响其他的编译环境,在busybox根目录的Makefile中做如下修改: CROSS_COMPILE ?= arm-linux-gnueabi- #ARCH ?= $(SUBARCH) ARCH ?= arm 配置busybox make defconfig 执行make menuconfig定制自己的busybox,然而,可能会遇到的下面的问题:
                        图 4.1 busybox的menuconfig问题   这是你的主机系统没安装过ncurses软件包导致的,该软件包用于终端显示,menuconfig必须用到的,我们这里安装下好了: sudo apt-get install libncurses5-dev libncursesw5-dev 尽管这里说明了可以通过menuconfig定制busybox,但是我们的实验中不需要做任何修改,默认配置已经满足需求。 在配置busybox的同时,有一个重要的部分需要说明,就是根文件系统下/dev目录下设备节点的创建,通过互联万网罗一下常见方法,其中有一篇文章写的比较清楚,基本有三种方法可以胜任这个工作,原文摘抄如下: 1. 手动创建:在制作根文件系统的时候,就在dev目录下创建好要使用的设备文件,系统挂接根文件系统后,就可以使用dev目录下的设备文件了。 2. 使用devfs文件系统:这种方法已经过时,具有不确定的设备映射、没有足够的主/次设备号、devfs消耗大量的内存。 3. udev:它是个用户程序,能根据系统中硬件设备的状态动态的更新设备文件,包括设备文件的创建、删除等。它的操作相对复杂,但灵活性很高。 而busybox支持mdev,mdev 是busybox自带的一个简化版的udev,适合于嵌入式的应用场景。其具有使用简单的特点。它的作用是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程序所需的节点文件。在以busybox为基础构建嵌入式linux的根文件系统时,使用它是最优的选择。 这里选择mdev的方式,busybox默认已经选择了,可以进menuconfig确认下: Linux System Utilities ---> [*] Support /etc/mdev.conf [*] Support subdirs/symlinks [*] Support regular expressions substitutions when renaming device [*] Support command execution at device addition/removal 配置完成后进行编译安装 make make install   由于并没有制定安装目录,所以编译好的文件都安装在busybox-x.xx.x/_install目录下 也可以设置自己的安装路径,通常两种方法: 1. 设置安装前缀 make CONFIG_PREFIX={your path} install 2. 配置menuconfig Busybox Settings ---> Installation Options ("make install"behavior) ---> (./_install)BusyBox installation prefix 安装后顶级目录如下:
                         图 4.2 busybox编译完成后生成的目录和文件 拷贝安装好的文件 然后拷贝Install目录下所有文件到已经建立好的根目录下 cp命令加上-a参数可以copy软链接   copy交叉工具链的lib到已经建立的顶级/lib目录下 cp -a /usr/arm-linux-gnueabi/lib/* lib/

5. 创建必要的初始化文件

inittab—init进程调用 # /etc/inittab # # Copyright (C) 2016-12-06 songxiongwei # # Note: BusyBox init doesn't supportrunlevels. The runlevels field is # completely ignored by BusyBox init. Ifyou want runlevels, use sysvinit. # # Format for each entry:::: # # id == tty to run on, or empty for/dev/console. # If specified, then /dev/$id device mustexist # runlevels == ignored, busybox doesn'tsupport it # action == one of sysinit, respawn,askfirst, wait, and once # process == program to run # Startup the system # mount all the file systems specified in/etc/fstab #挂载fstab指定的文件系统 ::sysinit:/bin/mount -a # now run any rc scripts #执行初始化系统的脚本rcS ::sysinit:/etc/init.d/rcS #Set hostname #设置主机名 ::sysinit:/bin/hostname -F /etc/hostname #Enable console logon #设置登录串口,下面的命令表示需要密码验证 #null::respawn:/sbin/getty -L ttyAMA0 115200 vt100 #也可以通过指定的串口直接登录系统 #ttyAMA0::respawn:-/bin/sh ttyAMA0::askfirst:-/bin/sh #askfirst类似respawn,不过它的主要用途是减少系统上执行的终端应用程序的数量。它将会促使init在控制台上显示“Please press Enter to active this console”的信息,并在重新启动之前等待用户按下enter键。 # system daemon 打开system log和kernel log #null::respawn:/sbin/syslogd -n #null::respawn:/sbin/klogd -n # Stuff to do before rebooting #关闭system log和kernel log #null::shutdown:/bin/killall klogd #null::shutdown:/bin/killall syslogd #卸载所有已挂载的挂载点 null::shutdown:/bin/umount -a -r #null::shutdown:/sbin/swapoff –a inittab脚本也可以是没有的,如果没有busybox init按照下面的步骤进行初始化: ::sysinit:/etc/init.d/rcS ::askfirst:/bin/sh ::ctrlaltdel:/sbin/reboot ::shutdown:/sbin/swapoff -a ::shutdown:/bin/umount -a -r ::restart:/sbin/init tty2::askfirst:/bin/sh tty3::askfirst:/bin/sh tty4::askfirst:/bin/sh
这时候只要把挂载命令写在rcS脚本或是单独写脚本,让rcS调用,两种方法都可以。 /etc/init.d/rcS 服务启动脚本,执行S开头的脚本,可以自定义。本文中设备文件的创建通过/etc/rc.d/S10mdev完成。 #!/bin/sh # Start all init scripts in /etc/init.d # executing them in numerical order. # echo "#####################callrcS#####################" for i in /etc/rc.d/S??* ;do # Ignore dangling symlinks (if any). [ ! -f "$i" ] && continue case "$i" in *.sh) # Sourceshell script for speed. ( trap -INT QUIT TSTP setstart . $i ) ;; *) # No shextension, so fork subprocess. $i start ;; esac done /etc/rc.d/S10mdev 用于创建设备文件,供rcS调用。 #!/bin/sh echo "call S10mdev" if [ ! -x /sbin/mdev ] then exit 0 fi echo "mdev ok" case "$1" in start) echo "/sbin/mdev" >/proc/sys/kernel/hotplug # put /dev in a tmpfs mount -n -o mode=0755 -t tmpfs mdev /dev # Create static device nodes in /dev mknod /dev/console c 5 1 chmod 600 /dev/console mknod /dev/null c 1 3 chmod 666 /dev/null # make and mount devpts mkdir /dev/pts mount -n -t devpts devpts /dev/pts echo "Starting the hotplug eventsdispatcher mdev" /sbin/mdev -s mkdir /dev/shm ;; stop) ;; *) echo "Usage: /etc/rc.d/init.d/mdev{start|stop}" echo exit 1 ;; esac exit 0
/etc/profile  用来设置环境变量等 export PATH= /bin: /sbin: /usr/bin: /usr/sbin: /usr/local/bin # If running interactively, then: if [ "$PS1" ]; then export PS1="[u@h W]\$ " export USER=`id -un` export LOGNAME=$USER export HOSTNAME=`/bin/hostname` export HISTSIZE=1000 export HISTFILESIZE=1000 export PAGER='/bin/more ' export EDITOR='/bin/vi' export INPUTRC=/etc/inputrc exportDMALLOC_OPTIONS=debug=0x34f47d83,inter=100,log=logfile export DISPLAY=:0 export LD_LIBRARY_PATH=/lib:/usr/lib/ ### Some aliases alias ll='ls -l' alias ps2='ps facux ' alias ps1='ps faxo "%U %t %p %a" ' alias af='ps af' alias cls='clear' alias df='df -h' alias indent='indent -bad -bap -bbo -nbc -br-brs -c33 -cd33 -ncdb -ce -ci4 -cli0 -cp33 -cs -d0 -di1 -nfc1 -nfca -hnl -i4-ip0 -l75 -lp -npcs -npsl -nsc -nsob -nss -ts4 ' #alias bc='bc -l' alias minicom='minicom -c on' alias calc='calc -Cd ' alias bc='calc -Cd ' fi; /etc/fstab 文件系统挂载配置,mount-a命令执行的时候读取,可以把这个命令加入到inittab中,也可以自己写脚本,通过rcS调用,为了简便我选择第一种做法,具体做法参考/etc/inittab。 # /etc/fstab: static file systeminformation. # # #/dev/root / ext3 rw,noauto 0 1 proc /proc proc defaults 0 0 #devpts /dev/pts devpts defaults 0 0 sysfs /sys sysfs defaults 0 0 #tmpfs /dev/shm tmpfs mode=0777 0 0 tmpfs /tmp tmpfs mode=1777 0 0 其中proc和sys目录mdev需要,详情请看/etc/rc.d/S10mdev   /etc/protocols 该文件是网络协议定义文件,里面记录了TCP/IP协议族的所有协议类型。文件中的每一行对应一个协议类型,它有3个字段,中间用TAB或空格分隔,分别表示“协议名称”、“协议号”和“协议别名”。 # Internet (IP) protocols # # Updated fromhttp://www.iana.org/assignments/protocol-numbers and other # sources. ip 0 IP # internet protocol, pseudoprotocol number hopopt 0 HOPOPT # IPv6 Hop-by-Hop Option [RFC1883] icmp 1 ICMP # internet control message protocol igmp 2 IGMP # Internet Group Management ggp 3 GGP # gateway-gateway protocol ipencap 4 IP-ENCAP # IP encapsulated in IP (officially``IP'') st 5 ST # ST datagram mode tcp 6 TCP # transmission control protocol egp 8 EGP # exterior gateway protocol igp 9 IGP # any private interior gateway(Cisco) pup 12 PUP # PARC universal packet protocol udp 17 UDP # user datagram protocol hmp 20 HMP # host monitoring protocol xns-idp 22 XNS-IDP # Xerox NS IDP rdp 27 RDP # "reliable datagram"protocol iso-tp4 29 ISO-TP4 # ISO Transport Protocol class 4[RFC905] dccp 33 DCCP # Datagram Congestion Control Prot.[RFC4340] xtp 36 XTP # Xpress Transfer Protocol ddp 37 DDP # Datagram Delivery Protocol idpr-cmtp 38 IDPR-CMTP # IDPR Control Message Transport ipv6 41 IPv6 # Internet Protocol, version 6 ipv6-route 43 IPv6-Route # Routing Header for IPv6 ipv6-frag 44 IPv6-Frag # Fragment Header for IPv6 idrp 45 IDRP # Inter-Domain Routing Protocol rsvp 46 RSVP # Reservation Protocol gre 47 GRE # General Routing Encapsulation esp 50 IPSEC-ESP # Encap Security Payload [RFC2406] ah 51 IPSEC-AH # Authentication Header [RFC2402] skip 57 SKIP # SKIP ipv6-icmp 58 IPv6-ICMP # ICMP for IPv6 ipv6-nonxt 59 IPv6-NoNxt # No Next Header for IPv6 ipv6-opts 60 IPv6-Opts # Destination Options for IPv6 rspf 73 RSPF CPHB # Radio Shortest Path First (officiallyCPHB) vmtp 81 VMTP # Versatile Message Transport eigrp 88 EIGRP # Enhanced Interior Routing Protocol(Cisco) ospf 89 OSPFIGP # Open Shortest Path First IGP ax.25 93 AX.25 # AX.25 frames ipip 94 IPIP # IP-within-IP EncapsulationProtocol etherip 97 ETHERIP # Ethernet-within-IP Encapsulation[RFC3378] encap 98 ENCAP # Yet Another IP encapsulation[RFC1241] # 99 # any private encryption scheme pim 103 PIM # Protocol Independent Multicast ipcomp 108 IPCOMP # IP Payload Compression Protocol vrrp 112 VRRP # Virtual Router RedundancyProtocol [RFC5798] l2tp 115 L2TP # Layer Two Tunneling Protocol[RFC2661] isis 124 ISIS # IS-IS over IPv4 sctp 132 SCTP # Stream Control TransmissionProtocol fc 133 FC # Fibre Channel mobility-header 135 Mobility-Header #Mobility Support for IPv6 [RFC3775] udplite 136 UDPLite # UDP-Lite [RFC3828] mpls-in-ip 137 MPLS-in-IP # MPLS-in-IP [RFC4023] manet 138 # MANETProtocols [RFC5498] hip 139 HIP # Host Identity Protocol shim6 140 Shim6 # Shim6 Protocol [RFC5533] wesp 141 WESP # Wrapped Encapsulating SecurityPayload rohc 142 ROHC # Robust Header Compression /etc/mdev.conf用于动态创建设备文件的配置文件 # system all-writable devices full 0:0 0666 null 0:0 0666 ptmx 0:0 0666 random 0:0 0666 tty 0:0 0666 zero 0:0 0666 # console devices tty[0-9]* 0:5 0660 ttyS[0-9]* root:root 660 # loop devices loop[0-9]* 0:0 0660 =loop/ # i2c devices i2c-0 0:0 0666 =i2c/0 i2c-1 0:0 0666 =i2c/1 # frame buffer devices fb[0-9] 0:0 0666 # input devices mice 0:0 0660 =input/ mouse.* 0:0 0660 =input/ event.* 0:0 0660 =input/ ts.* 0:0 0660 =input/ # rtc devices rtc0 0:0 0644 >rtc rtc[1-9] 0:0 0644 # misc devices mmcblk0p1 0:0 0600 =sdcard */bin/hotplug.sh sda1 0:0 0600 =udisk * /bin/hotplug.sh  /etc/hostname文件 myroot /etc/hosts文件 127.0.0.1 localhost /etc/group 内容格式: 组名:口令:组标识号:组内用户列表 (1)“组名”是用户组的名称,由字母或数字构成。与/etc/passwd中的登录名一样,组名不应重复。 (2)“口令”字段存放的是用户组加密后的口令字。一般Linux系统的用户组都没有口令,即这个字段一般为空,或者是*。 (3)“组标识号”与用户标识号类似,也是一个整数,被系统内部用来标识组。 (4)“组内用户列表”是属于这个组的所有用户的列表/b],不同用户之间用逗号(,)分隔。这个用户组可能是用户的主组,也可能是附加组。 root::0:root bin::1:root,bin,daemon daemon::2:root,bin,daemon sys::3:root,bin,adm adm::4:root,adm,daemon tty::5: disk::6:root lp::7:daemon,lp mem::8: kmem::9: wheel::10:root mail::12:mail news::13:news uucp::14:uucp man::15: games::20: gopher::30: dip::40: ftp::50: lock::54: nobody::99: users::100: slocate:x:21: tape:x:16: audio:x:17: video:x:18: cdrom:x:25: floppy:x:19: utmp:x:22: mailnull:x:47: xfs:x:43: ntp:x:38: rpc:x:32: gdm:x:42: rpcuser:x:29: nfsnobody:x:65534: nscd:x:28: ident:x:98: radvd:x:75: postgres:x:26: apache:x:48:seh squid:x:23: named:x:70: pcap:x:77: junkbust:x:73: pppusers:x:44: popusers:x:45: slipusers:x:46: mailman:x:41: mysql:x:27: ldap:x:55: pvm:x:24: nogroup:x:65534: user:x:500: messagebus:x:1000: haldaemon:x:1001: /etc/passwd 该文件存放的是用户账户信息,由6个分号组成的7个信息,解释如下: (1) 用户名。 (2) 密码(已经加密) (3) UID(用户标识),操作系统自己用的 (4) GID组标识。 (5) 用户全名或本地帐号 (6) 开始目录 (7) 登录使用的Shell,就是对登录命令进行解析的工具。 root:x:0:0:root:/root:/bin/sh daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:100:sync:/bin:/bin/sync mail:x:8:8:mail:/var/spool/mail:/bin/sh proxy:x:13:13:proxy:/bin:/bin/sh www-data:x:33:33:www-data:/var/www:/bin/sh backup:x:34:34:backup:/var/backups:/bin/sh operator:x:37:37:Operator:/var:/bin/sh haldaemon:x:68:68:hald:/:/bin/sh dbus:x:81:81:dbus:/var/run/dbus:/bin/sh ftp:x:83:83:ftp:/home/ftp:/bin/sh nobody:x:99:99:nobody:/home:/bin/sh sshd:x:103:99:Operator:/var:/bin/sh default:x:1000:1000:Default non-rootuser:/home/default:/bin/sh
/etc/shadow 用超级权限建立,解释如下: (1) 帐号名称 (2) 密码:这里是加密过的,但高手也可以解密的。要主要安全问题(代!符号标识该帐号不能用 来登录) (3) 上次修改密码的日期 (4) 密码不可被变更的天数 (5) 密码需要被重新变更的天数(99999表示不需要变更) (6) 密码变更前提前几天警告 (7) 帐号失效日期 (8) 帐号取消日期 (9) 保留条目,目前没用 root::10933:0:99999:7::: bin:*:10933:0:99999:7::: daemon:*:10933:0:99999:7::: adm:*:10933:0:99999:7::: lp:*:10933:0:99999:7::: sync:*:10933:0:99999:7::: shutdown:*:10933:0:99999:7::: halt:*:10933:0:99999:7::: uucp:*:10933:0:99999:7::: operator:*:10933:0:99999:7::: ftp:*:10933:0:99999:7::: nobody:*:10933:0:99999:7::: default::10933:0:99999:7:::

6. 镜像制作

上面所介绍的目录和配置文件,基本可以保证根文件系统的正常运行,是时候制作自己的镜像了,具体方法如下: 生成镜像 dd if=/dev/zero of=rootfs.ext3 bs=1Mcount=128 格式化成ext3格式 mkfs.ext3 rootfs.ext3 创建临时挂载点并挂载 mkdir tmpfs mount -t ext3 rootfs.ext3 tmpfs/ -o loop copy已经准备好的目录和文件 cp -r rootfs/*  tmpfs/ 卸载 sudo umount tmpfs 最后,把制作好的根文件系统镜像在qemu上实验一把:
                         图 6.1 系统初始化完成之后,登录串口状态 当整个系统正常运行起来,你的成就感是否随之而来了呢?更理性的问题是,你对linux系统的初始化基本流程是否掌握了呢?如果让你在系统启动过程中加入其他配置,比如以太网的初始化,应该怎么做?
参考资料: 文件系统的制作流程 http://blog.chinaunix.net/uid-26524139-id-3051743.html http://www.cnblogs.com/thinkinglife/p/3931497.html FHS的规范文档 http://refspecs.linuxfoundation.org/fhs.shtml linux系统目录说明 https://linux.die.net/man/7/hier 对busybox初始化调用inittab的说明 http://blog.csdn.net/lqrensn/article/details/5955740 mdev的用法来自busybox文档的中文翻译 http://www.cnblogs.com/hnrainll/archive/2011/06/25/2090182.html /etc/group /etc/passwd /etc/shadow文件格式说明 http://www.cnblogs.com/suger/p/3831905.html
扩展阅读: linux根文件系统挂载过程 http://www.cnblogs.com/armlinux/archive/2011/03/30/2396825.html fstab的介绍 https://wiki.archlinux.org/index.php/Fstab_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87) 有关fstab配置的介绍也可参考mount的man手册,手册中也有对mtab和/proc/mounts的说明