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是库函数调用*/
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章