Mandrake9.0的啟動過程(從init開始)


以閱讀原始碼的方式研究 linux的啟動過程,是我早已有之的心願。今天總算是開工了。由於理解系統初始化過程要有組合語言的基礎,所以我只好先從init開始。
init的原始碼在/usr/src/linux-2.4.19-9mdk/init目錄下,在這個目錄下共有三個檔案 do_mounts.cmain.cversion.c。其中main.c就是init 行程的原始碼。
這段原始碼並不長,只有640行。
首先用ctags -x main.c 產生一個tags檔案 ,用vi 打開後,可以看到各個函數的索引:

LPS_PREC macro 183 main.c #define LPS_PREC 8
MAX_INIT_ARGS macro 125 main.c #define MAX_INIT_ARGS 8
MAX_INIT_ENVS macro 126 main.c #define MAX_INIT_ENVS 8
__KERNEL_SYSCALLS__ macro 12 main.c #define __KERNEL_SYSCALLS__
argv_init variable 135 main.c static char *
argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
calibrate_delay function 185 main.c void __init
calibrate_delay(void)
checksetup function 160 main.c static int __init
checksetup(char *line)
child_reaper variable 498 main.c struct task_struct
*child_reaper = &init_task;
cols variable 131 main.c int rows, cols;
debug_kernel function 226 main.c static int __init
debug_kernel(char *str)
do_basic_setup function 521 main.c static void __init
do_basic_setup(void)
do_initcalls function 500 main.c static void __init
do_initcalls(void)
envp_init variable 136 main.c char *
envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
execute_command variable 133 main.c char *execute_command;
gr_setup function 148 main.c static int __init
gr_setup(char *str)
init function 603 main.c static int init(void
*unused)
loops_per_jiffy variable 178 main.c unsigned long
loops_per_jiffy = (1<<12);
parse_options function 254 main.c static void __init
parse_options(char *line)
profile_setup function 138 main.c static int __init
profile_setup(char *str)
quiet_kernel function 234 main.c static int __init
quiet_kernel(char *str)
rest_init function 389 main.c static void rest_init(void)
rows variable 131 main.c int rows, cols;
smp_init function 349 main.c static void __init
smp_init(void)
smp_init function 361 main.c static void __init
smp_init(void)
smp_init macro 354 main.c #define smp_init() do { }
while (0)
start_kernel function 401 main.c asmlinkage void __init
start_kernel(void)
wait_init_idle variable 344 main.c unsigned long
wait_init_idle;

