linux-0.11之main.c初學記錄

linux內核啓動初始化流程圖如下


源代碼分析:

/*

 *  linux/init/main.c
 *
 *  (C) 1991  Linus Torvalds
 */


#define __LIBRARY__ /*huang:宏定義,在unistd.h中會使用到*/
#include <unistd.h> /*huang:系統調用函數包含*/
#include <time.h> /*huang:時間類型頭文件*/


/*
 * we need this inline - forking from kernel space will result
 * in NO COPY ON WRITE (!!!), until an execve is executed. This
 * is no problem, but for the stack. This is handled by not letting
 * main() use the stack at all after fork(). Thus, no function
 * calls - which means inline code for fork too, as otherwise we
 * would use the stack upon exit from 'fork()'.
 *
 * Actually only pause and fork are needed inline, so that there
 * won't be any messing with the stack from main(), but we define
 * some others too.
 */
 /*huang:主要講述了系統調用fork()和pause()使用的內嵌代碼模塊,不會弄亂main中堆棧*/


/*huang:聲明系統調用,使用內聯方式*/
static inline _syscall0(int,fork) /*fork系統調用*/
static inline _syscall0(int,pause) /*pause系統調用,暫停執行進程,直到接收到一個信號*/
static inline _syscall1(int,setup,void *,BIOS) /*setup系統調用,用於linux初始化*/
static inline _syscall0(int,sync)main /*sync系統調用,用於更新文件系統*/


#include <linux/tty.h> /*包含tty頭文件,是串行通信參數與常數*/
#include <linux/sched.h> /*包含任務調度程序頭文件*/
#include <linux/head.h> /*包含head頭文件,定義了段描述符的簡單結構*/
#include <asm/system.h> /*系統頭文件,包含描述符、中斷嵌入彙編子程序*/
#include <asm/io.h> /*io頭文件,以宏的嵌入彙編程序形式定義對io端口的操作函數*/


#include <stddef.h> /*標準定義頭文件,NULL/offsetof*/
#include <stdarg.h> /*標準參數頭文件,va_list、va_start、va_end、va_arg...*/
#include <unistd.h> /*系統調用函數包含*/
#include <fcntl.h> /*文件控制頭文件*/
#include <sys/types.h> /*數據類型頭文件*/


#include <linux/fs.h> /*文件系統頭文件*/


static char printbuf[1024]; /*靜態字符串數組,用作內核顯示緩衝*/


/*huang:函數聲明*/
extern int vsprintf(); /將格式化輸出到一字符串中*/
extern void init(void); /*初始化*/
extern void blk_dev_init(void); /*塊設備初始化子程序*/
extern void chr_dev_init(void); /*字符設備初始化*/
extern void hd_init(void); /*硬盤初始化*/
extern void floppy_init(void); /*軟驅初始化*/
extern void mem_init(long start, long end); /*內存管理初始化*/
extern long rd_init(long mem_start, int length); /*虛擬盤初始化*/
extern long kernel_mktime(struct tm * tm); /*計算系統開機啓動時間*/
extern long startup_time; /*內核啓動時間*/


/*
 * This is set up by the setup-routine at boot-time
 */
 /*以下這些數據是在內核引導期間由錽etup.s程序設置的*/
#define EXT_MEM_K (*(unsigned short *)0x90002)      /*1MB以後的擴展內存大小*/
#define DRIVE_INFO (*(struct drive_info *)0x90080)  /*:磁盤參數信息*/
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC) /*根文件的設備號*/


/*
 * Yeah, yeah, it's ugly, but I cannot find how to do this correctly
 * and this seems to work. I anybody has more info on the real-time
 * clock I'd be interested. Most of this was trial and error, and some
 * bios-listing reading. Urghh.
 */
/*這段宏讀取CMOS實時時鐘信息*/
#define CMOS_READ(addr) ({ \
outb_p(0x80|addr,0x70); \ /*0x80|addr是要讀取的CMOS內存地址,0x70是寫地址端口號*/
inb_p(0x71); \ /*0x71是讀數據端口號*/
})
/*宏定義,將BCD碼轉爲二進制碼*/
#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)


