MINI2440啟動配置文件/etc/init.d/rcS文件分析
對于mini2440,雖然root_qtopia這個文件系統(tǒng)的GUI是基于Qtopia的,但其初始化啟動過程卻是由大部分由busybox完成,Qtopia(qpe)只是在啟動的最后階段被開啟。
由于默認的內(nèi)核命令行上有init=/linuxrc, 因此,在文件系統(tǒng)被掛載后,運行的第一個程序是根目錄下的linuxrc。這是一個指向/bin/busybox的鏈接,也就是說,系統(tǒng)起來后運行的第一個程序也就是busybox本身。這種情況下,busybox首先將試圖解析/etc/inittab來獲取進一步的初始化配置信息(參考busybox源代碼init/init.c中的parse_inittab()函數(shù))。而事實上,root_qtopia中并沒有/etc/inittab這個配置文件,根據(jù)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"。
下面是文件系統(tǒng)中/etc/init.d/rcS的內(nèi)容, 也是我們要分析的重點
#! /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
syslogd
/etc/rc.d/init.d/netd start
echo " " > /dev/tty1
echo "Starting networking..." > /dev/tty1
sleep 1
/etc/rc.d/init.d/httpd start
echo " " > /dev/tty1
echo "Starting web server..." > /dev/tty1
sleep 1
/etc/rc.d/init.d/leds start
echo " " > /dev/tty1
echo "Starting leds service..." > /dev/tty1
echo " "
sleep 1
/sbin/ifconfig lo 127.0.0.1
/etc/init.d/ifconfig-eth0
/bin/qtopia &
echo " " > /dev/tty1
echo "Starting Qtopia, please waiting..." > /dev/tty1
下面就逐個來分析:
1. 啟動環(huán)境設(shè)置必要的環(huán)境變量;
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin: runlevel=S prevlevel=N umask 022 export PATH runlevel prevlevel
2. 設(shè)置機器名字;
/bin/hostname FriendlyARM
3. 掛載“虛擬”文件系統(tǒng),
/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是正確創(chuàng)建設(shè)備節(jié)點的關(guān)鍵。對于2.6.29內(nèi)核來說,已經(jīng)沒有了devfs的支持,創(chuàng)建設(shè)備節(jié)點只有通過兩種辦法由文件系統(tǒng)完成:
1) 制作文件系統(tǒng)鏡像前用mknod手動創(chuàng)建好系統(tǒng)中所有的(包括可能有的)設(shè)備節(jié)點,并把這些節(jié)點文件一起做進文件系統(tǒng)鏡像中;
2)在文件系統(tǒng)初始化過程中,通過/sys目錄所輸出的信息,在/dev目錄下動態(tài)的創(chuàng)建系統(tǒng)中當前實際有的設(shè)備節(jié)點。
顯然,方法1)有很大的局限性,僅限于沒有設(shè)備動態(tài)增加或減少的情況,不適用于很多設(shè)備熱插拔的情況,比如U盤,SD卡等等。方法2)是目前大多數(shù)PC上Linux的做法(基于udev實現(xiàn))。這種方法有兩個前提:/sys目錄掛載和一個可寫的/dev目錄。
這也就是為什么我們在這里需要掛載/sys和ramfs在/dev目錄上。事實上,這種方法最早就是為熱插拔設(shè)計的,你可以理解為當系統(tǒng)啟動是,所有設(shè)備一下子全部“插入”了進來。
這里有一點要說明的是,在文件系統(tǒng)初始化跑到這里之前,原本的/dev目錄下必須有一個的設(shè)備節(jié)點:/dev/console。
4. 支持熱插拔
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev –s
/bin/hotplug
這幾個就是用來完成我上面所說的兩個東西:
1)通過mdev -s 在/dev目錄下建立必要的設(shè)備節(jié)點;
2)設(shè)置內(nèi)核的hotplug handler為mdev, 即當設(shè)備熱插拔時,由mdev接收來自內(nèi)核的消息并作出相應(yīng)的回應(yīng), 比如掛載U盤。
對于mdev,需要注意的是,文件系統(tǒng)里存在/etc/mdev.conf文件,它包含了mdev的配置信息。通過這個文件,我們可以自定義一些設(shè)備節(jié)點的名稱或鏈接來滿足特定的需要。這是root qtopia中mdev.conf的內(nèi)容:
# 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
可以看到,原本串口驅(qū)動注冊的設(shè)備名是s3c2410_serial0,s3c2410_serial1和s3c2410_serial2,而mdev則會在/dev目錄下對應(yīng)生成ttySAC0,ttySAC1和ttySAC2以符合應(yīng)用程序?qū)τ?strong>串口設(shè)備名稱的習慣。
同樣的,/dev/sdcard和/dev/udisk永遠分別指向SD卡和U盤的第一個分區(qū)。(所以,用那些沒有分區(qū)表的SD卡或U盤的兄弟知道原因了吧...)
5. 掛載一些其它的常用文件系統(tǒng)
# 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
就像注釋中所說的,這是用來掛載其他一些常用的文件系統(tǒng),并在/var目錄下(同樣是ramfs,可寫的)新建必要的目錄。
6. 設(shè)定系統(tǒng)時間
/sbin/hwclock -s
從硬件RTC中獲取,不過似乎有問題接下來就是啟動系統(tǒng)服務(wù)了,包括log記錄,網(wǎng)絡(luò), http server和自定義的"跑馬燈服務(wù)"...
【關(guān)于mdev.conf的補充說明】
# misc devices
mmcblk0p1 0:0 0600 =sdcard */bin/hotplug
sda1 0:0 0600 =udisk * /bin/hotplug
這兩句配置的意思是當SD卡或者U盤插入/拔出時,將這個消息傳遞給自定義的熱插拔handler, /bin/hotplug.
這個程序是友善之臂開發(fā)的用于自動掛載可移動設(shè)備的,目前是SD卡和U盤。它的邏輯很簡單,將SD卡或者U盤的第一個分區(qū)作為FAT/FAT32掛載到/sdcard或者/udisk。
但這也同時帶來一個問題,當SD卡或者U盤上沒有分區(qū)表或者第一個分區(qū)不是FAT/FAT32格式的時候,它就玩不轉(zhuǎn)了,兄弟們要小心了:)
7. 啟動一系列服務(wù):
Syslogd
/etc/rc.d/init.d/netd start
echo " " > /dev/tty1
echo "Starting networking..." > /dev/tty1
sleep 1
/etc/rc.d/init.d/httpd start
echo " " > /dev/tty1
echo "Starting web server..." > /dev/tty1
sleep 1
/etc/rc.d/init.d/leds start
echo " " > /dev/tty1
echo "Starting leds service..." > /dev/tty1
echo " "
sleep 1
nsyslog - 用于記錄內(nèi)核和應(yīng)用程序debug信息
nnetd - inetd, 一個掛載啟動各種網(wǎng)絡(luò)相關(guān)服務(wù)的看守進程
nhttpd - http server看守進程
nleds- 跑馬燈看守進程
其中,inetd的配置文件為/etc/inetd.conf,這是文件內(nèi)容:
# /etc/inetd.conf: see inetd(8) for further informations.
echo stream tcp nowait root internal
echo dgram udp wait root internal
daytime stream tcp nowait root internal
daytime dgram udp wait root internal
time stream tcp nowait root internal
time dgram udp wait root internal
# These are standard services.
#
ftp stream tcp nowait root /usr/sbin/ftpd /usr/sbin/ftpd
telnet stream tcp nowait root /usr/sbin/telnetd /usr/sbin/telnetd -i
可以看到,這里啟動的網(wǎng)絡(luò)服務(wù)有兩個:
1)ftp server 和
2)telnet server。
有關(guān)網(wǎng)絡(luò)服務(wù)的端口和協(xié)議等具體信息,可以參考/etc/services, /etc/protocol。
8. 配置網(wǎng)絡(luò)設(shè)備(網(wǎng)卡)
/sbin/ifconfig lo 127.0.0.1/etc/init.d/ifconfig-eth0:
1)設(shè)定本機回環(huán)地址為127.0.0.1
2)運行網(wǎng)卡設(shè)置腳本/etc/init.d/ifconfig-eth0
這是/etc/init.d/ifconfig-eth0的內(nèi)容, 加入了我的一些注釋
#!/bin/sh
echo -n Try to bring eth0 interface up......>/dev/ttySAC0
#判斷/etc/eth0-setting文件是否存在
if [ -f /etc/eth0-setting ] ; then
#讀取配置文件信息
source /etc/eth0-setting
#如果根文件系統(tǒng)為nfs,則說明網(wǎng)卡已經(jīng)配置OK,這里什么都不需要配置了
if grep -q "^/dev/root / nfs " /etc/mtab ; then echo -n NFS root ... > /dev/ttySAC0
#否則,根據(jù)配置文件中的MAC, IP, Mask和Gateway通過ifconfig命令相應(yīng)地配置網(wǎng)卡
else
ifconfig eth0 down
ifconfig eth0 hw ether $MAC
ifconfig eth0 $IP netmask $Mask up
route add default gw $Gateway fi
#將配置文件中的DNS設(shè)置寫入/etc/resolv.conf使之生效
echo nameserver $DNS > /etc/resolv.conf
#配置文件不存在,使用默認配置else
#如果根文件系統(tǒng)為nfs,則說明網(wǎng)卡已經(jīng)配置OK,這里什么都不需要配置了
if grep -q "^/dev/root / nfs " /etc/mtab ; then
echo -n NFS root ... > /dev/ttySAC0
else
#將網(wǎng)卡的IP地址設(shè)定為192.168.1.230
/sbin/ifconfig eth0 192.168.1.230 netmask 255.255.255.0 up
fi
fi
echo Done > /dev/ttySAC0
可以看到,NFS自動識別就是靠判斷/etc/mtab中是否有nfs的掛載記錄實現(xiàn)的。
這是root qtopia文件系統(tǒng)中/etc/eth0-settings文件:
IP=192.168.1.230
Mask=255.255.255.0
Gateway=192.168.1.1
DNS=192.168.1.1
MAC=08:90:90:90:90:90
終于到最后了,啟動Qtopia GUI環(huán)境
/bin/qtopia &
echo " " > /dev/tty1
echo "Starting Qtopia, please waiting..." > /dev/tty1
可以看到,這里Qtopia是通過運行/bin/qtopia來啟動的。事實上,/bin/qtopia也是一個腳本,它的任務(wù)是設(shè)定Qtopia運行必要的環(huán)境, 最后通過調(diào)用qpe可執(zhí)行文件真正啟動Qtopia。這是它的全部內(nèi)容,我加入了一些注釋:
#!/bin/sh
export TSLIB_TSDEVICE=/dev/input/event0
export TSLIB_CONFFILE=/usr/local/etc/ts.conf
export TSLIB_PLUGINDIR=/usr/local/lib/ts
export TSLIB_CALIBFILE=/etc/pointercal
export QTDIR=/opt/Qtopia
export QPEDIR=/opt/Qtopia
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:/usr/local/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 KDEDIR=/opt/kde
export HOME=/root
exec $QPEDIR/bin/qpe 1>/dev/null 2>/dev/null
#!/bin/sh
#(1)tslib環(huán)境變量設(shè)置,包括了touchscreen設(shè)備文件,tslib配置文件,tslib plug-in位置和touchscreen校準數(shù)據(jù)文件
export TSLIB_TSDEVICE=/dev/input/event0
export TSLIB_CONFFILE=/usr/local/etc/ts.conf
export TSLIB_PLUGINDIR=/usr/local/lib/ts
export TSLIB_CALIBFILE=/etc/pointercal
#(2)Qtopia環(huán)境變量設(shè)置,設(shè)定了Qtopia主要文件位置
export QTDIR=/opt/Qtopia
export QPEDIR=/opt/Qtopia
#(3)設(shè)定PATH和LD_LIBRARY_PATH以包含Qtopia的可執(zhí)行文件和共享庫文件,方便Qtopia正確運行
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:/usr/local/lib:$LD_LIBRARY_PATH
#(4)通過判斷/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
#(5)通過調(diào)用/opt/Qtopia/bin/qpe真正啟動Qtopia
exec $QPEDIR/bin/qpe 1>/dev/null 2>/dev/null
到此為止,文件系統(tǒng)從初始化到最終啟動Qtopia GUI環(huán)境的全部過程就結(jié)束了,大家可以看到,友善之臂的“小秘密”其實都在這里,說穿了很簡單:)只要大家能夠靜下心來認真看看腳本,看看源代碼,加上一些背景知識的了解,搞清楚一個嵌入式系統(tǒng)就這么簡單。
通常/linuxrc這個文件只有在
1. 使用了Initial Ramdisk (initrd)
2. 內(nèi)核命令行上指定了init=/linuxrc
這兩種情況下才有用,mini2440的root_qtopia屬于情況2),
在root_qtopia中,/linuxrc是指向/bin/busybox的符號鏈接,也就是說,整個文件系統(tǒng)的入口就變成了busybox的main()函數(shù),busybox支持這種方式來啟動busybox本身和整個文件系統(tǒng)的初始化。
評論