嵌入式linux学习笔录--根文件系统制作

2019-07-13 00:31发布

         制作最基本的根文件系统比较简单,对照着国嵌视频一步步来即可,但是如果移植tslib和嵌入式qt到文件系统,就会出现许许多多的问题。由于对shell脚本一窍不通,因此笔者在制作文件系统的时候遇到了很多麻烦,下面记录下整个过程,以待后用。

1、在你工作目录下创建文件,然后在rootfs里面创建根文件系统主要目录,命令如下:
#mkdir rootfs
#cd rootfs
#mkdir bin dev etc lib proc sbin sys usr mnt tmp var
#mkdir usr/bin usr/lib usr/sbin lib/modules
2、创建设备文件
#cd rootfs/dev
#mknod -m 666 console c 5 1
3、加入配置文件
#tar etc.tar.gz
#mv etc/* …/root/etc/ -rf
4、添加内核模块
#cd …/linux
#make modules ARCH=arm CROSS_COMPILE=arm-linux-
#make modules_install ARCH=arm INSTALL_MOD_PATH=…/rootfs
5、编译/安装busybox
(1).配置busybox
#make menuconfig
Busybox Settings ->build Options->
选中“Build busybox as a static binary”, 静态链接
Cross Compiler prefix (arm-linux-)

Installation Options->
选中“Don‘t use /usr”, 选中该项可以避免busybox 被安装到宿主系统的
/usr目录下,破坏宿主系统

选上Busybox Settings ->Busybox Library Tuning-->[*]Username completion   [*]Fancy shell prompts
否则登陆后提示信息显示不正常,如显示[u@h W]#,然后还需要在/etc/下建立passwd文件,内容为:root:x:0:0:root:/root:/bin/sh

Busybox Installation Prefix (/xxx/rootfs)
该选项表明编译后的busybox的安装位置
(2).编译、安装busybox
make
make install

至此,最基本的根文件系统制作完成,用nfs挂载看能否正常运行。
-----------------------------------------------------------------------

下面移植tslib到根文件系统
1、编译
2、将编译生成的整个tslib文件(默认在/usr/local/下)拷贝到根文件系统的/opt目录下
3、将文件系统的/opt/tslib/bin/ts_calibrate校正程序拷贝到文件系统的/usr/bin目录下

注意:将系统/opt/FriendlyARM/toolschain/4.4.3/arm-none-linux-gnueabi/lib目录下的所有库文件拷贝到文件系统的/lib目录下,否则会出现找不到对应可执行文件的错误。

------------------------------------------------------------------------------

系统启动过程分析:(借鉴root_qtopia启动过程)

由于默认的内核命令行上有init=/linuxrc, 因此,在文件系统被挂载后,运行的第一个程序是根目录下的linuxrc。这是一个指向/bin/busybox的链接,也就是说,系统起来后运行的第一个程序也就是busybox本身。这种情况下,busybox首先将试图解析/etc/inittab来获取进一步的初始化配置信息(参考busybox源代码init/init.c中的parse_inittab()函数)。而事实上,文件系统中并没有/etc/inittab这个配置文件,根据busybox的逻辑,它将生成默认的配置(部分): 
static void parse_inittab(void) 

#if ENABLE_FEATURE_USE_INITTAB 
       char *token[4]; 
        parser_t *parser = config_open2("/etc/inittab", fopen_for_read); 
  
        if (parser == NULL) 
#endif 
        { 
                /* No inittab file - set up some default behavior */ 
                /* Reboot on Ctrl-Alt-Del */ 
                new_init_action(CTRLALTDEL, "reboot", ""); 
                /* Umount all filesystems on halt/reboot */ 
                new_init_action(SHUTDOWN, "umount -a -r", ""); 
                /* Swapoff on halt/reboot */ 
                if (ENABLE_SWAPONOFF) 
                        new_init_action(SHUTDOWN, "swapoff -a", ""); 
                /* Prepare to restart init when a QUIT is received */ 
                new_init_action(RESTART, "init", ""); 
                /* Askfirst shell on tty1-4 */ 
                new_init_action(ASKFIRST, bb_default_login_shell, ""); 
//TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users 
                new_init_action(ASKFIRST, bb_default_login_shell, VC_2); 
                new_init_action(ASKFIRST, bb_default_login_shell, VC_3); 
                new_init_action(ASKFIRST, bb_default_login_shell, VC_4); 
                /* sysinit */ 
                new_init_action(SYSINIT, INIT_SCRIPT, ""); 
                return; 
        } 
…… 
其中, 最重要的一个,就是new_init_action(SYSINIT, INIT_SCRIPT, ""), 也就决定了接下去初始化的脚本是INIT_SCRIPT所定义的值。这个宏的默认值是"/etc/init.d/rcS"。 
下面是文件系统中/etc/init.d/rcS的内容, 也是我们要分析的重点 


#! /bin/sh 
  
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin: 
runlevel=S 
prevlevel=N 
umask 022 
export PATH runlevel prevlevel 
  

#       Trap CTRL-C &c only in this shell so we can interrupt subprocesses. 

