码迷,mamicode.com
首页 > 系统相关 > 详细

linux内核学习总结

时间:2016-05-01 19:11:26      阅读:242      评论:0      收藏:0      [点我收藏+]

标签:

丰富充实的linux学习已经告一段落,通过此次学习,对linux有了初步的认识和了解,形成了一个大概的框架,对linux的工作过程也有了基本的概念。对课程的内容总结如下:

 

1.计算机是如何工作的

从硬件的角度看:

根据冯诺依曼?体系结构,

1)cpu通过总线根据cs:ip的值从内存的代码段读取一条指令,读取的指令进入指令缓冲器;

2)然后ip=ip+所取指令的长度,从而指向下一条指令;

3)执行指令并转至1),

重复这个过程。?

从程序员的角度看:?

cpu抽象为一个for循环

for(;;){next instruction}?

 

2.操作系统是如何工作的

操作系统在计算机工作基础上进行进程调度和中断,使得多个进程能够快速切换执行,合理的分配cpu资源。

 

3.有关start_kernel

startkernel中的rest_init生成0号进程,kernel_init生成1号进程,kernelthread生成2号进程,1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先。之后的所有进程都是通过1号进程和2号进程生成。当系统没有进程需要执行时就调度到idle进程。

 

4.系统调用

系统调用把用户从底层的硬件编程中解放出来,极大地提高了系统的安全性,使用和程序具有可移植性。

??当用户态进程调用一个系统调用时,CPU切换到内核并开始执行一个内核函数。在Linux中是通过执行int $0x80来执行系统调用的,在这条汇编指令产生向量为128的编程异常。内核实现了很多不同的系统调用,进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数。system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号一个应用程序调用fork()封装例程,那么在执行int $0x80之前就把eax寄存器的值置为2(即__NR_fork)。这个寄存器的设置是libc库中的封装例程进行的,因此用户一般不关心系统调用号进入sys_call之后,立即将eax的值压入内核堆栈。

 

5.创建一个新进程在内核中的执行过程

Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:

复制一个PCB——task_struct

err = arch_dup_task_struct(tsk, orig);

要给新进程分配一个新的内核堆栈

ti = alloc_thread_info_node(tsk, node);

tsk->stack = ti;

setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈

要修改复制过来的进程数据,比如pid、进程链表等等都要改改吧,见copy_process内部。

从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,在copy_thread in copy_process中设定的。

*childregs = *current_pt_regs(); //复制内核堆栈

childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!

p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶

p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址

我们知道,Linux 系统中fork 、vfork 、clone等函数都可以用来创建一个新的进程,可以看到,fork 、vfork 、clone 等函数对应的系统调用,都是调用了 do_fork 函数实现的。所以具体调用过程是先do_fork 系统内核调用,执行到copy_process 复制父进程的所有信息给子进程;其中dup_task_struct 中为子进程分配了新的堆栈;同时调用 sched_fork,并把新进程设置为 TASK_RUNNING;并且copy_thread 中把父进程的寄存器上下文复制给子进程;最后设置 ret_from_fork 的地址为 eip 寄存器的值,使得子进程从ret_from_fork执行。

 

6.装载execve和fork的区别

execve和fork都是特殊的系统调用,陷入到内核态在返回到用户态继续执行。fork比较特殊,父进程和一般的系统调用一样,子进程从ret_from_fork开始执行返回到用户态。execve陷入到内核态,用加载的新的可执行文件将当前进程的可执行程序覆盖,当其返回时已经不是原来的可执行程序,而是新的可执行程序了。因为调用exec并不创建新进程,只是用一个全新的程序替换了当前进程的正文、数据、堆和栈段,所以前后的进程ID并未改变

 

7.进程的切换

一、调度时机

1.中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();

2.内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;

3.用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。

二、当x用户态进程通过进程调度策略切换到用户态进程y时,

最一般的情况:正在运行的用户态进程X切换到运行用户态进程Y的过程

1.正在运行的用户态进程X

2.发生中断——save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack).

3.SAVE_ALL //保存现场

4.中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换

5.标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行)

6.restore_all //恢复现场

7.iret - pop cs:eip/ss:esp/eflags from kernel stack

8.继续运行用户态进程Y

几种特殊情况

1.通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;

2.内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;

3.创建子进程的系统调用在子进程中的执行起点及返回用户态,如fork;

4.加载一个新的可执行程序后返回到用户态的情况,如execve;

三、有关进程切换

1.为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;

2.挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;

3.进程上下文包含了进程执行需要的所有信息

(1)用户地址空间: 包括程序代码,数据,用户堆栈等

(2)控制信息 :进程描述符,内核堆栈等

(3)硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)

4.schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换

(1)next = pick_next_task(rq, prev);//进程调度算法都封装这个函数内部

(2)context_switch(rq, prev, next);//进程上下文切换

(3)switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程

 

 

博客作业目录:

1.由汇编代码对计算机工作过程的分析http://blog.sina.com.cn/s/blog_14375bc180102w2lx.html

2.简单的时间片轮转多道程序内核的分析http://blog.sina.com.cn/s/blog_14375bc180102w2zl.html

3.跟踪分析Linux内核的启动过程http://blog.sina.com.cn/s/blog_14375bc180102w3g4.html

4.使用API和C代码中嵌入汇编代码系统调用http://blog.sina.com.cn/s/blog_14375bc180102w3p3.html

5.System_call中断处理过程分析http://blog.sina.com.cn/s/blog_14375bc180102w3z4.html

6.分析Linux内核创建一个新进程的过程http://blog.sina.com.cn/s/blog_14375bc180102w4ai.html

7.Linux内核如何装载和启动一个可执行程序http://www.cnblogs.com/yuxiaochuan/p/5376507.html

8.理解进程调度时机跟踪分析进程调度与进程切换的过程http://www.cnblogs.com/yuxiaochuan/p/5402770.html

 

总结:在学习《Linux内核分析》课程中最大的收获?学习完《Linux内核分析》课程后最大的遗憾是什么?

最大的收获是搞清楚了进程调度和中断机制,感受到了linux设计的巧妙

最大的遗憾是课程不够多啊= =

 

 

虞啸川 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

linux内核学习总结

标签:

原文地址:http://www.cnblogs.com/yuxiaochuan/p/5450904.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!