之前分析了head.s到main.c的跳转,下面看看mian.c的工作流程分析。
boot之后的执行代码是init目录下的main.c,执行初始化任务。研究一下
linus一开头就给出了四个内联静态函数
static inline _syscall0(int,fork)
static inline _syscall0(int,pause)
static inline _syscall1(int,setup,void *,BIOS)
static inline _syscall0(int,sync)
这个宏定义在include/unistd.h中
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name)); \
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
return -1; \
}
将第一个函数宏展开
_syscall0(int,fork)=>
int fork(void)
{
long __res;
__asm__ volatile ("int $0x80" \
:"=a"(__res)\
:"0"(__NR_fork));\
if(__res>=0) \
return (int) __res;\
errno = -__res;\
return -1;\
}
从main函数开始分析
1、
ROOT_DEV = ORIG_ROOT_DEV;
drive_info = DRIVE_INFO;
完成了根文件系统的设备号的一些问题,先跳过分析
2、
memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;
if (memory_end > 16*1024*1024)
memory_end = 16*1024*1024;
if (memory_end > 12*1024*1024)
buffer_memory_end = 4*1024*1024;
else if (memory_end > 6*1024*1024)
buffer_memory_end = 2*1024*1024;
else
buffer_memory_end = 1*1024*1024;
main_memory_start = buffer_memory_end;
根据机器物理内存设置高速缓存区和主内存区的位置和范围
其中
#define EXT_MEM_K (*(unsigned short *)0x90002)
关于物理内存大小呀、扩展大小呀,都是在setup阶段获取的,并将这些参数存储在0x90000开始的内存中
Setup 程序读取并保留的参数
—————————————————————————-
内存地址 长度(字节) 名称描述
—————————————————————————-
0x90000 2 光标位置列号(0x00-最左端),行号(0x00-最顶端)
0x90002 2 扩展内存数系统从1M 开始的扩展内存数值(KB)。
0x90004 2 显示页面当前显示页面
0x90006 1 显示模式
0x90007 1 字符列数
0x90008 2 ??
0x9000A 1 显示内存显示内存(0x00-64k,0x01-128k,0x02-192k,0x03=256k)
0x9000B 1 显示状态0x00-彩色,I/O=0x3dX;0x11-单色,I/O=0x3bX
0x9000C 2 特性参数显示卡特性参数
0x90080 16 硬盘参数表第1 个硬盘的参数表
0x90090 16 硬盘参数表第2 个硬盘的参数表(如果没有,则清零)
0x901FC 2 根设备号根文件系统所在的设备号(bootsec.s 中设置)
—————————————————————————-
从上表中可以看出,取两个字节为扩展内存数
memory_end = (1<<20) + (EXT_MEM_K<<10);
因为内核代码段(内存)映射到从物理内存地址0开始,所以可以根据内存大小获取内存开始和结束地址
1<<20 为1M,EXT_MEM_K<<10为从地址0开始EXT_MEM_K大小的结束地址,全部转换成字节。
memory_end &= 0xfffff000;
0xfff是4k大小,即将不满4k的忽略
if (memory_end > 16*1024*1024)
memory_end = 16*1024*1024;
16×1024×1024为16M,内存大小大于16M的也忽略,所以在linux0.12使用时,最大支持的内存就是16M,大于这个内存也不起作用
if (memory_end > 12*1024*1024)
buffer_memory_end = 4*1024*1024;
else if (memory_end > 6*1024*1024)
buffer_memory_end = 2*1024*1024;
else
buffer_memory_end = 1*1024*1024;
完成名为buffer内存区的设置,根据总内存的大小来进行分配
main_memory_start = buffer_memory_end;
在buffer内存区之后全部为main的缓存区。
到这里看看linus把内存区怎么去规划的
在这里linus还没有给虚拟盘划分空间
#ifdef RAMDISK
main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif
这里使用条件编译,为虚拟盘在主内存中划分空间。
在kernel/blk_drv/ramdisk.c中Written by Theodore Ts’o给出了这个函数
/*
* Returns amount of memory which needs to be reserved.
*/
long rd_init(long mem_start, int length)
{
int i;
char *cp;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
rd_start = (char *) mem_start;
rd_length = length;
cp = rd_start;
for (i=0; i < length; i++)
*cp++ = '\0';
return(length);
}
这里关于blk_dev暂不讨论,这个函数两个功能,初始化更虚拟盘相关的参数,并返回虚拟盘占用的字节数,将main的内存区后移。
3、
mem_init(main_memory_start,memory_end);
trap_init();
blk_dev_init();
chr_dev_init();
tty_init();
time_init();
sched_init();
buffer_init(buffer_memory_end);
hd_init();
floppy_init();
sti();
【为此研究一下内核内存管理部分的内容,看看mem_init做了些什么,见另一篇文章】
第二个初始化,块设备初始化
第三个初始化,字符设备初始化
第四个初始化,tty初始化
第五个初始化,设置开机启动时间
第六个初始化,调度程序初始化
第七个初始化,缓冲管理初始化
第八个初始化,硬盘初始化
第九个初始化,软驱初始化
第十个为开启中断
【上面每个会有一篇文章作为说明,这里暂不讨论】
4、
move_to_user_mode();
移到用户模式下执行。
5、
if (!fork()) { /* we count on this going ok */
init();
}
具体怎么进行fork暂不研究,init的主要任务就是对将要执行shell环境初始化,并执行shell
6、
for(;;) pause();
main会在此循环,这是任务0,永不停息
大致总结一下mian.c的工作,后面文章会详细说明