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

时间:2020-06-15 17:58:49   收藏:0   阅读:56

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


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

一、系统调用中断上下文的切换(以fork及execve为例分析)

fork系统调用用于创建一个新进程,称为子进程,它与进程(称为系统调用fork的进程)同时运行,此进程称为父进程。创建新的子进程后,两个进程将执行fork()系统调用之后的下一条指令。子进程使用相同的pc(程序计数器),相同的CPU寄存器,在父进程中使用的相同打开文件。它不需要参数并返回一个整数值。下面是fork()返回的不同值。一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。因此,可以通过返回值来判定该进程是父进程还是子进程
子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。fork调用一次,返回两次,这两个返回分别带回它们各自的返回值,其中在父进程中的返回值是子进程的进程号,而子进程中的返回值则返回 0。

技术图片

Linux系统?般会提供了execl、 execlp、 execle、 execv、 execvp和execve等6个?以加载执??个可执??件的库函数,这些库函数统称为exec函数,差异在于对命令?参数和环境变量参数的传递?式不同。 exec函数都是通过execve系统调?进?内核,对应的系统调?内核处理函数为__x64_sys_execve,它们都是通过调?do_execve来具体执?加载可执??件的?作。
整体的调用关系为如下

  • __x64_sys_execve
  • do_execve()
  • do_execveat_common()
  • __do_execve_file
  • exec_binprm()
  • search_binary_handler()
  • load_elf_binary()
  • start_thread()

技术图片

二、fork系统调用

(1) fork调用程序
技术图片

(2) 编译执行
技术图片

(3) 反汇编来查看fork系统调用过程
objdump -S fork > fork.s
打开fork.s文件可查询如下:
技术图片

(4) 查看系统文件
通过查看汇编代码已知调用了56号系统调用,查询/linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl 有:

技术图片

即内核函数调用__64_sys_clone,通过查询linux-5.4.34/kernel/fork.c 可确定sys_clone的函数返回值为do_fork()

(5)通过gdb查看fork运行函数栈
__x64_sys_clone, _do_fork, copy_process, dup_task_struct, copy_thread_tls 上设置断点,并运行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

技术图片

技术图片

技术图片

技术图片

技术图片

三、execve系统调用

技术图片

sys_execve的核心是调用do_execve函数,传给do_execve的第一个参数是已经拷贝到内核空间的路径名filename,第二个和第三个参数仍然是系统调用execve的第二个参数argv和第三个参数envp,它们代表的传给可执行文件的参数和环境变量仍然保留在用户空间中。

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

Linux中断分为两个半部:上半部(tophalf)和下半部(bottom half)。
上半部的功能是"登记中断",当一个中断发生时,它进行相应地硬件读写后就把中断例程的下半部挂到该设备的下半部执行队列中去。因此,上半部 执行的速度就会很快,可以服务更多的中断请求。但是,仅有"登记中断"是远远不够的,因为中断的事件可能很复杂。
因此,Linux引入了一个下半部,来完 成中断事件的绝大多数使命。下半部和上半部最大的不同是下半部是可中断的,而上半部是不可中断的,下半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断,下半部则相对来说并不是非常紧急的,通常还是比较耗时的,因此由系统自行安排运行时机,不在中断服务上下文中执行。在Linux驱动程序中,为设备实现一个中断包含两个步骤:
1)向内核注册中断
2)实现中断处理函数,其中request_irq用于实现中断的注册功能:int request_irq(unsigned int irq, void (*handler)(int, void*, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id)

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!