/*讀取CMOS時鐘信息作爲開機時間,並保存到startup_time*/
static void time_init(void)
{
struct tm time; /*時間保存到time*/
/*讀取CMOS時間*/
do {
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->二進制*/
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; /*機器具有的物理內存容量*/
static long buffer_memory_end = 0; /*高速緩存區末端地址*/
static long main_memory_start = 0; /*主內存開始的地址*/


struct drive_info { char dummy[32]; } drive_info;/*用於存儲磁盤信息*/


/*內核初始化主程序。初始化結束後將以任務0的身份運行*/
void main(void) /* This really IS void, no error here. */
{ /* The startup routine assumes (well, ...) this */
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
 /*中斷是禁止的,以下設置完纔開啓*/


  ROOT_DEV = ORIG_ROOT_DEV; /*跟設備號*/
  drive_info = DRIVE_INFO; /*磁盤參數信息*/
memory_end = (1<<20) + (EXT_MEM_K<<10); /*內存1Mb+(擴展內存K)*1024字節*/
memory_end &= 0xfffff000; /*忽略最多不大於4Kb的內存*/
if (memory_end > 16*1024*1024) /*內存大於16Mb 按照16Mb計算*/
memory_end = 16*1024*1024;
if (memory_end > 12*1024*1024)   /*內存大於12Mb 則設置緩存區末端4Mb*/
buffer_memory_end = 4*1024*1024;
else if (memory_end > 6*1024*1024) /*內存大於6Mb 則設置緩存區末端2Mb*/
buffer_memory_end = 2*1024*1024;
else
buffer_memory_end = 1*1024*1024; /*否則設置緩存區末端1Mb*/
main_memory_start = buffer_memory_end; /*主內存開始地址設置爲緩衝區末端*/


/*Makefile文件中定義了內存虛擬盤符號RAMDISK,則初始化虛擬盤*/
#ifdef RAMDISK
main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif
/*內核初始化主要工作*/
mem_init(main_memory_start,memory_end); /*主存區初始化*/
trap_init();   /*陷阱門(硬件中斷向量)初始化*/
blk_dev_init(); /*塊設備初始化*/
chr_dev_init(); /*字符設備初始化*/
tty_init(); /*tty初始化*/
time_init(); /*設置開機啓動時間*/
sched_init(); /*調度程序初始化*/
buffer_init(buffer_memory_end); /*緩衝管理初始化,建內存鏈表*/
hd_init(); /*硬盤初始化*/
floppy_init(); /*軟盤初始化*/
sti(); /*所有初始化完畢,開啓中斷*/
/*huang:通過在堆棧中的參數設置,利用中斷返回指令啓動任務0執行*/
move_to_user_mode(); /*移到用戶模式下執行*/
if (!fork()) { /* we count on this going ok */
init(); /*在新建的子進程(任務1)中執行*/
}
/*以下都將以任務0的身份運行*/
/*
 *   NOTE!!   For any other task 'pause()' would mean we have to get a
 * signal to awaken, but task0 is the sole exception (see 'schedule()')
 * as task 0 gets activated at every idle moment (when no other tasks
 * can run). For task0 'pause()' just means we go check if some other
 * task can run, and if not we return here.
 */
 /*注意!對於任何其他任務,pause()將意味着我們必須等待接收到一個信號纔會返回就緒態,但任務0
 *是唯一例外情況。因爲任務0在任何空閒時間裏都會被激活(當沒有其他任務在運行時),因此對於任務0
 *pause()僅意味着我們返回來查看是否其他任務可以運行,如果沒有的話我們就回到這裏,一直循環執行pause()
 */
 
for(;;) pause();
/*pause()系統調用會把任務0轉換成可中斷等待狀態,再執行調度函數。
*但是調度函數只要發現系統中沒有其他任務可以執行時就會切換到任務0,而不
*依賴於任務0的狀態。
*/
}


/*下面函數產生格式化信息並輸出到標準輸出中*/
static int printf(const char *fmt, ...)
{
va_list args;
int i;


va_start(args, fmt);
/*使用vsprintf()將格式化的字符串放入printbuf緩衝區,然後write輸出到stdout*/
write(1,printbuf,i=vsprintf(printbuf, fmt, args));
va_end(args);
return i;
}


/*讀取並執行/etc/rc文件時所使用的命令行參數和環境參數*/
static char * argv_rc[] = { "/bin/sh", NULL };
static char * envp_rc[] = { "HOME=/", NULL };


/*運行登錄shell時所使用的命令行參數和環境參數*/
static char * argv[] = { "-/bin/sh",NULL };
static char * envp[] = { "HOME=/usr/root", NULL };


/*init是在任務0第一次創建子進程時,首先初始化shell,然後登錄shell並加載*/
void init(void)
{
int pid,i;
/*讀取硬盤參數包含分區表信息並加載虛擬盤和安裝根文件系統設備*/
setup((void *) &drive_info); /*drive_info是兩個硬盤參數表*/
(void) open("/dev/tty0",O_RDWR,0); /*讀寫方式訪問/dev/tty0 */
(void) dup(0); /*stdout*/
(void) dup(0); /*stderr*/
/*打印緩衝區數據塊數和總字節數,每塊是1024字節,以及主內存空閒內存字節數*/
printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
NR_BUFFERS*BLOCK_SIZE);
printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);

if (!(pid=fork())) { /*創建子進程 1*/
close(0); /*關閉stdin*/
if (open("/etc/rc",O_RDONLY,0)) /*以只讀方式打開/etc/rc*/
_exit(1);
execve("/bin/sh",argv_rc,envp_rc); /*進程替換爲shell*/
_exit(2);
}
if (pid>0)
while (pid != wait(&i)) /*等待子進程1結束,空循環*/
/* nothing */;
/*init進程中的大循環*/
while (1) {
if ((pid=fork())<0) { /*創建新子進程*/
printf("Fork failed in init\r\n");
continue;
}
if (!pid) { /*創建子進程2*/
close(0);close(1);close(2); /*關閉stdin, stdout,stderr*/
setsid(); /*創建新一期會話期*/
(void) open("/dev/tty0",O_RDWR,0); /*讀寫方式訪問/dev/tty0(stdin) */
(void) dup(0); /*stdout(複製)*/
(void) dup(0); /*stderr(複製)*/
_exit(execve("/bin/sh",argv,envp));
}
while (1)
if (pid == wait(&i)) /*等待子進程2結束*/
break;
printf("\n\rchild %d died with code %04x\n\r",pid,i);
sync(); /*刷新緩衝區*/
}
_exit(0); /* NOTE! _exit, not exit() */   /*_exit()是系統調用,exit是庫函數調用*/
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章