?? main.c
字號:
/* passed * linux/init/main.c * * (C) 1991 Linus Torvalds */
#include <set_seg.h>
#define __LIBRARY__ // 定義該變量是為了包括定義在unistd.h 中的內(nèi)嵌匯編代碼等信息。
#include <unistd.h>#include <time.h> // 時(shí)間類型頭文件。其中最主要定義了tm 結(jié)構(gòu)和一些有關(guān)
// 時(shí)間的函數(shù)原形。/* * 我們需要下面這些內(nèi)嵌語句- 從內(nèi)核空間創(chuàng)建進(jìn)程(forking)將導(dǎo)致沒有寫時(shí)復(fù) * 制(COPY ON WRITE)!!!直到一個(gè)執(zhí)行execve 調(diào)用。這對堆棧可能帶來問題。處 * 理的方法是在fork()調(diào)用之后不讓main()使用任何堆棧。因此就不能有函數(shù)調(diào) * 用- 這意味著fork 也要使用內(nèi)嵌的代碼,否則我們在從fork()退出時(shí)就要使用堆棧了。 * * 實(shí)際上只有pause 和fork 需要使用內(nèi)嵌方式,以保證從main()中不會弄亂堆棧,
* 但是我們同時(shí)還定義了其它一些函數(shù)。
*/
static _inline _syscall0(int,fork)// 是unistd.h 中的內(nèi)嵌宏代碼。以嵌入?yún)R編的形式調(diào)用
// Linux 的系統(tǒng)調(diào)用中斷0x80。該中斷是所有系統(tǒng)調(diào)用的
// 入口。該條語句實(shí)際上是int fork()創(chuàng)建進(jìn)程系統(tǒng)調(diào)用。
// syscall0 名稱中最后的0 表示無參數(shù),1 表示1 個(gè)參數(shù)。static _inline _syscall0(int,pause)// int pause()系統(tǒng)調(diào)用:暫停進(jìn)程的執(zhí)行,直到
// 收到一個(gè)信號。static _inline _syscall1(int,setup,void *,BIOS)// int setup(void * BIOS)系統(tǒng)調(diào)用,僅用于
// linux 初始化(僅在這個(gè)程序中被調(diào)用)。static _inline _syscall0(int,sync)// int sync()系統(tǒng)調(diào)用:更新文件系統(tǒng)。#include <linux/tty.h> // tty 頭文件,定義了有關(guān)tty_io,串行通信方面的
// 參數(shù)、常數(shù)。#include <linux/sched.h> // 調(diào)度程序頭文件,定義了任務(wù)結(jié)構(gòu)task_struct、第1 個(gè)
// 初始任務(wù)的數(shù)據(jù)。還有一些以宏的形式定義的有關(guān)描述符
// 參數(shù)設(shè)置和獲取的嵌入式匯編函數(shù)程序。#include <linux/head.h> // head 頭文件,定義了段描述符的簡單結(jié)構(gòu),
// 和幾個(gè)選擇符常量。#include <asm/system.h> // 系統(tǒng)頭文件。以宏的形式定義了許多有關(guān)設(shè)置或修改
// 描述符/中斷門等的嵌入式匯編子程序。#include <asm/io.h> // io 頭文件。以宏的嵌入?yún)R編程序形式定義對io 端
// 口操作的函數(shù)。#include <stddef.h> // 標(biāo)準(zhǔn)定義頭文件。定義了NULL, offsetof(TYPE, MEMBER)。#include <stdarg.h> // 標(biāo)準(zhǔn)參數(shù)頭文件。以宏的形式定義變量參數(shù)列表。主要說
// 明了-個(gè)類型(va_list)和三個(gè)宏(va_start, va_arg 和
// va_end),vsprintf、vprintf、vfprintf。#include <fcntl.h> // 文件控制頭文件。用于文件及其描述符的操作控制常數(shù)
// 符號的定義。#include <sys/types.h>// 類型頭文件。定義了基本的系統(tǒng)數(shù)據(jù)類型。#include <linux/fs.h>// 文件系統(tǒng)頭文件。定義文件表結(jié)構(gòu)
//(file,buffer_head,m_inode 等)。static char printbuf[1024];extern int vsprintf(); // 送格式化輸出到一字符串中(在kernel/vsprintf.c)。extern void init(void); // 函數(shù)原形,初始化(在后面)。extern void blk_dev_init(void);// 塊設(shè)備初始化子程序(kernel/blk_drv/ll_rw_blk.c)extern void chr_dev_init(void);// 字符設(shè)備初始化(kernel/chr_drv/tty_io.c)extern void hd_init(void);// 硬盤初始化程序(kernel/blk_drv/hd.c)extern void floppy_init(void);// 軟驅(qū)初始化程序(kernel/blk_drv/floppy.c)extern void mem_init(long start, long end);// 內(nèi)存管理初始化(mm/memory.c)extern long rd_init(long mem_start, int length);//虛擬盤初始化(kernel/blk_drv/ramdisk.c)extern long kernel_mktime(struct tm * tm);// 建立內(nèi)核時(shí)間(秒)。extern long startup_time;// 內(nèi)核啟動(dòng)時(shí)間(開機(jī)時(shí)間)(秒)。/* * 以下這些數(shù)據(jù)是由setup.s 程序在引導(dǎo)時(shí)間設(shè)置的。 */#define EXT_MEM_K (*(unsigned short *)0x90002)// 1M 以后的擴(kuò)展內(nèi)存大?。↘B)。#define DRIVE_INFO (*(struct drive_info *)0x90080)// 硬盤參數(shù)表基址。#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)// 根文件系統(tǒng)所在設(shè)備號。/* * 是啊,是啊,下面這段程序很差勁,但我不知道如何正確地實(shí)現(xiàn),而且好象
* 它還能運(yùn)行。如果有關(guān)于實(shí)時(shí)時(shí)鐘更多的資料,那我很感興趣。這些都是試
* 探出來的,以及看了一些bios 程序,呵! */
// 這段宏讀取CMOS 實(shí)時(shí)時(shí)鐘信息。
// 0x70 是寫端口號,0x80|addr 是要讀取的CMOS 內(nèi)存地址。
// 0x71 是讀端口號。
/*
#define CMOS_READ(addr) ({ \outb_p(0x80|addr,0x70); \inb_p(0x71); \})*/
_inline unsigned char CMOS_READ(unsigned char addr)
{
outb_p(addr,0x70);
return inb_p(0x71);
}
// 將BCD 碼轉(zhuǎn)換成數(shù)字。#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
// 該子程序取CMOS 時(shí)鐘,并設(shè)置開機(jī)時(shí)間 startup_time(為從1970-1-1-0 時(shí)起到開機(jī)時(shí)的秒數(shù))。static void time_init(void){ struct tm time; do {// 參見后面CMOS 內(nèi)存列表。 time.tm_sec = CMOS_READ(0);
time.tm_min = CMOS_READ(2); time.tm_hour = CMOS_READ(4); time.tm_mday = CMOS_READ(7); time.tm_mon = CMOS_READ(8); time.tm_year = CMOS_READ(9); } while (time.tm_sec != CMOS_READ(0)); BCD_TO_BIN(time.tm_sec); BCD_TO_BIN(time.tm_min); BCD_TO_BIN(time.tm_hour); BCD_TO_BIN(time.tm_mday); BCD_TO_BIN(time.tm_mon); BCD_TO_BIN(time.tm_year); time.tm_mon--; startup_time = kernel_mktime(&time);}static long memory_end = 0;// 機(jī)器具有的內(nèi)存(字節(jié)數(shù))。static long buffer_memory_end = 0;// 高速緩沖區(qū)末端地址。static long main_memory_start = 0;// 主內(nèi)存(將用于分頁)開始的位置。struct drive_info { char dummy[32]; } drive_info;// 用于存放硬盤參數(shù)表信息。void main_rename(void) /* 這里確實(shí)是void,并沒錯(cuò)。 */{ /* 在startup 程序(head.s)中就是這樣假設(shè)的。 *//* * 此時(shí)中斷仍被禁止著,做完必要的設(shè)置后就將其開啟。 */
// 下面這段代碼用于保存:
// 根設(shè)備號 -> ROOT_DEV; 高速緩存末端地址 -> buffer_memory_end;
// 機(jī)器內(nèi)存數(shù) -> memory_end;主內(nèi)存開始地址 -> main_memory_start; ROOT_DEV = ORIG_ROOT_DEV; drive_info = DRIVE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10);// 內(nèi)存大小=1Mb 字節(jié)+擴(kuò)展內(nèi)存(k)*1024 字節(jié)。 memory_end &= 0xfffff000; // 忽略不到4Kb(1 頁)的內(nèi)存數(shù)。 if (memory_end > 16*1024*1024) // 如果內(nèi)存超過16Mb,則按16Mb 計(jì)。 memory_end = 16*1024*1024; if (memory_end > 12*1024*1024) // 如果內(nèi)存>12Mb,則設(shè)置緩沖區(qū)末端=4Mb buffer_memory_end = 4*1024*1024; else if (memory_end > 6*1024*1024) // 否則如果內(nèi)存>6Mb,則設(shè)置緩沖區(qū)末端=2Mb buffer_memory_end = 2*1024*1024; else buffer_memory_end = 1*1024*1024;// 否則則設(shè)置緩沖區(qū)末端=1Mb main_memory_start = buffer_memory_end;// 主內(nèi)存起始位置=緩沖區(qū)末端;#ifdef RAMDISK // 如果定義了虛擬盤,則主內(nèi)存將減少。 main_memory_start += rd_init(main_memory_start, RAMDISK*1024);#endif
// 以下是內(nèi)核進(jìn)行所有方面的初始化工作。閱讀時(shí)最好跟著調(diào)用的程序深入進(jìn)去看,實(shí)在看
// 不下去了,就先放一放,看下一個(gè)初始化調(diào)用-- 這是經(jīng)驗(yàn)之談:) mem_init(main_memory_start,memory_end); trap_init(); // 陷阱門(硬件中斷向量)初始化。(kernel/traps.c) blk_dev_init(); // 塊設(shè)備初始化。(kernel/blk_dev/ll_rw_blk.c) chr_dev_init(); // 字符設(shè)備初始化。(kernel/chr_dev/tty_io.c)空,為以后擴(kuò)展做準(zhǔn)備。 tty_init(); // tty 初始化。(kernel/chr_dev/tty_io.c) time_init(); // 設(shè)置開機(jī)啟動(dòng)時(shí)間 -> startup_time。 sched_init(); // 調(diào)度程序初始化(加載了任務(wù)0 的tr, ldtr) (kernel/sched.c) buffer_init(buffer_memory_end);// 緩沖管理初始化,建內(nèi)存鏈表等。(fs/buffer.c) hd_init(); // 硬盤初始化。(kernel/blk_dev/hd.c) floppy_init(); // 軟驅(qū)初始化。(kernel/blk_dev/floppy.c) sti(); // 所有初始化工作都做完了,開啟中斷。
// 下面過程通過在堆棧中設(shè)置的參數(shù),利用中斷返回指令切換到任務(wù)0。 move_to_user_mode(); // 移到用戶模式。(include/asm/system.h) if (!fork()) { /* we count on this going ok */ init(); }/* * 注意!! 對于任何其它的任務(wù),'pause()'將意味著我們必須等待收到一個(gè)信號才會返 * 回就緒運(yùn)行態(tài),但任務(wù)0(task0)是唯一的意外情況(參見'schedule()'),因?yàn)槿?/span> * 務(wù)0 在任何空閑時(shí)間里都會被激活(當(dāng)沒有其它任務(wù)在運(yùn)行時(shí)), * 因此對于任務(wù)0'pause()'僅意味著我們返回來查看是否有其它任務(wù)可以運(yùn)行,如果沒 * 有的話我們就回到這里,一直循環(huán)執(zhí)行'pause()'。 */ for(;;) pause();} // end main
static int printf(const char *fmt, ...)// 產(chǎn)生格式化信息并輸出到標(biāo)準(zhǔn)輸出設(shè)備stdout(1),這里是指屏幕上顯示。參數(shù)'*fmt'
// 指定輸出將采用的格式,參見各種標(biāo)準(zhǔn)C 語言書籍。該子程序正好是vsprintf 如何使
// 用的一個(gè)例子。
// 該程序使用vsprintf()將格式化的字符串放入printbuf 緩沖區(qū),然后用write()
// 將緩沖區(qū)的內(nèi)容輸出到標(biāo)準(zhǔn)設(shè)備(1--stdout)。
{ va_list args; int i; va_start(args, fmt); write(1,printbuf,i=vsprintf(printbuf, fmt, args)); va_end(args); return i;}static char * argv_rc[] = { "/bin/sh", NULL };// 調(diào)用執(zhí)行程序時(shí)參數(shù)的字符串?dāng)?shù)組。static char * envp_rc[] = { "HOME=/", NULL };// 調(diào)用執(zhí)行程序時(shí)的環(huán)境字符串?dāng)?shù)組。static char * argv[] = { "-/bin/sh",NULL }; // 同上。static char * envp[] = { "HOME=/usr/root", NULL };void init(void){ int pid,i;
// 讀取硬盤參數(shù)包括分區(qū)表信息并建立虛擬盤和安裝根文件系統(tǒng)設(shè)備。
// 該函數(shù)是在25 行上的宏定義的,對應(yīng)函數(shù)是sys_setup(),在kernel/blk_drv/hd.c。 setup((void *) &drive_info);
(void) open("/dev/tty0",O_RDWR,0); // 用讀寫訪問方式打開設(shè)備“/dev/tty0”,
// 這里對應(yīng)終端控制臺。
// 返回的句柄號0 -- stdin 標(biāo)準(zhǔn)輸入設(shè)備。 (void) dup(0); // 復(fù)制句柄,產(chǎn)生句柄1 號-- stdout 標(biāo)準(zhǔn)輸出設(shè)備。 (void) dup(0); // 復(fù)制句柄,產(chǎn)生句柄2 號-- stderr 標(biāo)準(zhǔn)出錯(cuò)輸出設(shè)備。 printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS, \ NR_BUFFERS*BLOCK_SIZE); // 打印緩沖區(qū)塊數(shù)和總字節(jié)數(shù),每塊1024 字節(jié)。 printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);//空閑內(nèi)存字節(jié)數(shù)。
// 下面fork()用于創(chuàng)建一個(gè)子進(jìn)程(子任務(wù))。對于被創(chuàng)建的子進(jìn)程,fork()將返回0 值,
// 對于原(父進(jìn)程)將返回子進(jìn)程的進(jìn)程號。所以if (!(pid=fork())) {...} 內(nèi)是子進(jìn)程執(zhí)行的內(nèi)容。
// 該子進(jìn)程關(guān)閉了句柄0(stdin),以只讀方式打開/etc/rc 文件,并執(zhí)行/bin/sh 程序,所帶參數(shù)和
// 環(huán)境變量分別由argv_rc 和envp_rc 數(shù)組給出。參見后面的描述。 if (!(pid=fork())) { close(0); if (open("/etc/rc",O_RDONLY,0)) _exit(1); // 如果打開文件失敗,則退出(/lib/_exit.c)。 execve("/bin/sh",argv_rc,envp_rc); // 裝入/bin/sh 程序并執(zhí)行。(/lib/execve.c) _exit(2); // 若execve()執(zhí)行失敗則退出(出錯(cuò)碼2,“文件或目錄不存在”)。 }
// 下面是父進(jìn)程執(zhí)行的語句。wait()是等待子進(jìn)程停止或終止,其返回值應(yīng)是子進(jìn)程的
// 進(jìn)程號(pid)。這三句的作用是父進(jìn)程等待子進(jìn)程的結(jié)束。&i 是存放返回狀態(tài)信息的
// 位置。如果wait()返回值不等于子進(jìn)程號,則繼續(xù)等待。 if (pid>0) while (pid != wait(&i)) { /* nothing */;}
// --
// 如果執(zhí)行到這里,說明剛創(chuàng)建的子進(jìn)程的執(zhí)行已停止或終止了。下面循環(huán)中首先再創(chuàng)建
// 一個(gè)子進(jìn)程,如果出錯(cuò),則顯示“初始化程序創(chuàng)建子進(jìn)程失敗”的信息并繼續(xù)執(zhí)行。對
// 于所創(chuàng)建的子進(jìn)程關(guān)閉所有以前還遺留的句柄(stdin, stdout, stderr),新創(chuàng)建一個(gè)
// 會話并設(shè)置進(jìn)程組號,然后重新打開/dev/tty0 作為stdin,并復(fù)制成stdout 和stderr。
// 再次執(zhí)行系統(tǒng)解釋程序/bin/sh。但這次執(zhí)行所選用的參數(shù)和環(huán)境數(shù)組另選了一套(見上面)。
// 然后父進(jìn)程再次運(yùn)行wait()等待。如果子進(jìn)程又停止了執(zhí)行,則在標(biāo)準(zhǔn)輸出上顯示出錯(cuò)信息
// “子進(jìn)程pid 停止了運(yùn)行,返回碼是i”,
// 然后繼續(xù)重試下去…,形成“大”死循環(huán)。 while (1) { if ((pid=fork())<0) { printf("Fork failed in init\r\n"); continue; } if (!pid) { close(0);close(1);close(2); setsid(); (void) open("/dev/tty0",O_RDWR,0); (void) dup(0); (void) dup(0); _exit(execve("/bin/sh",argv,envp)); } while (1) if (pid == wait(&i)) break; printf("\n\rchild %d died with code %04x\n\r",pid,i); sync(); } _exit(0); /* NOTE! _exit, not exit() */}
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -