MINI2440啟動(dòng)配置文件/etc/init.d/rcS文件分析
對(duì)于mini2440,雖然root_qtopia這個(gè)文件系統(tǒng)的GUI是基于Qtopia的,但其初始化啟動(dòng)過(guò)程卻是由大部分由busybox完成,Qtopia(qpe)只是在啟動(dòng)的最后階段被開(kāi)啟。
由于默認(rèn)的內(nèi)核命令行上有init=/linuxrc, 因此,在文件系統(tǒng)被掛載后,運(yùn)行的第一個(gè)程序是根目錄下的linuxrc。這是一個(gè)指向/bin/busybox的鏈接,也就是說(shuō),系統(tǒng)起來(lái)后運(yùn)行的第一個(gè)程序也就是busybox本身。這種情況下,busybox首先將試圖解析/etc/inittab來(lái)獲取進(jìn)一步的初始化配置信息(參考busybox源代碼init/init.c中的parse_inittab()函數(shù))。而事實(shí)上,root_qtopia中并沒(méi)有/etc/inittab這個(gè)配置文件,根據(jù)busybox的邏輯,它將生成默認(rèn)的配置(部分):
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;
}
……
其中, 最重要的一個(gè),就是new_init_action(SYSINIT, INIT_SCRIPT, ""), 也就決定了接下去初始化的腳本是INIT_SCRIPT所定義的值。這個(gè)宏的默認(rèn)值是"/etc/init.d/rcS"。
下面是文件系統(tǒng)中/etc/init.d/rcS的內(nèi)容, 也是我們要分析的重點(diǎn)
#! /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
下面就逐個(gè)來(lái)分析:
1. 啟動(dòng)環(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è)置機(jī)器名字;
/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目錄上掛載一個(gè)ramfs,相當(dāng)于把原本NAND Flash上的只讀的/dev目錄“覆蓋”上一塊可寫(xiě)的空的SDRAM。
這里要注意的是,/sys和掛載了ramfs的/dev是正確創(chuàng)建設(shè)備節(jié)點(diǎn)的關(guān)鍵。對(duì)于2.6.29內(nèi)核來(lái)說(shuō),已經(jīng)沒(méi)有了devfs的支持,創(chuàng)建設(shè)備節(jié)點(diǎn)只有通過(guò)兩種辦法由文件系統(tǒng)完成:
1) 制作文件系統(tǒng)鏡像前用mknod手動(dòng)創(chuàng)建好系統(tǒng)中所有的(包括可能有的)設(shè)備節(jié)點(diǎn),并把這些節(jié)點(diǎn)文件一起做進(jìn)文件系統(tǒng)鏡像中;
2)在文件系統(tǒng)初始化過(guò)程中,通過(guò)/sys目錄所輸出的信息,在/dev目錄下動(dòng)態(tài)的創(chuàng)建系統(tǒng)中當(dāng)前實(shí)際有的設(shè)備節(jié)點(diǎn)。
顯然,方法1)有很大的局限性,僅限于沒(méi)有設(shè)備動(dòng)態(tài)增加或減少的情況,不適用于很多設(shè)備熱插拔的情況,比如U盤(pán),SD卡等等。方法2)是目前大多數(shù)PC上Linux的做法(基于udev實(shí)現(xiàn))。這種方法有兩個(gè)前提:/sys目錄掛載和一個(gè)可寫(xiě)的/dev目錄。
這也就是為什么我們?cè)谶@里需要掛載/sys和ramfs在/dev目錄上。事實(shí)上,這種方法最早就是為熱插拔設(shè)計(jì)的,你可以理解為當(dāng)系統(tǒng)啟動(dòng)是,所有設(shè)備一下子全部“插入”了進(jìn)來(lái)。
這里有一點(diǎn)要說(shuō)明的是,在文件系統(tǒng)初始化跑到這里之前,原本的/dev目錄下必須有一個(gè)的設(shè)備節(jié)點(diǎn):/dev/console。
4. 支持熱插拔
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev –s
/bin/hotplug
這幾個(gè)就是用來(lái)完成我上面所說(shuō)的兩個(gè)東西:
1)通過(guò)mdev -s 在/dev目錄下建立必要的設(shè)備節(jié)點(diǎn);
2)設(shè)置內(nèi)核的hotplug handler為mdev, 即當(dāng)設(shè)備熱插拔時(shí),由mdev接收來(lái)自內(nèi)核的消息并作出相應(yīng)的回應(yīng), 比如掛載U盤(pán)。
對(duì)于mdev,需要注意的是,文件系統(tǒng)里存在/etc/mdev.conf文件,它包含了mdev的配置信息。通過(guò)這個(gè)文件,我們可以自定義一些設(shè)備節(jié)點(diǎn)的名稱或鏈接來(lái)滿足特定的需要。這是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ū)動(dòng)注冊(cè)的設(shè)備名是s3c2410_serial0,s3c2410_serial1和s3c2410_serial2,而mdev則會(huì)在/dev目錄下對(duì)應(yīng)生成ttySAC0,ttySAC1和ttySAC2以符合應(yīng)用程序?qū)τ?strong>串口設(shè)備名稱的習(xí)慣。
同樣的,/dev/sdcard和/dev/udisk永遠(yuǎn)分別指向SD卡和U盤(pán)的第一個(gè)分區(qū)。(所以,用那些沒(méi)有分區(qū)表的SD卡或U盤(pán)的兄弟知道原因了吧...)
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
就像注釋中所說(shuō)的,這是用來(lái)掛載其他一些常用的文件系統(tǒng),并在/var目錄下(同樣是ramfs,可寫(xiě)的)新建必要的目錄。
6. 設(shè)定系統(tǒng)時(shí)間
/sbin/hwclock -s
從硬件RTC中獲取,不過(guò)似乎有問(wèn)題接下來(lái)就是啟動(dòng)系統(tǒng)服務(wù)了,包括log記錄,網(wǎng)絡(luò), http server和自定義的"跑馬燈服務(wù)"...
【關(guān)于mdev.conf的補(bǔ)充說(shuō)明】
# misc devices
mmcblk0p1 0:0 0600 =sdcard */bin/hotplug
sda1 0:0 0600 =udisk * /bin/hotplug
這兩句配置的意思是當(dāng)SD卡或者U盤(pán)插入/拔出時(shí),將這個(gè)消息傳遞給自定義的熱插拔handler, /bin/hotplug.
這個(gè)程序是友善之臂開(kāi)發(fā)的用于自動(dòng)掛載可移動(dòng)設(shè)備的,目前是SD卡和U盤(pán)。它的邏輯很簡(jiǎn)單,將SD卡或者U盤(pán)的第一個(gè)分區(qū)作為FAT/FAT32掛載到/sdcard或者/udisk。
但這也同時(shí)帶來(lái)一個(gè)問(wèn)題,當(dāng)SD卡或者U盤(pán)上沒(méi)有分區(qū)表或者第一個(gè)分區(qū)不是FAT/FAT32格式的時(shí)候,它就玩不轉(zhuǎn)了,兄弟們要小心了:)
7. 啟動(dòng)一系列服務(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, 一個(gè)掛載啟動(dòng)各種網(wǎng)絡(luò)相關(guān)服務(wù)的看守進(jìn)程
nhttpd - http server看守進(jìn)程
nleds- 跑馬燈看守進(jìn)程
其中,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
可以看到,這里啟動(dòng)的網(wǎng)絡(luò)服務(wù)有兩個(gè):
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è)定本機(jī)回環(huán)地址為127.0.0.1
2)運(yùn)行網(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,則說(shuō)明網(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通過(guò)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è)置寫(xiě)入/etc/resolv.conf使之生效
echo nameserver $DNS > /etc/resolv.conf
#配置文件不存在,使用默認(rèn)配置else
#如果根文件系統(tǒng)為nfs,則說(shuō)明網(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自動(dòng)識(shí)別就是靠判斷/etc/mtab中是否有nfs的掛載記錄實(shí)現(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
終于到最后了,啟動(dòng)Qtopia GUI環(huán)境
/bin/qtopia &
echo " " > /dev/tty1
echo "Starting Qtopia, please waiting..." > /dev/tty1
可以看到,這里Qtopia是通過(guò)運(yùn)行/bin/qtopia來(lái)啟動(dòng)的。事實(shí)上,/bin/qtopia也是一個(gè)腳本,它的任務(wù)是設(shè)定Qtopia運(yùn)行必要的環(huán)境, 最后通過(guò)調(diào)用qpe可執(zhí)行文件真正啟動(dòng)Qtopia。這是它的全部?jī)?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校準(zhǔn)數(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í)行文件和共享庫(kù)文件,方便Qtopia正確運(yùn)行
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:/usr/local/lib:$LD_LIBRARY_PATH
#(4)通過(guò)判斷/sys/devices/virtual/input/input0/uevent中是否包含touchscreen信息使Qtopia自動(dòng)識(shí)別touchscreen和USB鼠標(biāo)
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)通過(guò)調(diào)用/opt/Qtopia/bin/qpe真正啟動(dòng)Qtopia
exec $QPEDIR/bin/qpe 1>/dev/null 2>/dev/null
到此為止,文件系統(tǒng)從初始化到最終啟動(dòng)Qtopia GUI環(huán)境的全部過(guò)程就結(jié)束了,大家可以看到,友善之臂的“小秘密”其實(shí)都在這里,說(shuō)穿了很簡(jiǎn)單:)只要大家能夠靜下心來(lái)認(rèn)真看看腳本,看看源代碼,加上一些背景知識(shí)的了解,搞清楚一個(gè)嵌入式系統(tǒng)就這么簡(jiǎn)單。
通常/linuxrc這個(gè)文件只有在
1. 使用了Initial Ramdisk (initrd)
2. 內(nèi)核命令行上指定了init=/linuxrc
這兩種情況下才有用,mini2440的root_qtopia屬于情況2),
在root_qtopia中,/linuxrc是指向/bin/busybox的符號(hào)鏈接,也就是說(shuō),整個(gè)文件系統(tǒng)的入口就變成了busybox的main()函數(shù),busybox支持這種方式來(lái)啟動(dòng)busybox本身和整個(gè)文件系統(tǒng)的初始化。
評(píng)論