一 实验目的
1.以fork和execve系统调用为例分析中断上下文的切换
2.分析execve系统调用中断上下文的特殊之处
3.分析fork子进程启动执行时进程上下文的特殊之处
4.以系统调用作为特殊的中断,结合中断上下文切换和进程上下文切换分析Linux系统的一般执行过程
二 实验过程
1.fork函数调用
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
struct task_struct *p;
int trace = 0;
long nr;
/*
* Determine whether and which event to report to ptracer. When
* called from kernel_thread or CLONE_UNTRACED is explicitly
* requested, no event is reported; otherwise, report if the event
* for the type of forking is enabled.
*/
if (!(clone_flags & CLONE_UNTRACED)) {
if (clone_flags & CLONE_VFORK)
trace = PTRACE_EVENT_VFORK;
else if ((clone_flags & CSIGNAL) != SIGCHLD)
trace = PTRACE_EVENT_CLONE;
else
trace = PTRACE_EVENT_FORK;
if (likely(!ptrace_event_enabled(current, trace)))
trace = 0;
}
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, NULL, trace);
/*
* Do this prior waking up the new thread - the thread pointer
* might get invalid after that point, if the thread exits quickly.
*/
if (!IS_ERR(p)) {
struct completion vfork;
struct pid *pid;
trace_sched_process_fork(current, p);
pid = get_task_pid(p, PIDTYPE_PID);
nr = pid_vnr(pid);
if (clone_flags & CLONE_PARENT_SETTID)
put_user(nr, parent_tidptr);
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
get_task_struct(p);
}
wake_up_new_task(p);
/* forking complete and child started to run, tell ptracer */
if (unlikely(trace))
ptrace_event_pid(trace, pid);
if (clone_flags & CLONE_VFORK) {
if (!wait_for_vfork_done(p, &vfork))
ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
}
put_pid(pid);
} else {
nr = PTR_ERR(p);
}
return nr;
}
父进程通过函数中的copy_process函数来实现对子进程的创建。
2.execve函数调用
execve和其他系统调?不同之处是加载完新的可执?程序之后已经覆盖了原来?进程的上下?环境。execve系统调用的作用是运行另外一个指定的程序。它会把新程序加载到当前进程的内存空间内,当前的进程会被丢弃,它的堆、栈和所有的段数据都会被新进程相应的部分代替,然后会从新程序的初始化代码和 main 函数开始运行。同时,进程的 ID 将保持不变。
execve的调用逻辑是
sys_execve() -> do_execve() -> do_execveat_common() -> do_execve_file -> exec_binprm() -> search_binary_handler() -> load_elf_binary() -> start_thread()
3.linux系统的一般执行过程
3.1一般情况
正在运行的用户态进程X切换到运行用户态进程Y的过程
(1)正在运行的用户态进程X
(2)发生中断——save cs:eip/esp/eflags(current) to kernel stack,then 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
(8)继续运行用户态进程Y
3.2特殊情况
(1)通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;
(2)内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;
(3)创建子进程的系统调用在子进程中的执行起点及返回用户态,如fork;
(4)加载一个新的可执行程序后返回到用户态的情况,如execve;
三 总结
1、Linux进程调度是基于分时和优先级的。
2、Linux中,内核线程是只有内核态没有用户态的特殊进程。
3、内核可以看作各种中断处理过程和内核线程的集合。
4、Linux系统的一般执行过程 可以抽象成正在运行的用户态进程X切换到运行用户态进程Y的过程。
5、Linux中,内核线程可以主动调度,主动调度时不需要中断上下文的切换。
6、Linux内核调用schedule()函数进行调度,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换。
结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程
原文:https://www.cnblogs.com/wozaizai/p/13127391.html