2023-2024-1 20232812《Linux内核原理与分析》第九周作业

一、Linux的进程调度时机

进程切换分为自愿(voluntary)和强制(involuntary)两种。通常自愿切换是指任务由于等待某种资源,将state改为非RUNNING状态后,调用schedule()主动让出CPU;而强制切换(即抢占)则是任务状态仍为RUNNING却失去CPU使用权,情况有任务时间片用完、有更高优先级的任务、任务中调用cond_resched()或yield让出CPU。

1、进程状态转换的时刻:进程终止、进程睡眠;

2、当前进程的时间片用完时(current->counter=0);

3、设备驱动程序

4、进程从中断、异常及系统调用返回到用户态时;

时机1,进程要调用sleep()或exit()等函数进行状态转换,这些函数会主动调用调度程序进行进程调度;

时机2,由于进程的时间片是由时钟中断来更新的,因此,这种情况和时机4是一样的。

时机3,当设备驱动程序执行长而重复的任务时,直接调用调度程序。在每次反复循环中,驱动程序都检查need_resched的值,如果必要,则调用调度程序schedule()主动放弃CPU。

时机4,如前所述,不管是从中断、异常还是系统调用返回,最终都调用ret_from_sys_call(),由这个函数进行调度标志的检测,如果必要,则调用调用调度程序。那么,为什么从系统调用返回时要调用调度程序呢?这当然是从效率考虑。从系统调用返回意味着要离开内核态而返回到用户态,而状态的转换要花费一定的时间,因此,在返回到用户态前,系统把在内核态该处理的事全部做完。
 

switch_to为宏定义,不能设置断点,查看其内联汇编代码:

#define switch_to(prev, next, last)                    
do {
    // 定义寄存器变量
    unsigned long ebx, ecx, edx, esi, edi;              
 
    // 使用汇编嵌入代码实现进程切换
    asm volatile(
        // 保存当前进程的标志寄存器 (EFLAGS) 到堆栈
        "pushfl\n\t"      
        // 保存当前进程的基址寄存器 (EBP) 到堆栈
        "pushl %%ebp\n\t"        
        // 将当前进程的栈指针 (ESP) 存储到 [prev_sp] 变量
        "movl %%esp,%[prev_sp]\n\t"  
        // 将下一个进程的栈指针 (next_sp) 加载到 ESP 寄存器中,实现切换
        "movl %[next_sp],%%esp\n\t"  
        // 将标号 '1' 存储到 [prev_ip] 变量,以便返回当前进程的位置
        "movl $1f,%[prev_ip]\n\t"     
        // 将下一个进程的入口地址 (next_ip) 压入堆栈,以便下一次进程切换时返回该地址
        "pushl %[next_ip]\n\t"     
        // 执行进程切换的汇编代码(__switch_to 函数)
        __switch_canary                   
        // 跳转到 __switch_to 标号执行下一个进程的切换
        "jmp __switch_to\n" 
        // 标号 '1' 用于返回当前进程的位置
        "1:\t"                        
        // 弹出下一个进程的基址寄存器 (EBP)
        "popl %%ebp\n\t"    
        // 恢复标志寄存器 (EFLAGS) 的值
        "popfl\n"       
        : [prev_sp] "=m" (prev->thread.sp),     // 输出约束,将 [prev_sp] 绑定到 prev 进程的栈指针
          [prev_ip] "=m" (prev->thread.ip),     // 输出约束,将 [prev_ip] 绑定到 prev 进程的入口地址
          "=a" (last),                 // 输出约束,将 'last' 绑定到寄存器 EAX
          "=b" (ebx), "=c" (ecx), "=d" (edx),      // 输出约束,将寄存器 EBX、ECX、EDX 绑定到相应变量
          "=S" (esi), "=D" (edi)             // 输出约束,将寄存器 ESI、EDI 绑定到相应变量
          __switch_canary_oparam                // 输出约束,可能包括一些其他参数
        : [next_sp]  "m" (next->thread.sp),        // 输入约束,将 [next_sp] 绑定到 next 进程的栈指针
          [next_ip]  "m" (next->thread.ip),       // 输入约束,将 [next_ip] 绑定到 next 进程的入口地址
          [prev]     "a" (prev),              // 输入约束,将当前进程的指针 'prev' 绑定到寄存器 EAX
          [next]     "d" (next)               // 输入约束,将下一个进程的指针 'next' 绑定到寄存器 EDX
          __switch_canary_iparam                // 输入约束,可能包括一些其他参数
        "memory");                  // 内存约束,告诉编译器需要刷新内存缓存
 
} while (0)

二、实验过程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值