trap ":" INT QUIT TSTP 
/bin/hostname FriendlyARM 
  
/bin/mount -n -t proc none /proc 
/bin/mount -n -t sysfs none /sys 
/bin/mount -n -t usbfs none /proc/bus/usb 
/bin/mount -t ramfs none /dev 
  
echo /sbin/mdev > /proc/sys/kernel/hotplug 
/sbin/mdev -s 
/bin/hotplug 
  
# mounting file system specified in /etc/fstab 
mkdir -p /dev/pts 
mkdir -p /dev/shm 
/bin/mount -n -t devpts none /dev/pts -o mode=0622 
/bin/mount -n -t tmpfs tmpfs /dev/shm 
/bin/mount -n -t ramfs none /tmp 
/bin/mount -n -t ramfs none /var 
mkdir -p /var/empty 
mkdir -p /var/log 
mkdir -p /var/lock 
mkdir -p /var/run 
mkdir -p /var/tmp 
  
/sbin/hwclock -s    //配置RTC时钟 
/sbin/ifconfig lo 127.0.0.1  //配置回环网卡

/bin/qt4 & 
echo "                                  " > /dev/tty1 
echo "Starting Qt4, please waiting..." > /dev/tty1 
下面就逐个来分析: 
1.          启动环境设置必要的环境变量; 
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin: runlevel=S prevlevel=N umask 022 export PATH runlevel prevlevel 
2.          设置机器名字; 
/bin/hostname FriendlyARM 
3.          挂载“虚拟”文件系统, 
/bin/mount -n -t proc none /proc 
/bin/mount -n -t sysfs none /sys 
/bin/mount -n -t usbfs none /proc/bus/usb 
/bin/mount -t ramfs none /dev 
这里是挂载/proc, /sys,并且在/dev目录上挂载一个ramfs,相当于把原本NAND Flash上的只读的/dev目录“覆盖”上一块可写的空的SDRAM。 
这里要注意的是,/sys和挂载了ramfs的/dev是正确创建设备节点的关键。对于2.6.29内核来说,已经没有了devfs的支持,创建设备节点只有通过两种办法由文件系统完成: 
1) 制作文件系统镜像前用mknod手动创建好系统中所有的(包括可能有的)设备节点,并把这些节点文件一起做进文件系统镜像中; 
2)在文件系统初始化过程中,通过/sys目录所输出的信息,在/dev目录下动态的创建系统中当前实际有的设备节点。 
显然,方法1)有很大的局限性,仅限于没有设备动态增加或减少的情况,不适用于很多设备热插拔的情况,比如U盘,SD卡等等。方法2)是目前大多数PC上Linux的做法(基于udev实现)。这种方法有两个前提:/sys目录挂载和一个可写的/dev目录。 
这也就是为什么我们在这里需要挂载/sys和ramfs在/dev目录上。事实上,这种方法最早就是为热插拔设计的,你可以理解为当系统启动是,所有设备一下子全部“插入”了进来。 
这里有一点要说明的是,在文件系统初始化跑到这里之前,原本的/dev目录下必须有一个的设备节点:/dev/console。 
4.          支持热插拔 
echo /sbin/mdev > /proc/sys/kernel/hotplug 
/sbin/mdev –s 
/bin/hotplug 
这几个就是用来完成我上面所说的两个东西: 
1)通过mdev -s 在/dev目录下建立必要的设备节点; 
2)设置内核的hotplug handler为mdev, 即当设备热插拔时,由mdev接收来自内核的消息并作出相应的回应, 比如挂载U盘。 
对于mdev,需要注意的是,文件系统里存在/etc/mdev.conf文件,它包含了mdev的配置信息。通过这个文件,我们可以自定义一些设备节点的名称或链接来满足特定的需要。这是root qtopia中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    
vc/[0-9]*       0:5     0660    
  
# serial port devices 
s3c2410_serial0 0:5     0666    =ttySAC0 
s3c2410_serial1 0:5     0666    =ttySAC1 
s3c2410_serial2 0:5     0666    =ttySAC2 
s3c2410_serial3 0:5     0666    =ttySAC3 
  