有了這個索引後,查找函數就方便了。再用vi 打開main.c,找到init函數,如下:
原始碼片段:
static int init(void * unused)
{
lock_kernel();
do_basic_setup();

prepare_namespace();
#ifdef CONFIG_GRKERNSEC
grsecurity_init();
#endif
/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/
free_initmem();
unlock_kernel();

if (open("/dev/console", O_RDWR, 0) < 0)
printk("Warning: unable to open an initial console.n");

(void) dup(0);
(void) dup(0);

/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/

if (execute_command)
execve(execute_command,argv_init,envp_init);
execve("/sbin/init",argv_init,envp_init);
execve("/etc/init",argv_init,envp_init);
execve("/bin/init",argv_init,envp_init);
execve("/bin/sh",argv_init,envp_init);
panic("No init found. Try passing init= option to kernel.");
在原始碼中,可以看到很多如同 #ifdef CONFIG_GRKERNSEC 的巨集定義,這些巨集定義可以在/usr/src/linux-2.4.19-9mdk/目錄下的.config文件中找到。用vi 查看.config文件中的巨集定義,發現 "# CONFIG_GRKERNSEC is not set",也就是沒有定義,因此,這個巨集定義可以不管它。先來看執行流程。

一、do_basic_setup()函數
init 進程第一個執行的函數是 lock_kernel(),這個函數在很多內核的原始碼中都有,但我沒有找到它的函數定義,只好放棄。
第二個執行的函數就是 do_basic_setup(),這個函數的內容如下:
原始碼片段 :
/*
* Ok, the machine is now initialized. None of the devices
* have been touched yet, but the CPU subsystem is up and
* running, and memory and process management works.
*
* Now we can finally start doing some real work..
*/
static void __init do_basic_setup(void)
{

/*
* Tell the world that we're going to be the grim
* reaper of innocent orphaned children.
*
* We don't want people to have to make incorrect
* assumptions about where in the task array this
* can be found.
*/
child_reaper = current;

#if defined(CONFIG_MTRR) /* Do this after SMP initialization */
/*
* We should probably create some architecture-dependent "fixup after
* everything is up" style function where this would belong better
* than in init/main.c..
*/
mtrr_init();
#endif /*mtrr(Memory Type Range Register)是Inter P6系列處理器用來控制處理器讀寫暫存器範圍的。*/

#ifdef CONFIG_SYSCTL
sysctl_init();
#endif /* 對 /proc 檔案系統和sysctl()系統呼叫相關部分進行初始化*/

/*
* Ok, at this point all CPU's should be initialized, so
* we can start looking into devices..
*/
#if defined(CONFIG_ARCH_S390)
s390_init_machine_check();
#endif

#ifdef CONFIG_PCI
pci_init();
#endif /* 初始化PCI總線 */
#ifdef CONFIG_SBUS
sbus_init();
#endif
#if defined(CONFIG_PPC)
ppc_init();
#endif
#ifdef CONFIG_MCA
mca_init();
#endif
#ifdef CONFIG_ARCH_ACORN
ecard_init();
#endif
#ifdef CONFIG_ZORRO
zorro_init();
#endif
#ifdef CONFIG_DIO
dio_init();
#endif
#ifdef CONFIG_NUBUS
nubus_init();
#endif
#ifdef CONFIG_ISAPNP
isapnp_init();
#endif /* 對ISA總線即插即用初始化 */
#ifdef CONFIG_TC
tc_init();
#endif

/* Networking initialization needs a process context */
sock_init(); /* 初始化網絡協議堆疊 */

start_context_thread();
do_initcalls();

#ifdef CONFIG_IRDA
irda_proto_init();
irda_device_init(); /* Must be done after protocol initialization */
#endif
#ifdef CONFIG_PCMCIA
init_pcmcia_ds(); /* Do this last */
#endif
}
很明顯,這段原始碼片段是用來進行對系統初始化的。開頭的一段註釋告訴我們,系統硬體此時只有cpu子系統在運轉,記憶體管理和行程管理也開始工作了。
接下來,就是對硬體的初始化。
這一部分與硬體密切相關,在編譯核心時,將根據配置檔案 .config 來編譯相應的部分。用vi查看.config檔案,發現定義的項目如下:

CONFIG_MTRR=y
CONFIG_SYSCTL=y

CONFIG_PCI=y
# CONFIG_PCI_GOBIOS is not set
# CONFIG_PCI_GODIRECT is not set
CONFIG_PCI_GOANY=y
CONFIG_PCI_BIOS=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_NAMES=y
CONFIG_PCI_HERMES=m

# CONFIG_SBUS is not set
# CONFIG_MCA is not set
CONFIG_ISAPNP=y

CONFIG_TCIC=y
CONFIG_TC35815=m

CONFIG_IRDA=m
CONFIG_IRDA_ULTRA=y
CONFIG_IRDA_CACHE_LAST_LSAP=y
CONFIG_IRDA_FAST_RR=y
# CONFIG_IRDA_DEBUG is not set

CONFIG_PCMCIA=m
CONFIG_PCMCIA_AHA152X=m
CONFIG_PCMCIA_FDOMAIN=m
CONFIG_PCMCIA_NINJA_SCSI=m
CONFIG_PCMCIA_QLOGIC=m
CONFIG_PCMCIA_HERMES=m
CONFIG_PCMCIA_3C589=m
CONFIG_PCMCIA_3C574=m
CONFIG_PCMCIA_FMVJ18X=m
CONFIG_PCMCIA_PCNET=m
CONFIG_PCMCIA_AXNET=m
CONFIG_PCMCIA_NMCLAN=m
CONFIG_PCMCIA_SMC91C92=m
CONFIG_PCMCIA_XIRC2PS=m
CONFIG_PCMCIA_IBMTR=m
CONFIG_PCMCIA_XIRCOM=m
CONFIG_PCMCIA_XIRTULIP=m
CONFIG_PCMCIA_RAYCS=m
CONFIG_PCMCIA_NETWAVE=m
CONFIG_PCMCIA_WAVELAN=m
CONFIG_PCMCIA_WVLAN=m
CONFIG_PCMCIA_SERIAL_CS=m

這樣一看,mandrake預設配置的東西真不少,就連我根本用不上的IRDAPCMCIA都編譯成模組了。有了這些原始碼後,在開機時,就會看到這些啟動訊息:

[root@c4 linux-2.4.19-9mdk]#dmesg
......
mtrr: v1.40 (20010327) Richard Gooch (rgooch@atnf.csiro.au)
mtrr: detected mtrr type: Intel
PCI: PCI BIOS revision 2.10 entry at 0xfdb81, last bus=3
PCI: Using configuration type 1
PCI: Probing PCI hardware
Unknown bridge resource 0: assuming transparent
PCI: Using IRQ router PIIX [8086/2440] at 00:1f.0
isapnp: Scanning for PnP cards...
isapnp: No Plug & Play device found
Linux NET4.0 for Linux 2.4
......


二、prepare_namespace()函數
接下來要執行的是prepare_namespace()函數。這個函數在/usr/src/linux-2.4.19-9mdk/init/do_mounts.c檔案中。內容如下:
原始碼片段 :
/*
* Prepare the namespace - decide what/where to mount, load ramdisks, etc.
*/
void prepare_namespace(void)
{
int is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
#ifdef CONFIG_ALL_PPC
extern void arch_discover_root(void);
arch_discover_root();
#endif /* CONFIG_ALL_PPC */
#ifdef CONFIG_BLK_DEV_INITRD
if (!initrd_start)
mount_initrd = 0;
real_root_dev = ROOT_DEV;
#endif
sys_mkdir("/dev", 0700);
sys_mkdir("/root", 0700);
sys_mknod("/dev/console", S_IFCHR|0600, MKDEV(TTYAUX_MAJOR, 1));
#ifdef CONFIG_DEVFS_FS
sys_mount("devfs", "/dev", "devfs", 0, NULL);
do_devfs = 1;
#endif

create_dev("/dev/root", ROOT_DEV, NULL);
if (mount_initrd) {
if (initrd_load() && ROOT_DEV != MKDEV(RAMDISK_MAJOR, 0)) {
handle_initrd();
goto out;
}
} else if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
mount_root();
out:
sys_umount("/dev", 0);
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
mount_devfs_fs ();
}
這段原始碼片段主要是決定根設備安裝在那兒,在中間要處理一下RAM disk並判斷是不是軟碟啟動的。RAM主要用來在核心安裝根檔案系統之前,預先裝入一些模組。如果在lilo中指定了一個initrd.img映像文件,則內核在安裝根設備之前,把它裝上,否則正常安裝根設備

三、切換使用者模式運行
在完成初始化後,系統接著執行free_initmem(),將初始化過程中使用的記憶體釋放。然後執行unlock_kernel(),這個函數想必就是前面lock_kernel()的逆操作了。然後以可讀可寫方式打開一個控制台設備。並複製兩個檔案描述符
最後,init檢查是否有給定的指令,如果沒有,則按順序檢查是否存在/sbin/init、/etc/init、/bin/init/bin/sh等檔案,如果存在,則跳轉執行相應的程序。一般情況下,系統都會啟動/sbin/init 程序,從此以後建構的行程都會在使用者模式下執行。下一步的系統啟動過 程,也由 /sbin/init 來接著完成。


在內核載入結束並啟動 /sbin/init 之後,系統就切換成使用者模式下執行。在這之後建構的一切行程,都是在使用者模式進行。這裡先要講清楚一個概念:就是 init 行程雖然是從內核開始的,即在前面所講的init/main.c中的init()函數在啟動後就已經是一個核心線程,但在轉到執行 /sbin/init之後,內核中的init()就變成了/sbin/init程序,狀態也轉變成了使用者模式,也就是說核心線程變成了一個普通的行程。這樣 一來,內核中的init函數實際上只是init行程的進入點,它在執行execve("/sbin/init",argv_init,envp_init) 時改變成為一個普通的使用者行程。這就是exec系列函數替換進程映像的變身******************,只要你學過unix環境下的多行程程式設計,應該都能理解這一點。
除此之外,它們的原始碼來源也有差別,內核中的 init() 函數的原始碼在 /init/main.c 中,是內核的一部分。而 /sbin/init 程序的原始碼是 sysvinit 軟體套件的一部分,可以從
ftp://ftp.cistron.nl/pub/people/miquels/sysvinit/ 下載,如:halt, init, killall5, last, lastb (鏈接至 last), mesg, pidof (鏈接至 killall5), poweroff (鏈接至 halt), reboot (鏈接至 halt), runlevel, shutdown, sulogin, telinit (鏈接至 init), utmpdumpwall等等。對於啟動過程而言,這裡最重要的就是init程序。它啟動之後,要完成很多任務:檢查檔案系統,啟動各種背景服務行程,最後為每個終端和虛 擬控制台啟動一個 getty 行程供使用者登錄。由於所有其它使用者行程都是由 init 衍生的,因此它又是其它一切使用者行程的父行程。
init 行程啟動後,就要按照 /etc/inittab 的內容行程系統設置。下面就是 mandrake9.0 的 inittab 內容:

#
# inittab This file describes how the INIT process should set up
# the system in a certain run-level.
#
# Author: Miquel van Smoorenburg,
# Modified for RHS Linux by Marc Ewing and Donnie Barnes
#

# Default runlevel. The runlevels used by Mandrake Linux are:
# 0 - halt (Do NOT set initdefault to this)
# 1 - Single user mode
# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)
# 3 - Full multiuser mode
# 4 - unused
# 5 - X11
# 6 - reboot (Do NOT set initdefault to this)
#
id:5:initdefault:

# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

# Things to run in every runlevel.
ud::once:/sbin/update

# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

# When our UPS tells us power has failed, assume we have a few minutes
# of power left. Schedule a shutdown for 2 minutes from now.
# This does, of course, assume you have powerd installed and your
# UPS connected and working correctly.
pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"

# If power was restored before the shutdown kicked in, cancel it.
pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"


# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

從中可以看到,inittab的每一行由四個字段組成。分別是:
id:runlevels:action:process
id : 是每一行的標識符,長度一般為2個字符而且在整個inittab中必須唯一。對於getty或其他login程序項,要求id與tty的編號相同,否則getty程序將不能正常工作。
runlevels : 指定運行級別,一般使用0-6以及S或s。執行級別是指 init 行程的整個系統的一個運行狀態,它定義了系統所提供的服務。
常見的運 行級別如下:
0,系統終止;
1,單使用者模式;
2,沒有網路檔案系統支援的多使用者模式;
3,有網路檔案系統支援的多使用者模式;
4,保留;
5,啟動到Xwindow;
6,重新啟動,S和s意義相同, 表示單使用者模式且無需 inittab 檔案,因此也不必在inittab中出現,實際上,進入單使用者模式時,init 直接在控制台 (/dev/console)上執行 /sbin/sulogin。實際上,7-9的運行級別也是可以使用的,只是傳統的Unix系統沒有定義這幾個級別。 級別可以是並列的多個值,以匹配多個運行級別,對大多數 action 來說,僅當 runlevel 與當前運行級別匹配成功才會執行。
action : 指出的是 init 程序執行 process 命令的方式。
initdefault 是一個特殊的 action 值,用於標識預設的執行級別。當 init 行程由核心啟動後,它將讀取 inittab 中的 initdefault 項目,取得其中的執行級別,並作為當前的執行
級別。如果沒有 inittab 檔案,或者其中沒有 initdefault 項目,init 將在控制台上請求輸入運行級別。
respawn
在進入相應 runlevel 時執行,如果該行程結束,init 會再發起一個行程執行同樣的命令。
once
在進入有 runlevels 指定的執行級別時運行並 且只執行一次。
wait
的執行效果和 once 一樣,但 init 會等待該命令結束。
ctrlaltdel 指定在使用者按下Ctrl-Alt-Del時執行的命 令。
powerfail 在系統接收到斷電信號時啟動相關行程。
powerwaitpowerfail 一樣,在系統接收到斷電信號時啟動相關行程,並等待行程終止,在行程終止前不做任何其它操作。
off 若該項相關行程已經存在則強行終止,否則忽略。
sysinit、boot、bootwait action 將在系統啟動時無條件執行,而忽略其中的執行級 別,其中 sysinit 指定需執行的第一個程序(或腳本),boot 將在 sysinit 之後執行,
bootwait 的執行效果和 boot 一樣,但 init 會等待該命令結束,其 餘的 action(不含 initdefault)都與某個執行級別相關,可以在 inittabman 手冊中找到各個 action 的定義的詳細描述。
process : 給出每行要執行的命令。

知道 inittab 的字段意義以後,我們來看一下 inittab 的結構。inittab 的第一行是設定系統的執行級別,如下所示:

id:5:initdefault:

從以上的介紹中可以知道,系統的執行級別是5,也就是系統啟動時自動進入x-window。接著進行系統初始化:

si::sysinit:/etc/rc.d/rc.sysinit

這樣 init 行程就會執行 /etc/rc.d/rc.sysinit。這個腳本最常見的動作就是啟動交換磁區檢查磁碟載入硬體模組,這些動作無論哪個 運行級別都是需要優先執行的。只有在 rc.sysinit 執行完以後 init 進程才會執行其他的 bootbootwait 動作。如果沒有 bootbootwait 動作,init 行程就接著執行下面的內容:

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

這裡,系統將會根據執行級別選擇要執行的命令。從前面我們已經知道系統的執行級別是5,所以實際上只有這一行被執行了:

l5:5:wait:/etc/rc.d/rc 5

這一行的最後是以參數5運行/etc/rc.d/rc,實際上就是運行/etc/rc.d/rc3.d下的所有程序(或腳本)。用ls命令查看 /etc/rc.d/rc3.d 目錄,如下:

[kj501@c4 kj501]$ ls /etc/rc.d/rc3.d/
K09dm@ S16ypserv@ S56rawdevices@ S90crond@
K55routed@ S17alsa@ S56xinetd@ S90postgresql@
K90mysql@ S18sound@ S60cups@ S91smb@
S01usb@ S20random@ S60nfs@ S92lisa@
S03iptables@ S20xfs@ S60rwhod@ S95innd@
S05harddrake@ S25netfs@ S66yppasswdd@ S95kheader@
S10network@ S26apmd@ S75keytable@ S99devfsd@
S11portmap@ S26ypxfrd@ S80postfix@ S99hamboot@
S12syslog@ S40atd@ S85httpd@ S99linuxconf@
S13partmon@ S40saslauthd@ S85numlock@ S99local@
S14nfslock@ S55named@ S85proftpd@
S15gpm@ S55sshd@ S89internet@

這些檔案都是一些系統服務程序,以「S」開頭的文件用來在進入執行級別5時啟動服務行程,以「K」開頭的文件用來在系統關閉,離開執行級別5之前終止服務行程。後面的數字大小決定執行的先後次序,對於以「K」開頭的服務行程,按照從高到低的順序執行,對於以「S」開頭的服務行程,按照從低到高的順序執行。 如果用 ls -l 查看這些文件,就會發現它們大部分都是到 /etc/rc.d/init.d 目錄下各個 shell 腳本的符號鏈接,而且這些腳本一般能接受 start、 stop、restart、status 等參數,在啟動時以 start 參數啟動以「S」開頭的腳本鏈接,如果存在一個同樣的腳本鏈接但是以「K」開頭,也 會先執行以「K」開頭的腳本鏈接,然後再執行以「S」開打頭的腳本鏈接,以保證這個服務行程是重新啟動的。要注意在這些腳本的最後,都有一個 S99local@ 的鏈接,這個鏈接是鏈接到 /etc/rc.d/rc.local,而不是像其它腳本鏈接一樣鏈接到 /etc/rc.d/init.d 下。可以在這個 rc.local 加入一些每個執行級別都要執行但又必須在系統服務行程啟動後才能執行的命令,比如說對一些系統服務程序的參數設置。執行這 些腳本之後,下面就要執行一個在每
個執行級別都要執行的命令:

ud::once:/sbin/update

這句話表明每一個執行級別都要運行命令 update,此程序每隔30秒把記憶體緩衝區的內容回寫一次,稱為"同步",以防止系統崩潰或突然斷電造成的資料遺失和損壞。在這條命令
之後,系統要設置一個陷阱捕獲參數Trap處理Ctrl-Alt-Del三個鍵同時按下時的執行動作。

ca::ctrlaltdel:/sbin/shutdown -t3 -r now

這樣,當使用者按下 Ctrl-Alt-Del 時,系統就會執行 /sbin/shutdown -t3 -r now 這個命令重新啟動系統。如果你要使 Ctrl-Alt-Del 失效,在這一行的前面加上一個#,也就是
把它註釋掉就行了。

再往下,我們可以看到這一行:

pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"

這是處理在突然斷電時系統執行的命令的。要讓系統執行這個命令,你需要有 UPS 的支援並要啟動 powerd 服務行程。與此相對應,如果在 shutdown 命令還沒有執行完成時,
電源又重新恢復正常,則系統會執行下面的命令中止shutdown命令:

pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"

最後,系統要執行下列的命令,產生6個控制台:

1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

這些操作都是通過 getty 進行的。mingettygetty 的簡化,不能處理序列埠操作。getty 的功能一般包括: 打開終端線,並設置模式;輸出登錄界面及提示,接受使用者名稱的輸入;以該使用者名稱作為 login 的參數,載入 login 程序等等。預設的登錄提示記錄在 /etc/issue 檔案中,但每次啟動,一般都會由 rc.local 腳本根據系統環境重新生成。大家可以看到,在第二個區段上(也就是 runlevel 區段)並列有幾個運行級別2345,這表明系統只有在2345運行級別時才會啟動這些進程。respawn 的意思在前面已經介紹過,這裡就不說了。在產生 這六個控制台後,每一個控制台都會出現 login : 提示符號,系統的啟動過程也就到此結束了,此時只要輸入你的使用者名稱和密碼,你就開始正常地使用 linux了。

Reference:
    http://www.umbi.umd.edu/cgi-bin/man-cgi?inittab
    http://www.linux-boot.net/Loaders/
    http://linux.vbird.org/linux_basic/0510osloader.php
    http://bbs.ee.ntu.edu.tw/boards/Linux/16/2/8.html
    http://www.linuxnetmag.com/en/issue4/m4boot1.html
    http://oldfield.wattle.id.au/luv/boot.html
    http://www-128.ibm.com/developerworks/cn/linux/l-fireboot.html
    http://www.cpqlinux.com/sysinit.html
    http://www.samhart.com/cgi-bin/classnotes/wiki.pl?UNIX02/The_Init_Process
    http://docs.kde.org/development/en/kdeadmin/ksysv/what-is-sysv-init.html
    http://cmpp.linuxforum.net/cman-html/man5/inittab.5.html
    http://www.5dmail.net/html/2003-10-6/2003106110334.htm
    http://www.yolinux.com/TUTORIALS/LinuxTutorialInitProcess.html
    http://freebsd.active-venture.com/handbook/boot-init.html
    http://jamesthornton.com/freebsd/books/handbook/boot-init.html
 
arrow
arrow
    全站熱搜

    Bluelove1968 發表在 痞客邦 留言(0) 人氣()