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

 

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

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

一、fork和execve系统调用的区别与联系

•fork两次返回,第一次返回到父进程继续向下执行,第二次子进程返回到ret_from_fork后正常返回到用户态,其通过do_fork函数创建进程,其大概过程如下:

long _do_fork(struct kernel_clone_args *args) {    
    .....
    //复制进程描述符和执?时所需的其他数据结构       
    p = copy_process(NULL, trace, NUMA_NO_NODE, args);    
    ......
    wake_up_new_task(p);//将?进程添加到就绪队列    
    .......
    return nr;//返回?进程pid(?进程中fork返回值为?进程的pid)
 }

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

 

_do_fork函数主要完成了调?copy_process()复制?进程、获得pid、调?wake_up_new_task将?进程加?就绪队列等待调度执?等、通过clone_flags标志进行一些辅助性工作等。

 

•execve在执行时陷入内核态,用execve中加载的程序把当前正在执行的进程覆盖掉,当系统调用返回时返回到新的可执行程序起点,程序执?的起点,静态链接的可执??件也就是main函数的?致位置,动态链接的可执??件还需
要ld链接好动态链接库再从main函数开始执?。其函数调用的实质是运行内核态的sys_execve()函数,大致过程如下:

(1).sys_execve中的do_execve()读取128个字节的文件头部,以此判定可执行文件的类型;

  (2).调用search_binary_handler()去搜索和匹配合适的可执行文件的装载处理过程;

(3).EIF文件由load_elf_binary()函数负责装载。

 

二、fork系统调用

(1).创建子进程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[])
{
    int pid;

    pid = fork();
    if(pid<0)
    {
      //error
       fprintf(stderr,"For Failed");
       exit(-1);
    }
    else if(pid==0)
    {
       //child
       printf("this is child process \n");
    }
    else
    {
       //parent
       printf("this is Parent process \n");
       wait(NULL);
       printf("child complete \n");
    }
    return 0;
}

(2).编译、执行:

gcc -o fork fork.c -static -m64
./fork

 

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

 

(3).反汇编 

objdump -S fork > fork.s

 

  打开fork.s文件可查询如下:

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

 

 查询448f60有:

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

 

 对应56号,查询/linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl,有:

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

 

 即内核函数调用__64_sys_clone,查询linux-5.4.34/kernel/fork.c,有:

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

 

可确定sys_clone函数返回值就是do_fork(),接下来在__x64_sys_clone_do_forkcopy_processdup_task_structcopy_thread_tls下断点,shell下运行fork可执行文件,查看此时函数栈。

qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"
gdb vmlinux

 

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

 

断点设置完成后,shell下运行./fork,有:

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

 

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

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

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

 

 按照该顺序得到最终结果。

三、execve系统调用

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

 

execve和其他系统调?不同之处是加载完新的可执?程序之后已经覆盖了原来?进程的上下?环境。通过调用栈可以看出execve的调用关系为:

__x64_sys_execve -> do_execve() –> do_execveat_common() -> __do_execve_?le -> exec_binprm()-> search_binary_handler() -> load_elf_binary() -> start_thread()

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

 

 

 

四、Linux系统的一般执行过程

以32位x86系统结构linux-3.18.6为例,以系统调?作为特殊的中断简要总结如下。

(1)正在运?的?户态进程X。
(2)发?中断(包括异常、系统调?等),CPU完成以下动作。
                 save cs:eip/ss:esp/eflags:当前CPU上下?压?进程X的内核堆栈。
                 load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack):加载当前进程内核堆栈相关信息,跳转到中断处理程序,即中断执?路径的起点。
(3)SAVE_ALL,保存现场,此时完成了中断上下?切换,即从进程X的?户态到进程X的内核态。
(4)中断处理过程中或中断返回前调?了schedule函数,其中的switch_to做了关键的进程上下?切换。将当前进程X的内核堆栈切换到进程调度算法选出来的next进程
(5)标号1,即前述3.18.6内核的swtich_to代码第50?“”1:\t“ ”(地址为switch_to中的“$1f”),之后开始运?进程Y(这?进程Y曾经通过以上步骤被切换出去,因此可以
从标号1继续执?)。

(6)restore_all,恢复现场,与(3)中保存现场相对应。注意这?是进程Y的中断处理过程中,?(3)中保存现场是在进程X的中断处理过程中,因为内核堆栈从进程X
切换到进程Y了。
(7)iret - pop cs:eip/ss:esp/eflags,从Y进程的内核堆栈中弹出(2)中硬件完成的压栈内容。此时完成了中断上下?的切换,即从进程Y的内核态返回到进程Y的?户
态。
(8)继续运??户态进程Y。

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

上一篇:shell 强化1


下一篇:Ubuntu 通过apt方式安装OpenCV