-- linux爱好者,业余时间热衷于分析linux内核源码 -- 目前主要研究云计算和虚拟化相关的技术,主要包括libvirt/qemu,openstack,opennebula架构和源码分析。 -- 第五届云计算大会演讲嘉宾 微博:@Marshal-Liu
分类: LINUX
2012-02-28 19:55:28
Linux的启动过程可以分为四个阶段:系统上电阶段, BIOS阶段,引导程序阶段,Linux内核阶段。
(1)系统上电阶段
对于x86体系结构来说,CPU上电后,eip = 0xffff fff0, CPU执行eip指向的指令,通常这是条跳转指令,即跳转到BIOS的入口。
(2)BIOS阶段
BIOS主要完成两个功能:加电自检,即POST(Power On Self Test)和加载内核引导程序,这里特指MBR(Master Boot Record主引导记录区512字节),POST过程主要完成系统硬件的检测,比如内存检测,系统总线检测等。
(3)引导程序阶段
这里说的内核引导程序包括两部分:MBR中的主引导程序和活动分区中的次引导程序。MBR中的主引导程序包含446字节的程序和64字节的磁盘分区表,主引导程序会扫描磁盘分区表,寻找活动的磁盘分区,将活动分区中的次引导程序加载到内存执行,次引导程序负责完成加载内核的任务。
(4)Linux内核阶段
内核被加载到内存后,首先为内核的运行做前期的准备,主要包括以下几个主要步骤:
基本的思想就是在内存中找一块安全的内存区,通常在内核数据段或堆结束的地方,在这段内存中将物理地址0开始的一段内存同时映射虚拟地址空间的0xC000 0000和0x0000 0000 开始的地址处。
Linux系统调用的执行流程可以描述为:
(1) 应用程序调用C库中的函数
(2) C库函数的实现为触发int 0x80系统调用中断,系统调用号在%eax中
(3) 操作系统拿中断向量号0x80查询中断向量表,执行对应的中断处理函数
(4) 中断处理函数拿系统调用号%eax查询系统调用表,执行相应的系统调用
(5) 系统调用执行完成后返回应用程序
三、Linux中断分类和中断处理过程
中断的初始化主要包括:中断向量表的初始化,中断数组的初始化,中断处理函数的注册等。
中断产生和处理过程中硬件执行的操作:
设备通过中断请求线(IRQ线)向中断控制器发送一个中断信号,中断控制器接收到中断信号后,一方面经过译码电路将中断信号转化为中断号,保存在指定的IO ports中,另一方面通过与CPU的INTR管脚直接相连的INT线向CPU发送中断信号;
CPU收到中断信号后,等执行完当前指令,在执行下一条指令前,去检查有无中断处于Pending状态,如果有,通过INTA向中断控制器发送响应信号,并将中断号通过Data Line取回,然后读取IDT表,获得该中断号对应的IDT表中的第i项,然后根据IDT[i]描述符中指定的段选择子,去GDT表中查找,获得中断处理程序的基址,然后用基址+IDT[i]中指定的中断处理程序的偏移地址,来获得中断处理程序的地址,当然,在读取IDT和GDT表时还会有一些安全性检查。
CPU去判断有没有发生特权级的变化(用户态陷入内核态),如果有,通过tr寄存器获得TSS段的基址,从中读取内核态堆栈的ss和esp,此时就将用户态的堆栈切换到了内核态的堆栈,接着将用户态的ss和esp保存在内核堆栈中,如果此中断的类型是故障(fault),则重新修改cs和eip的值为产生中断的这条指令的地址,以便中断处理完成后,重新执行这条指令。然后,在内核栈中保存eflags,cs和eip的值,如果有硬件出错码,也将其保存到堆栈上,最后将cs:eip的值赋值为中断处理函数的基址+IDT[i]的偏移。
从用户态进入中断与从内核态进入中断的堆栈示意图如下。
IDT表的初始化:通过set_intr_gate()和trap_init(),后者用于初始化20个异常和一个系统调用相关的中断0x80。
在init_IRQ()中通过循环调用set_intr_gate(vector,interrupt[i])来初始化vector对应的IDT表项,这里的interrupt[256]数组,通过下面的代码把所有中断的处理函数都定义为统一的入口:common_interrupt。
体系结构无关的中断处理过程: do_IRQ()。
irq_desc数据包含了224个irq_desc_t描述符,每一个中断号对应一个描述符,这个描述符中的action链表,它指向irqaction的数据结构,代表这个中断对应的处理函数,共享同一中断向量的所有的处理函数都挂在这个链表上,通过遍历这个链表,并查是否要是设备注册的处理函数(dev_id)。
软中断(softirq)由内核静态分配,在编译时就确定了,个数有限,目前内核支持8/9个,具有优先级,主要与定时器,网络包的收发,块设备,调度,Tasklet等有关。在特定的点检查有无可执行的软中断:
(1) local_bh_enable重新激活软中断时
(2) do_IRQ完成中断处理过程后
(3) ksoftirqd被唤醒时
Tasklet是I/O驱动程序中实现可延迟函数的首选,建立在软中断HI_SOFTIRQ和TASKLET_SOFTIRQ之上,二者的区别是优先级不同,这两种Tasklet由对应的数组tasklet_vec和tasklet_hi_vec来管理,数组的每一项对应一个CPU,即tasklet是与CPU绑定的。
Workqueue由工作队列和工作者线程两部分构成,工作者线程会定期的扫描工作队列有无可处理的任务,软中断和tasklet运行在中断上下文,所以不可睡眠,workquue运行在进程上下文,可睡眠。