# 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 
可以看到,原本串口驱动注册的设备名是s3c2410_serial0,s3c2410_serial1和s3c2410_serial2,而mdev则会在/dev目录下对应生成ttySAC0,ttySAC1和ttySAC2以符合应用程序对于串口设备名称的习惯。 
同样的,/dev/sdcard和/dev/udisk永远分别指向SD卡和U盘的第一个分区。(所以,用那些没有分区表的SD卡或U盘的兄弟知道原因了吧...) 
5.          挂载一些其它的常用文件系统 
# mounting file system specified in /etc/fstab 
mkdir -p /dev/pts 
mkdir -p /dev/shm 
/bin/mount -n -t devpts none /dev/pts -o mode=0622 
/bin/mount -n -t tmpfs tmpfs /dev/shm 
/bin/mount -n -t ramfs none /tmp 
/bin/mount -n -t ramfs none /var 
mkdir -p /var/empty 
mkdir -p /var/log 
mkdir -p /var/lock 
mkdir -p /var/run 
mkdir -p /var/tmp 
就像注释中所说的,这是用来挂载其他一些常用的文件系统,并在/var目录下(同样是ramfs,可写的)新建必要的目录。 
6.          设定系统时间 
/sbin/hwclock -s 
从硬件RTC中获取,不过似乎有问题接下来就是启动系统服务了,包括log记录,网络, http server和自定义的"跑马灯服务"... 
【关于mdev.conf的补充说明】 
# misc devices 
mmcblk0p1 0:0 0600 =sdcard */bin/hotplug 
sda1 0:0 0600 =udisk * /bin/hotplug 
这两句配置的意思是当SD卡或者U盘插入/拔出时,将这个消息传递给自定义的热插拔handler, /bin/hotplug. 
这个程序是友善之臂开发的用于自动挂载可移动设备的,目前是SD卡和U盘。它的逻辑很简单,将SD卡或者U盘的第一个分区作为FAT/FAT32挂载到/sdcard或者/udisk。
但这也同时带来一个问题,当SD卡或者U盘上没有分区表或者第一个分区不是FAT/FAT32格式的时候,它就玩不转了,兄弟们要小心了:) 


/bin/qt4 & 
echo " " > /dev/tty1 
echo "Starting Qt4, please waiting..." > /dev/tty1 
可以看到,这里Qtopia是通过运行/bin/qt4来启动的。事实上,/bin/qt4也是一个脚本,它的任务是设定Qt运行必要的环境。脚本如下:


#!/bin/sh 
export TSLIB=/opt/tslib  
export TSLIB_TSDEVICE=/dev/input/event0 
export TSLIB_CONFFILE=/usr/local/etc/ts.conf 
export TSLIB_PLUGINDIR=$TSLIB/lib/ts 
export TSLIB_CALIBFILE=/etc/pointercal 
export LD_LIBRARY_PATH=$TSLIB/lib:$LD_LIBRARY_PATH 
  
  
TS_INFO_FILE=/sys/devices/virtual/input/input0/uevent 
if [ -e $TS_INFO_FILE -a "/bin/grep -q TouchScreen < $TS_INFO_FILE" ]; then 
        export QWS_MOUSE_PROTO="TPanel:/dev/input/event0 USB:/dev/input/mice" 
        if [ -e /etc/pointercal -a ! -s /etc/pointercal ] ; then 
                rm /etc/pointercal 
        fi      
else 
        export QWS_MOUSE_PROTO="USB:/dev/input/mice" 
        >/etc/pointercal 
fi 
unset TS_INFO_FILE 
  
export QWS_KEYBOARD=TTY:/dev/tty1 
  
export HOME=/root 
/usr/bin/ts_calibarte


#!/bin/sh 
#(1)tslib环境变量设置,包括了touchscreen设备文件,tslib配置文件,tslib plug-in位置和touchscreen校准数据文件 
export TSLIB_TSDEVICE=/dev/input/event0 
export TSLIB_CONFFILE=/usr/local/etc/ts.conf 
export TSLIB_PLUGINDIR=$TSLIB/lib/ts  
export TSLIB_CALIBFILE=/etc/pointercal 


#(2)设定LD_LIBRARY_PATHA的tslib共享库文件。
export LD_LIBRARY_PATH=$TSLIB/lib:$LD_LIBRARY_PATH 
#(3)通过判断/sys/devices/virtual/input/input0/uevent中是否包含touchscreen信息使Qtopia自动识别touchscreen和USB鼠标 
TS_INFO_FILE=/sys/devices/virtual/input/input0/uevent 
if [ -e $TS_INFO_FILE -a "/bin/grep -q TouchScreen < $TS_INFO_FILE" ]; then 
export QWS_MOUSE_PROTO="TPanel:/dev/input/event0 USB:/dev/input/mice" 
if [ -e /etc/pointercal -a ! -s /etc/pointercal ] ; then 
rm /etc/pointercal 
fi 
else 
export QWS_MOUSE_PROTO="USB:/dev/input/mice" >/etc/pointercal 
fi 
unset TS_INFO_FILE 
export QWS_KEYBOARD=TTY:/dev/tty1export KDEDIR=/opt/kde 
export HOME=/root 
-----------------------------------------------------------------------------------------------

针对我的文件系统,启动时依次调用的脚本顺序如下:
系统启动-->linuxrc(指向busybox链接)-->/etc/init.d/rcS-->设置一些环境变量,执行/bin/qt4脚本-->执行/bin/ts-devices脚本,配置环境变量-->运行校正程序/usr/bin/ts_calibrate-->启动结束

几点注意:
1、环境变量要设置正确
2、根文件系统的linuxrc是一个指向busybox的链接,不能通过拷贝获得,只能使用命令ln -s /bin/busybox linuxrc创建,否则会出现运行busybox时缺少参数的问题。
3、/sbin目录下的mdev也是一个指向busybox的链接文件,也只能通过命令获得。
4、至于在运行触摸校正程序的时,出现触摸无反应的问题,至今还没弄明白,使用root_qtopia里面的一些配置文件解决了这个困扰我多天的问题,但是具体问题在哪还未能搞清楚。