前面我们说到head最终调用了main.c的main方法。现在我们就来看一些这个文件的源码。
1 /*
2 * linux/init/main.c
3 *
4 * (C) 1991 Linus Torvalds
5 */
我们来看一下main函数
2 * linux/init/main.c
3 *
4 * (C) 1991 Linus Torvalds
5 */
我们来看一下main函数
104 void main(void) /* This really IS void, no error here. */
105 { /* The startup routine assumes (well, ...) this */
106 /*
107 * Interrupts are still disabled. Do necessary setups, then
108 * enable them
109 */
110 ROOT_DEV = ORIG_ROOT_DEV;
111 drive_info = DRIVE_INFO;
112 memory_end = (1<<20) + (EXT_MEM_K<<10);
105 { /* The startup routine assumes (well, ...) this */
106 /*
107 * Interrupts are still disabled. Do necessary setups, then
108 * enable them
109 */
110 ROOT_DEV = ORIG_ROOT_DEV;
111 drive_info = DRIVE_INFO;
112 memory_end = (1<<20) + (EXT_MEM_K<<10);
我们来看一下符号定义
55 /*
56 * This is set up by the setup-routine at boot-time
57 */
58 #define EXT_MEM_K (*(unsigned short *)0x90002)
59 #define DRIVE_INFO (*(struct drive_info *)0x90080)
60 #define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)
这些值是我们在setup中通过询问BIOS获得,并保存的。现在拿来使用。
113 memory_end &= 0xfffff000;
56 * This is set up by the setup-routine at boot-time
57 */
58 #define EXT_MEM_K (*(unsigned short *)0x90002)
59 #define DRIVE_INFO (*(struct drive_info *)0x90080)
60 #define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)
这些值是我们在setup中通过询问BIOS获得,并保存的。现在拿来使用。
113 memory_end &= 0xfffff000;
对齐4k边界
114 if (memory_end > 16*1024*1024)
115 memory_end = 16*1024*1024;
最大16M
116 if (memory_end > 12*1024*1024)
117 buffer_memory_end = 4*1024*1024;
118 else if (memory_end > 6*1024*1024)
119 buffer_memory_end = 2*1024*1024;
120 else
121 buffer_memory_end = 1*1024*1024;
根据当前内存大小设置缓冲区的长度,这里采用4M。
116 if (memory_end > 12*1024*1024)
117 buffer_memory_end = 4*1024*1024;
118 else if (memory_end > 6*1024*1024)
119 buffer_memory_end = 2*1024*1024;
120 else
121 buffer_memory_end = 1*1024*1024;
根据当前内存大小设置缓冲区的长度,这里采用4M。
122 main_memory_start = buffer_memory_end;
主内存区域在缓冲区上面。缓冲区下面是内核代码区。如下图
123 #ifdef RAMDISK
124 main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
125 #endif
124 main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
125 #endif
接下来就是一系列的初始化工作:
126 mem_init(main_memory_start,memory_end);
127 trap_init();
128 blk_dev_init();
129 chr_dev_init();
130 tty_init();
131 time_init();
132 sched_init();
133 buffer_init(buffer_memory_end);
134 hd_init();
135 floppy_init();
136 sti();
我们依次来分析,首先126行:
127 trap_init();
128 blk_dev_init();
129 chr_dev_init();
130 tty_init();
131 time_init();
132 sched_init();
133 buffer_init(buffer_memory_end);
134 hd_init();
135 floppy_init();
136 sti();
我们依次来分析,首先126行:
399 void mem_init(long start_mem, long end_mem)
400 {
401 int i;
402
403 HIGH_MEMORY = end_mem;
404 for (i=0 ; i<PAGING_PAGES ; i++)
405 mem_map[i] = USED;
406 i = MAP_NR(start_mem);
407 end_mem -= start_mem;
408 end_mem >>= 12;
409 while (end_mem-->0)
410 mem_map[i++]=0;
411 }
400 {
401 int i;
402
403 HIGH_MEMORY = end_mem;
404 for (i=0 ; i<PAGING_PAGES ; i++)
405 mem_map[i] = USED;
406 i = MAP_NR(start_mem);
407 end_mem -= start_mem;
408 end_mem >>= 12;
409 while (end_mem-->0)
410 mem_map[i++]=0;
411 }
42 /* these are not to be changed without changing head.s etc */
43 #define LOW_MEM 0x100000
44 #define PAGING_MEMORY (15*1024*1024)
45 #define PAGING_PAGES (PAGING_MEMORY>>12)
46 #define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
47 #define USED 100
57 static unsigned char mem_map [ PAGING_PAGES ] = {0,};
404行 for循环把每个内存页初始化为USED状态,406-410其实是把除内核空间之外的内存页设置为未用状态。
我们再来看下一个初始化工作:127 trap_init();
kernel/traps.c:
在此之前先看一下用到的函数
include/asm/system.h:
22 #define _set_gate(gate_addr,type,dpl,addr) \
23 __asm__ ("movw %%dx,%%ax\n\t" \
24 "movw %0,%%dx\n\t" \
25 "movl %%eax,%1\n\t" \
26 "movl %%edx,%2" \
27 : \
28 : "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
29 "o" (*((char *) (gate_addr))), \
30 "o" (*(4+(char *) (gate_addr))), \
31 "d" ((char *) (addr)),"a" (0x00080000))
23 __asm__ ("movw %%dx,%%ax\n\t" \
24 "movw %0,%%dx\n\t" \
25 "movl %%eax,%1\n\t" \
26 "movl %%edx,%2" \
27 : \
28 : "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
29 "o" (*((char *) (gate_addr))), \
30 "o" (*(4+(char *) (gate_addr))), \
31 "d" ((char *) (addr)),"a" (0x00080000))
36 #define set_trap_gate(n,addr) \
37 _set_gate(&idt[n],15,0,addr)
39 #define set_system_gate(n,addr) \
40 _set_gate(&idt[n],15,3,addr)
40 _set_gate(&idt[n],15,3,addr)
这段代码大致一看就知道是在idt中设置门描述符的。
181 void trap_init(void)
182 {
183 int i;
184
185 set_trap_gate(0,÷_error);
186 set_trap_gate(1,&debug);
187 set_trap_gate(2,&nmi);
182 {
183 int i;
184
185 set_trap_gate(0,÷_error);
186 set_trap_gate(1,&debug);
187 set_trap_gate(2,&nmi);
因此这几句就是设置了0-2号陷阱门描述符,只要注意这里的dpl=0(上面37行),也就是说是系统级。
188 set_system_gate(3,&int3); /* int3-5 can be called from all */
189 set_system_gate(4,&overflow);
190 set_system_gate(5,&bounds);
这里dpl=3,也就是用户级。
191 set_trap_gate(6,&invalid_op);
192 set_trap_gate(7,&device_not_available);
193 set_trap_gate(8,&double_fault);
194 set_trap_gate(9,&coprocessor_segment_overrun);
195 set_trap_gate(10,&invalid_TSS);
196 set_trap_gate(11,&segment_not_present);
197 set_trap_gate(12,&stack_segment);
198 set_trap_gate(13,&general_protection);
199 set_trap_gate(14,&page_fault);
200 set_trap_gate(15,&reserved);
201 set_trap_gate(16,&coprocessor_error);
202 for (i=17;i<48;i++)
203 set_trap_gate(i,&reserved);
204 set_trap_gate(45,&irq13);
205 outb_p(inb_p(0x21)&0xfb,0x21);
206 outb(inb_p(0xA1)&0xdf,0xA1);
允许8259A主芯片IRQ2中断,允许8259A从芯片IRQ13中断
207 set_trap_gate(39,¶llel_interrupt);
设置并行口1的中断0x27陷阱门描述符
208 }
因为我们之前在setup中临时设置了idtr,又在head中具体分配了内存,但是那时还没有具体设置值,以上就是为其设置具体的值。
208 }
因为我们之前在setup中临时设置了idtr,又在head中具体分配了内存,但是那时还没有具体设置值,以上就是为其设置具体的值。
1.上面dpl=0的是中断门,dpl=3的是陷阱门?
不是这样划分的,中断门、陷阱门都属于系统段。它们是通过type值来区分的,如下图
通过上图可以看到中断门type = 14(1110),陷阱门type =15(1111)。另外,中断门会关中断,陷阱门不会,这是二者的唯一区别。
不是这样划分的,中断门、陷阱门都属于系统段。它们是通过type值来区分的,如下图
通过上图可以看到中断门type = 14(1110),陷阱门type =15(1111)。另外,中断门会关中断,陷阱门不会,这是二者的唯一区别。
继续128 blk_dev_init();
kernel/blk_drv/ll_rw_blk.c:
157 void blk_dev_init(void)
158 {
159 int i;
160
161 for (i=0 ; i<NR_REQUEST ; i++) {
162 request[i].dev = -1;
163 request[i].next = NULL;
164 }
165 }
看来这里主要是把请求队列初始化。同一时间可能会有不同的进程等待读写磁盘,这些读写操作封装成请求,按照电梯调度算法排列在request数组中,同时使用next连接成链。当数组满了之后,下一步请求的进程会执行sleep_on进行等待。
158 {
159 int i;
160
161 for (i=0 ; i<NR_REQUEST ; i++) {
162 request[i].dev = -1;
163 request[i].next = NULL;
164 }
165 }
看来这里主要是把请求队列初始化。同一时间可能会有不同的进程等待读写磁盘,这些读写操作封装成请求,按照电梯调度算法排列在request数组中,同时使用next连接成链。当数组满了之后,下一步请求的进程会执行sleep_on进行等待。
继续 129 chr_dev_init();
kernel/chr_drv/tty_io.c