linux 进程切换原理,linux kernel 进程切换原理分析

前言:本文为我学习孟宁老师的《庖丁解牛linux 内核》课程的简单总结,同时作为课间作业。

唐建,《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

1、概述

本文通过一个简单的自定义内核程序来简单描述内核中程序的相互切换过程,给大家展示内存多进程随着时间片轮转而来回切换

的过程以及原理。

2、代码结构

我们的例子代码结构:主要有两个文件mypcb.c myinterrupt.c

mypcb.c文件内容:

20180611000621932898.png

20180611000622478930.png

myinteruppt.c文件内容:

20180611000622686011.png

20180611000622887232.png

my_start_kernel() ,系统的起点,完成系统环境的准备

my_process()这问进程的主体,我们将起多个进程实例。

my_schedule() 系统进程调度器,我们将在这个函数看到进程是怎么切换的。

my_time_handler()  时间中断函数,这个函数里面产生时间片,时间片到了后就需要进行进程切换了。

3、my_start_kernel() 系统环境的准备。

我们先看下重要的结构体 ,进程的结构体,相当于系统的task_struct

struct Thread {

unsigned longip;

unsigned longsp;

};

typedef struct PCB{

int pid;                                  ——pid

volatile long state;/* -1 unrunnable, 0 runnable, >0 stopped */  ——当前进程调状态

unsigned long stack[KERNEL_STACK_SIZE];                            ——进程的堆栈

/* CPU-specific state of this task */

struct Thread thread;                                                                 ——线程,不用理会线程。主要看其内容ip=eip,sp=esp。就是进程当前执行到哪里了。

unsigned longtask_entry;                                       ——进程的入口

struct PCB *next;               ——下一个进程

}tPCB;

我们的linux 系统进程调度大体上就是操作task_struct,所以我们这模拟的系统就是围绕这tpcb进行操作

回到正题,my_start_kernel如下图这个函数分三部分

(1)、0号进程的初始化

进程的入口为my_process,所以我们一会主要看怎么跳转到进程的入口的。

这里task_entry 赋予了进程入口my_process()。同时sp=esp = 进程的栈底,因为是刚开始嘛,栈还是空的。

(2)、fork多个进程,这里模拟了fork的过程,

20180611000623118734.png

(3)、我们这着重讲这个,就是my_start_kernel怎么切换到0号进程的。

asm volatile(

"movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */ ——将esp 赋值为0号进程的sp,好了,这样栈就切换到0号进程的栈了

"pushl %1\n\t" /* push ebp */                                         ——将将因为0号进程还没开始跑呢,所以ebp=esp啊,所以这里实际上是将ebp压栈

"pushl %0\n\t" /* push task[pid].thread.ip */                  ——将ip=eip 将0号进程的eip入栈。

"ret\n\t" /* pop task[pid].thread.ip to eip */                    ——ret= pop eip。准备切换到0号进程了,将0号进程的ip放入eip寄存器中。

"popl %%ebp\n\t"                                                         ——sp=ebp,准备栈底。到这里,ebp、eip、esp都准备好了,注意eip=my_process,

也就是已经切换到0号进程了。

:

: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)/* input c or d mean %ecx/%edx*/

);

4、时间片

20180611000623429356.png

如上图,实际上就是没1000个时钟就赋值my_need_sched=1,这个是调度标志位,表示要开始调度到下一个进程了。

5、my_process

前面我们讲到0号进程已经开始跑起来了,也就是跑到了my_process这里。

20180611000623669649.png

如上图,可以看到这里是死循环,当发现调度标志被设置为1时,表示要开始调度了,这个进程不能再执行了,轮到别人了,于是调用my_schedule去切换到其他进程。

6、my_schedule,进程切换。

现在来到我们的核心了,实际上就保存上一个进程的现场,然后将需要调度的进程的现场恢复到寄存器中,让寄存器按照这个进程的现场运行。

20180611000623899197.png

(1)、这个就是取出马上需要进程的结构体

(2)、这个就是难点了

/* switch to next process */

asm volatile(

"pushl %%ebp\n\t" /* save ebp */       ——将本进程的ebp入栈

"movl %%esp,%0\n\t" /* save esp */   ——将esp保存到本进程中,用于下次执行

"movl %2,%%esp\n\t" /* restore esp */  ——将新进程的sp写入esp

"movl $1f,%1\n\t" /* save eip */

"pushl %3\n\t"

"ret\n\t" /* restore eip */

"1:\t" /* next process start here */

"popl %%ebp\n\t"

: "=m" (prev->thread.sp),"=m" (prev->thread.ip)

: "m" (next->thread.sp),"m" (next->thread.ip)

);

这个实际上就是保存本进程的信息,然后将下一个进程的东西装入寄存器,并开始执行。

7、执行结果

可以推测,我们的进程是按照序号顺序执行的

20180611000624223494.png

原文:https://www.cnblogs.com/tjyuanxi/p/9164181.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值