Linux操作系统总结

一、Linux的启动

1、初始化系统文件

在讨论Linux的各项内容之前,我们先要将Linux运行起来。Linux的启动过程中主要是要进行内核根文件系统的挂载,有了内核系统根文件才能访问到所有其他文件。其加载过程为:首先进行内核参数处理,从BIOS寄存器读取必要信息和参数,setup_arch() 函数完成参数的获取和存储。然后进行根文件系统的挂载:(1)挂载一个虚拟的挂载点rootfs2)将root作为0号进程的根目录,这样root就是所有进程的父进程(3)将这个实际的根目录挂载到根文件挂载点上。至此,根文件系统就完成了挂载。

随后系统会执行kernel_start( )函数,里面调用initcall( )函数,进行一系列的初始化动作。至此,Linux的初始化工作完成,下面简单介绍一个具体功能初始化的过程。

2、时钟初始化

Linux使用两个时钟源,一个定时中断源(PIT),一个计时时钟源(TSC),中断源产生中断,计时源用于更新时间,如果只用其一,都会产生一些问题。前面说了在start_kernel( )函数中进行了许多进程的初始化,这里只具体分析一下时钟的初始化过程。

计时时钟源数据结构:(1)身份标识(2jiffies变量:开机以来的时间(3Xtime:当前墙上时间。两个变量由中断时间源决定何时改变。

Linux内核中有个时钟源链表,其中记录了所有注册的时钟源,每次选择其中最精确的时钟源(链表头部)。start_kernel( )函数一开始会执行initcall( )把所有初始化相关的都执行一遍,包括时钟源的初始化clocksource_register( ),执行完后时钟源链表中就有了pit时钟源。

定时时钟源(pit)中断处理程序运行时,实际上就触发了两个函数,用于更新墙上时间和开机以来的时间,随后当前进程计时时间片减一(涉及到时间片轮转)。

二、进程相关

1、调度

对于进程的调度,有一个十分主要的数据结构称为可运行队列。

 

如图,array中是一个结构体,包含链表数、位图、140个优先级

array[0]是过期进程:进程刚刚被运行过

array[1]是活跃进程:处于就绪态的进程

先调用活跃进程(找进程优先级最高的),如果活跃的为空,则把过期的进程全部复制到活跃里,继续执行。实际上过期的进程也是可执行进程,比如时间片到了但未执行完的进程。

调度过程:先看活跃进程中进程数是否为0,如果为0则将过期进程中进程都拷贝过来,然后查找位图找到优先级最高的进程链表,取链表头作为下一个执行线程。

进程调度时机:

(1)进程状态变换时

(2)时间片用完

(3)进程系统调用返回到用户态时

(4)中断处理后,进程返回到用户态

Linux的进程调度机制:1)基于优先级调度,实时进程优先级高于普通进程(2)动态优先级,避免饥饿(3)对实时进程和普通进程有不同的调度策略;普通进程基于动态优先级的时间片轮转算法;实时进程先进先出和时间片轮转(实时进程永远都在活跃进程里,不可能进入过期进程)。

如何判断进程种类?

动态优先级<=3*静态优先级/4+28,则是实时进程,否则是普通进程

2、运行

Linux内核的一般执行过程

1A进程运行

2)发生中断

3swapgs保存现场,切换到A进程内核空间(加载内核栈栈顶到rsp寄存器)

4)执行进程调度算法,中断返回前调用schedule函数来完成

5)调用switch_to完成上下文切换

6)恢复上下文

7)此时已切换到进程B运行

3、切换

进程的切换重点在于一些主要寄存器的存储和切换,如RBPRSP的切换。课上讲解了进程切换的主要汇编代码,这里再复习一次:

1"pushq %%rbp\n\t"

rbp中的值压入堆栈中,这步完成来rbp的存储(当前进程栈基地址)

2"movq %%rsp,%0\n\t"

