结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程

一、实验要求:

结合中断上下文切换和进程上下文切换分析Linux内核一般执行过程

  • 以fork和execve系统调用为例分析中断上下文的切换
  • 分析execve系统调用中断上下文的特殊之处
  • 分析fork子进程启动执行时进程上下文的特殊之处
  • 以系统调用作为特殊的中断,结合中断上下文切换和进程上下文切换分析Linux系统的一般执行过程

二、实验过程:

fork函数:

一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

 

 fork仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

    1)在父进程中,fork返回新创建子进程的进程ID;

    2)在子进程中,fork返回0;

    3)如果出现错误,fork返回一个负值;

在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,

fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

引用一位网友的话来解释fpid的值为什么在父子进程中不同。“其实就相当于链表,进程形成了链表,父进程的fpid(p 意味point)指向子进程的进程id,

因为子进程没有子进程,所以其fpid为0.

 

fork特殊之处:

fork在陷内核态之后有两次返回,第次返回到原来的进程的位置继续执,但是在进程中fork也返回了次,会返回到个特定的点——ret_from_fork,所以它可以正常系统调返回到户态

execve函数:

函数执行成功时没有返回值,执行失败时的返回值为-1.

execve()用来执行参数filename字符串所代表的文件路径,第二个参数是利用数组指针来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。

execve系统调用的过程总结如下:

1. execve系统调用陷入内核,并传入命令行参数和shell上下文环境

2. execve陷入内核的第一个函数:do_execve,该函数封装命令行参数和shell上下文

3. do_execve调用do_execveat_common,后者进一步调用__do_execve_file,打开ELF文件并把所有的信息一股脑的装入linux_binprm结构体

4. __do_execve_file中调用search_binary_handler,寻找解析ELF文件的函数

5. search_binary_handler找到ELF文件解析函数load_elf_binary

6. load_elf_binary解析ELF文件,把ELF文件装入内存,修改进程的用户态堆栈(主要是把命令行参数和shell上下文加入到用户态堆栈),修改进程的数据段代码段

7. load_elf_binary调用start_thread修改进程内核堆栈(特别是内核堆栈的ip指针)

8.进程从execve返回到用户态后ip指向ELF文件的main函数地址,用户态堆栈中包含了命令行参数和shell上下文环境

 

 execve特殊之处:

execve在调用时陷入内核态,就执行do_execve文件,覆盖当前的可执行程序,所以返回的是新的可执行程序的起点。main函数位置是静态链接的可执行文件,动态链接的可执行文件需要连接动态链接库后在开始执行。

 

Linux系统的一般执行过程:

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

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

    2、发生中断——(CPU自动完成)

save cs:eip/esp/eflags(current) to kernel stack

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返回执行的是Y进程曾经发生中断时用户态的下一条指令,恢复现场,恢复不是X进程的现场,而是曾经保存的Y进程现场)

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

几种特殊情况

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

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

    3、创建子进程的系统调用在子进程中的执行起点及返回用户态,如fork,返回了两次,在父进程返回一次,在子进程也返回;如果next进程是一个新创建的子进程,没有被执行过,则next进程的执行起点是ret_from_fork;

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

 

参考:

1. https://blog.csdn.net/kxjrzyk/article/details/81603049

2. https://www.cnblogs.com/jxhd1/p/6706701.html

3. 《庖丁解牛linux内核分析》

原文地址:https://www.cnblogs.com/smyhrps/p/13129137.html