2021-2022-1 20212809《Linux内核原理与分析》第九周作业

本文介绍了Linux内核中的进程调度时机,包括中断处理过程中的调度和内核线程的主动调度。深入探讨了schedule()函数,特别是pick_next_task()和context_switch(),其中switch_to()函数在进程上下文切换中的关键作用。此外,还讨论了使用gdb跟踪fork命令时的断点设置和进程切换过程。
摘要由CSDN通过智能技术生成
进程调度时机
  • 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
  • 内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
  • 用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
schedule()函数

schedule()函数原型位于linux-3.18.6/kernel/sched/core.c中,其中主要的关键函数有pick_next_task(),这个函数调用之后,会根据某种进程调度策略选择出下一个运行的进程。紧接着是context_switch(),这是进程上下文切换函数,它的函数实现如下:

context_switch(struct rq *rq, struct task_struct *prev,struct task_struct *next)
{
	struct mm_struct *mm, *oldmm;

	prepare_task_switch(rq, prev, next);

	mm = next->mm;
	oldmm = prev->active_mm;
	arch_start_context_switch(prev);
	if (!mm) {
		next->active_mm = oldmm;
		atomic_inc(&oldmm->mm_count);
		enter_lazy_tlb(oldmm, next);
	} else
		switch_mm(oldmm, mm, next);

	if (!prev->mm) {
		prev->active_mm = NULL;
		rq->prev_mm = oldmm;
	}
	spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
        context_tracking_task_switch(prev, next);
        
        switch_to(prev, next, prev);        //关键函数

        barrier();
	/*
	 * this_rq must be evaluated again because prev may have moved
	 * CPUs since it called schedule(), thus the 'rq' on its stack
	 * frame will be invalid.
	 */
	finish_task_switch(this_rq(), prev);
}

在context_switch()函数中,最为重要的是switch_to()函数,它主要是由内联汇编实现,功能是完成进程切换

#define switch_to(prev, next, last)                    
do {                                 
         unsigned long ebx, ecx, edx, esi, edi;              
         asm volatile("pushfl\n\t"            /* 保存当前进程的flags */   
                   "pushl %%ebp\n\t"          /* 把当前进程的当前的ebp压入当前进程的栈   */ 
                   "movl %%esp,%[prev_sp]\n\t"  /*保存当前的esp到prev->thread.sp指向的内存中   */ 
                   "movl %[next_sp],%%esp\n\t"  /* 重置esp,把下个进程的next->thread.sp赋予esp */ 
                   "movl $1f,%[prev_ip]\n\t"   /*把1:的代码在内存中存储的地址保存到prev->thread.ip中*/ 
                   "pushl %[next_ip]\n\t"        /*重置eip   */    
                    __switch_canary                   
                   "jmp __switch_to\n"      /*跳转到switch_to函数*/
                   "1:\t"                        
                   "popl %%ebp\n\t"       /* 重置ebp  */    
                   "popfl\n"     /* 重置flags*/  

                   : [prev_sp] "=m" (prev->thread.sp),     
                     [prev_ip] "=m" (prev->thread.ip),        
                     "=a" (last),                 
                     "=b" (ebx), "=c" (ecx), "=d" (edx),      
                     "=S" (esi), "=D" (edi)             

                     __switch_canary_oparam                

                       : [next_sp]  "m" (next->thread.sp),        
                     [next_ip]  "m" (next->thread.ip),       
                     [prev]     "a" (prev),              
                     [next]     "d" (next)               
                     __switch_canary_iparam                
                     "memory");                  
} while (0)

gdb跟踪fork命令

断点设置:

b schedule
b context_switch
b pick_next_task

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

进程切换的一般过程:一个用户态进程A,发生中断,save cs:eip/esp/eflags等到内核堆栈;SAVE_ALL保存现场。中断处理过程中直接调用或者中断返回前调用schedule,其中switch_to中的汇编代码做了关键的部分。切换了内核堆栈,从当前进程进入到下一个进程。从标号1开始就开始运行下一个进程(这个进程必须是曾经通过这个过程被切换出去的,比如新进程就不包含在内);恢复下一个进程的现场,pop出eip,esp。继续运行进程切换前用户态正在跑的程序。
特殊情况:

  • 通过中断处理过程中的调度,用户态进程与内核进程之间互相切换,与一般情形类似;
  • 内核进程程主动调用 schedule 函数,只有进程上下文的切换,没有中断上下文切换;
  • 创建子进程的系统调用在子进程中的执行起点及返回用户态,如:fork;
  • 加载一个新的可执行程序后返回到用户态的情况,如:execve。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值