• 结合中断上下文切换和进程上下文切换分析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内核分析》

  • 相关阅读:
    hdu1875(最小生成树prime)
    hdu1839(最小生成树)
    poj2739(尺取法+质数筛)
    poj2100(尺取法)
    codeforces-div2-449-B
    gym-101350M
    gym-10135I
    gym-101350H
    gym-101350D
    hdu 5569
  • 原文地址:https://www.cnblogs.com/smyhrps/p/13129137.html
Copyright © 2020-2023  润新知