MINI2440啟動配置文件/etc/init.d/rcS文件分析
對于mini2440,雖然root_qtopia這個文件系統的GUI是基于Qtopia的,但其初始化啟動過程卻是由大部分由busybox完成,Qtopia(qpe)只是在啟動的最后階段被開啟。
由于默認的內核命令行上有init=/linuxrc, 因此,在文件系統被掛載后,運行的第一個程序是根目錄下的linuxrc。這是一個指向/bin/busybox的鏈接,也就是說,系統起來后運行的第一個程序也就是busybox本身。這種情況下,busybox首先將試圖解析/etc/inittab來獲取進一步的初始化配置信息(參考busybox源代碼init/init.c中的parse_inittab()函數)。而事實上,root_qtopia中并沒有/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
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. 啟動環境設置必要的環境變量;
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格式的時候,它就玩不轉了,兄弟們要小心了:)
7. 啟動一系列服務:
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 - 用于記錄內核和應用程序debug信息
nnetd - inetd, 一個掛載啟動各種網絡相關服務的看守進程
nhttpd - http server看守進程
nleds- 跑馬燈看守進程
其中,inetd的配置文件為/etc/inetd.conf,這是文件內容:
# /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
可以看到,這里啟動的網絡服務有兩個:
1)ftp server 和
2)telnet server。
有關網絡服務的端口和協議等具體信息,可以參考/etc/services, /etc/protocol。
8. 配置網絡設備(網卡)
/sbin/ifconfig lo 127.0.0.1/etc/init.d/ifconfig-eth0:
1)設定本機回環地址為127.0.0.1
2)運行網卡設置腳本/etc/init.d/ifconfig-eth0
這是/etc/init.d/ifconfig-eth0的內容, 加入了我的一些注釋
#!/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
#如果根文件系統為nfs,則說明網卡已經配置OK,這里什么都不需要配置了
if grep -q "^/dev/root / nfs " /etc/mtab ; then echo -n NFS root ... > /dev/ttySAC0
#否則,根據配置文件中的MAC, IP, Mask和Gateway通過ifconfig命令相應地配置網卡
else
ifconfig eth0 down
ifconfig eth0 hw ether $MAC
ifconfig eth0 $IP netmask $Mask up
route add default gw $Gateway fi
#將配置文件中的DNS設置寫入/etc/resolv.conf使之生效
echo nameserver $DNS > /etc/resolv.conf
#配置文件不存在,使用默認配置else
#如果根文件系統為nfs,則說明網卡已經配置OK,這里什么都不需要配置了
if grep -q "^/dev/root / nfs " /etc/mtab ; then
echo -n NFS root ... > /dev/ttySAC0
else
#將網卡的IP地址設定為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的掛載記錄實現的。
這是root qtopia文件系統中/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環境
/bin/qtopia &
echo " " > /dev/tty1
echo "Starting Qtopia, please waiting..." > /dev/tty1
可以看到,這里Qtopia是通過運行/bin/qtopia來啟動的。事實上,/bin/qtopia也是一個腳本,它的任務是設定Qtopia運行必要的環境, 最后通過調用qpe可執行文件真正啟動Qtopia。這是它的全部內容,我加入了一些注釋:
#!/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環境變量設置,包括了touchscreen設備文件,tslib配置文件,tslib plug-in位置和touchscreen校準數據文件
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環境變量設置,設定了Qtopia主要文件位置
export QTDIR=/opt/Qtopia
export QPEDIR=/opt/Qtopia
#(3)設定PATH和LD_LIBRARY_PATH以包含Qtopia的可執行文件和共享庫文件,方便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)通過調用/opt/Qtopia/bin/qpe真正啟動Qtopia
exec $QPEDIR/bin/qpe 1>/dev/null 2>/dev/null
到此為止,文件系統從初始化到最終啟動Qtopia GUI環境的全部過程就結束了,大家可以看到,友善之臂的“小秘密”其實都在這里,說穿了很簡單:)只要大家能夠靜下心來認真看看腳本,看看源代碼,加上一些背景知識的了解,搞清楚一個嵌入式系統就這么簡單。
通常/linuxrc這個文件只有在
1. 使用了Initial Ramdisk (initrd)
2. 內核命令行上指定了init=/linuxrc
這兩種情況下才有用,mini2440的root_qtopia屬于情況2),
在root_qtopia中,/linuxrc是指向/bin/busybox的符號鏈接,也就是說,整個文件系統的入口就變成了busybox的main()函數,busybox支持這種方式來啟動busybox本身和整個文件系統的初始化。
評論