标签:例程 comm $1 lin text 读写 调用 任务 代码
结合中断上下文切换和进程上下文切换分析Linux内核一般执行过程
系统调用
系统调用利用陷阱来实现,是异常的一种,从而从用户态进?到内核态。
一般的系统调用
fork 系统调用过程
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()
(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
./fork
,在每个断点通过bt
查看堆栈execve
系统调用的堆栈情况如下:sys_execve的核心是调用do_execve函数,传给do_execve的第一个参数是已经拷贝到内核空间的路径名filename,第二个和第三个参数仍然是系统调用execve的第二个参数argv和第三个参数envp,它们代表的传给可执行文件的参数和环境变量仍然保留在用户空间中。
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)
进程上下文
当一个进程在执行时,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下文。
当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当前进程的上下文,以便在再次执行该进程时,能够必得到切换时的状态执行下去。
在LINUX中,当前进程上下文均保存在进程的任务数据结构中。在发生中断时,内核就在被中断进程的上下文中,在内核态下执行中断服务例程。但同时会保留所有需要用到的资源,以便中断服务结束时能恢复被中断进程的执行。
进程调度时机
上下文切换
上下文切换 , 其实际含义是任务切换, 或者CPU寄存器切换。当多任务内核决定运行另外的任务时, 它保存正在运行任务的当前状态, 也就是CPU寄存器中的全部内容。这些内容被保存在任务自己的堆栈中, 入栈工作完成后就把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU寄存器, 并开始下一个任务的运行, 这一过程就是context switch。
Linux执行过程如下
以正在运行的用户态进程X切换到用户态进程Y为例具体表述如下:
中断处理程序的限制
结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程
标签:例程 comm $1 lin text 读写 调用 任务 代码
原文地址:https://www.cnblogs.com/tangxin2019/p/13131967.html