操作系统全部笔记目录 见:操作系统笔记整理
注意我们程序执行时就是按照首先读第一个扇区,然后第一个扇区的bootset继续读后面的扇区,所以代码的安排必须要合理:
需要用makefile来自己设计编译细节。设计哪个放在前面,哪个放在后面。编译出来以后,操作系统叫image,一定要符合上图的样子。我们需要写到0磁道0扇区。
makefile如下:
disk: Image
dd bs=8192 if=Image of=/dev/PS0
Image: boot/bootsect boot/setup tools/system tools/build
tools/build boot/bootsect boot/setup tools/system > Image
tools/system: boot/head.o init/main.o $(DRIVERS) …
$(LD) boot/head.o init/main.o $(DRIVERS) … -o tools/system
生成Image需要很多依赖,依赖有了以后,就把这些东西合成到Image镜像里。同时这些依赖也分别要依赖其他项。注意上面的$(DRIVERS)表示驱动程序。
system的第一个部分就是head.s。
注意head.s是32位保护模式,所以要执行32位的汇编(和原来的汇编不一样了):
stratup_32: movl $0x10,%eax mov %ax,%ds mov %ax,%es
mov %as,%fs mov %as,%gs //指向gdt的0x10项(数据段)
lss _stack_start,%esp //设置栈(系统栈)
call setup_idt
call setup_gdt
xorl %eax,%eax
1:incl %eax
movl %eax,0x000000 cmpl %eax,0x100000
je 1b //0地址处和1M地址处相同(A20没开启), 就死循环
jmp after_page_tables //页表, 什么东东?
setup_idt: lea ignore_int,%edx
movl $0x00080000,%eax movw %dx,%ax
lea _idt,%edi movl %eax,(%edi)
主要工作就是重新设置idt和gdt表。因为之前的这个表是为了之前操作使用的,现在操作系统需要真正控制硬件了。
内嵌汇编就是C语言文件中,有些指令必须使用汇编来严格控制执行。
head.s文件执行完以后,要跳转到main.c。它的做法是:
after_page_tables:
pushl $0 pushl $0 pushl $0 pushl $L6
pushl $_main jmp set_paging
L6: jmp L6
setup_paging: 设置页表 ret
setup_paging执行ret后,会执行函数main()
进入main()后的栈为0, 0, 0, L6
main()函数的三个参数是0, 0, 0
main()函数返回时进入L6, L6是个死循环,即死机了。所以main永远不会返回(main里面也是不断循环)。
jmp set_paging以后,就跳到 set_paging ,然后设置页表,并执行ret,弹栈,main出栈,执行main函数。
main函数:
void main(void)
{
mem_init();
trap_init();
blk_dev_init();
chr_dev_init();
tty_init();
time_init();
sched_init();
buffer_init();
hd_init();
floppy_init();
sti();
move_to_user_mode();
if(!fork()){init();}
}
三个参数分别是envp,argv,argc,但此处main并没使用,此处的main只保留传统main的形式和命名,main表示C语言函数的入口。main的工作就是xx_init: 内存、 中断、 设备、时钟、 CPU等内容的初始化。
我们取出内存初始化程序来查看一下:(在linux/mm/memory.c中)
void mem_init(long start_mem,long end_mem)
{
int i;
for(i=0; i<PAGING_PAGES; i++)
mem_map[i] = USED;
i = MAP_NR(start_mem);
end_mem -= start_mem;
end_mem >>= 12;
while(end_mem -- > 0)
mem_map[i++] = 0; }
end_mem >>= 12; 左移12表示除以2的12次方,即4K,也就是每4K分为1页。
注意start_mem和end_mem,回想之前学的内容,扩展内存的大小保存在0x90002的地方 。
前面使用的区域是操作系统的内存,后面是未使用区。通过执行内存初始化函数,我们就分出了用过的和空白的内存区域。