rsp中的值存入thread.sp中,完成rsp的存储

3"movq %2,%%rsp\n\t"

next.thread.sp的值传入rsp中,也就是rsp表示的栈顶指针指向下一个进程的内存区

4"movq $1f,%1\n\t"

$1f存入thread.ip

5"pushq %3\n\t"

thread.ip的值($1f)压入栈中,因为要完成rip的存储切换,但rip不能随意更改,只能通过堆栈操作进行更改,所以使用这样的方法来保存

6"ret\n\t"

pop next.threadrip,执行next.thread也就是$1后面的语句

7"popq %%rbp\n\t"

 pop出的值存入rbp中,也就是rbp指向当前进程的栈底

三、中断和异常

中断和异常的作用和其对操作系统的重要意义就不多赘述了,这里分析一下中断的大致过程。

首先明确中断和异常处理程序只能在内核态运行,且中断处理进程只能占用被中断进程的内存空间,比如A进程中断了C进程,那么A进程只能在C进程的kernel_stack运行。中断处理程序中没有调度程序存在,只有中断可以打断中断的执行。

1、如何引起中断?

IRQ线发送一个信号给中断控制器,控制器将这个信号转换成对应中断向量,将中断向量值发送到data线上同时发送INTR信号告知cpu中断来临,cpu读中断向量。0~31信号是异常,32之后为中断向量

2、硬件级中断过程

中断过程中自动保存的寄存器:ssspflagscseip、硬件出错码

1)获得中断向量i

2)去IDT表中读第i项,拿到段选择符

3)去GDT表中根据段选择符找段描述符(段的详细信息,基地址长度等)

4)比较gdt中代码段的权限是否大于当前cs的权限

5)从(4)中判断,若gdt>cs则是用户态进入内核,gdt=cs则是内核态。如果是用户态就要先转换至内核态再进行运行。步骤为:(1)得到被中断进程内核栈(2)改变cpu指向即改变ssesp寄存器(3)在新栈中保存用户态的栈信息,也就是保存现场的一部分

6)若引起中断的是故障,如前面所说,故障的eip存的是引起故障的地址,那么在处理前就先修改cseip,因为cseip存的是下一条指令地址,处理完之后恢复现场回去再执行一次。

7)保存现场eflagscseip(在被中断进程的内核栈)

8)保存出错码

9)装载中断处理程序的cseip

四、文件系统

文件系统是Linux中最直接可见的一部分,它是计算机组织、存储和保存信息的重要手段。本课中学到的文件系统主要是文件系统的结构以及一些文件操作如openwriteread等的执行过程。

比如此时要进行文件的打开,内核通过cdev字符设备结构体(fileoptarion和设备号)来对设备(本例中是读写磁盘)进行管理调用fileoperation(具体实现函数的集合)时就会执行diskopen,因为在file里定义了具体的实现。

系统调用打开文件执行过程:

(1)open函数判断文件类型,设备文件则用设备文件打开方式

(2)新建一个struct_file结构,里面有fileoperation

(3)fileoperation会和innode中的cdev绑定,cdev中的opretion来自内核注册的op,即执行open时相当于执行open_light

(4)在fd中找一个空闲块添加struct_file结构体链接

(5)返回fd索引号(open结束)

之前以为文件系统就是存储用户要储存的信息的功能,学完之后才发现其用途十分广泛。

五、总结

之前在学习操作系统时大致的了解操作系统的内存管理、文件管理、进程管理、中断等基础知识,但从未从内核源码级别去了解过。这门课让我在课上课下阅读了一定量的Linux内核源码,使我更加直观深入的了解了操作系统的实现。但目前由于我汇编基础较差,代码阅读量不够,许多地方止步于老师讲解的内容,并不能将内容连接成一个整体融会贯通,希望以后能够更进一步,将所学用于实际。

posted @ 2020-07-09 22:52  LLLLX  阅读(741)  评论(0编辑  收藏  举报