init/main.c源码分析

本文深入分析了Linux内核`init/main.c`中的任务数据结构和任务状态段设置,特别是如何为新任务分配内存、初始化堆栈段以及在不同权限级别之间的返回流程。通过对IRET指令的探讨,揭示了在任务切换过程中如何避免任务切换并确保执行`init_task`的过程。
摘要由CSDN通过智能技术生成
前面我们说到head最终调用了main.c的main方法。现在我们就来看一些这个文件的源码。
  1 /*
  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);
我们来看一下符号定义
 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;
对齐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。
122     main_memory_start = buffer_memory_end;
主内存区域在缓冲区上面。缓冲区下面是内核代码区。如下图

123 #ifdef RAMDISK
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行:
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 }

 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))

 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)

这段代码大致一看就知道是在idt中设置门描述符的。
181 void trap_init(void)
182 {
183     int i;
184
185     set_trap_gate(0,&divide_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,&parallel_interrupt);
设置并行口1的中断0x27陷阱门描述符
208 }
因为我们之前在setup中临时设置了idtr,又在head中具体分配了内存,但是那时还没有具体设置值,以上就是为其设置具体的值。
1.上面dpl=0的是中断门,dpl=3的是陷阱门?
不是这样划分的,中断门、陷阱门都属于系统段。它们是通过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进行等待。
继续 129     chr_dev_init();
kernel/chr_drv/tty_io.c
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值