Linux内核学习总结
朱恒志 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”
总结:
如白驹过隙,学期已经过半。
《Linux内核分析》的课程的基础部分已经学完,虽然仅仅短短的半个学期,但也让我受益匪浅。
第一周:计算机工作的进行
http://www.cnblogs.com/20135314ZHU/p/5222946.html
- 复杂又简单的计算机:计算机能实现许多看起来很复杂的功能,计算和处理大量的数据,但是,它实现这些复杂功能是不断地重复大量既定的简单的操作。
- 程序:我们一般做的,是告诉计算机操作的步骤、输入的数据是什么、处理后的结果如何存储。然后,计算机会尽可能忠实地按照程序的顺序将每一个操作步骤的指令取出,变成机器语言,通过软硬件协同工作。
- 汇编语言:汇编语言其实也是机器语言的一种翻译。现在编译器功能优化的比较强大,学习汇编语言真正用的时候并不多,但它是我们分析处理问题的一种角度。能够看懂汇编语句,在分析程序真正执行的流程、单步调试程序的方面都是有帮助的。
第二周:完成一个简单的时间片轮转多道程序内核代码:
http://www.cnblogs.com/20135314ZHU/p/5245234.html
- 操作系统的核心功能就是:进程调度和中断机制,通过与硬件的配合实现多任务处理,再加上上层应用软件的支持,最终变成可以使用户可以很容易操作的计算机系统。
- Linux是一个多进程的操作系统,所以,其他的进程必须等到正在运行的进程空闲CPU后才能运行。当正在运行的进程等待其他的系统资源时,Linux内核将取得CPU的控制权,并将CPU分配给其他正在等待的进程,这就是进程切换。
- 进程切换机制中包含esp的切换、堆栈的切换。从esp可以找到进程的描述符;堆栈中ebp的切换,确定了当前变量空间属于哪个进程。
第三周:跟踪分析Linux内核的启动过程
http://www.cnblogs.com/20135314ZHU/p/5269030.html
- 内核启动过程:
rest_init()
就是一个0号进程,在start_kernel
内核一启动时就一直存在;然后这个0号进程就创建了1号进程kernel_init
,接下来还创建了其他的一些服务类的内核线程如kthreadd。这样整个系统就启动起来了。 - 使用qemu在自己的电脑上运行待续....
第四周:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
http://www.cnblogs.com/20135314ZHU/p/5289206.html
1. 系统调用
- 即便是最简单的程序,在进行输入输出等操作时也会需要调用操作系统所提供的服务,也就是系统调用。
- Linux下的系统调用是通过中断(int 0x80)来实现的。
2. 传递参数
- 在执行int 80指令时,寄存器 eax 中存放的是系统调用的功能号,而传给系统调用的参数则必须按顺序放到寄存器 ebx,ecx,edx,esi,edi 中,当系统调用完成之后,返回值可以在寄存器 eax 中获得。
- Linux 采用的是 C 语言的调用模式,这就意味着所有参数必须以相反的顺序进栈,即最后一个参数先入栈,而第一个参数则最后入栈。
第五周:扒开系统调用的三层皮(下)/给MenuOS增加time和time-asm命令
http://www.cnblogs.com/20135314ZHU/p/5318211.html
1. 保存现场
- 在系统调用时,我们需要SAVE_ALL,用于保存系统调用时的上下文。
- 同样,中断处理的第一步应该也要保存中断程序现场。
- 目的:在中断处理完之后,可以返回到原来被中断的地方,在原有的运行环境下继续正确的执行下去。
2. 确定中断信息
- 在系统调用时,我们需要将系统调用号通过eax传入,通过
sys_call_table
查询到调用的系统调用,然后跳转到相应的程序进行处理。 - 同样,中断处理时系统也需要有一个中断号,通过检索中断向量表,了解中断的类型和设备。
3. 处理中断
- 跳转到相应的中断处理程序后,对中断进行处理。
4. 返回
- 系统调用时最后要restore_all恢复系统调用时的现场,并用iret返回用户态。
- 同样,执行完中断处理程序,内核也要执行特定指令序列,恢复中断时现场,并使得进程回到用户态。
第六周:进程创建
http://www.cnblogs.com/20135314ZHU/p/5344752.html
Linux通过复制父进程来创建一个新进程,通过调用do_ fork来实现并为每个新创建的进程动态地分配一个task_ struct结构。
1. 新进程的开始
-
copy_thread()中:
p->thread.ip = (unsigned long) ret _from _fork;
-
将子进程的ip设置为ret_ form _ fork的首地址,因此子进程是从ret_ from_ fork开始执行的。
2. 执行起点与内核堆栈保证一致
-
在设置子进程的ip之前:
*childregs = *current_ pt_ regs();
-
将父进程的regs参数赋值到子进程的内核堆栈,*childregs的类型为pt_regs,其中存放了SAVE ALL中压入栈的参数。
第七周:
http://www.cnblogs.com/20135314ZHU/p/5365530.html
第八周:进程的切换和一般执行
http://www.cnblogs.com/20135314ZHU/p/5386076.html
- 进程的切换中的关键操作是:切换地址空间、切换内核堆栈、切换内核控制流程和一些必要的寄存器保存和恢复。这些操作并非针对用户代码,切换完成后,也没有立即跑到next的用户空间中执行。用户上下文的保存和恢复是通过中断和异常机制,在内核态和用户态相互切换时才发生的。
- 在调度时机方面,内核线程可以直接调用schedule()进行进程切换(主动),也可以在中断处理过程中进行调度(被动)。用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
- 内核的使用:32位x86系统下,每个进程的地址空间有4G,用户态0-3G,3G以上仅内核态可以访问。所有进程3G以上是共享的,在内核中代码段,堆栈段都是相同的,回到用户态才不同(taxi)。进程进入内核就都一样,没有进程陷入内核就执行0号进程。内核可以看作各种中断处理过程和内核线程的集合。
Linux读书笔记1/2章:
http://www.cnblogs.com/20135314ZHU/p/5284870.html
第三章读书笔记:
http://www.cnblogs.com/20135314ZHU/p/5339373.html
第四章进程调度读书笔记:
http://www.cnblogs.com/20135314ZHU/p/5387887.html
深入理解计算机第七章:
http://www.cnblogs.com/20135314ZHU/p/5362979.html
老师有时候风趣的讲课方式特别吸引人,特别是老师的微笑,特别可爱。