一个简单的时间片轮转多道程序

  • 操作系统的“两把剑”:中断上下文(保存现场和恢复现场)和进程上下文的切换

源代码的分析

*使用的源代码为视频中所使用的精简内核的源代码

首先分析mypcd.h

struct Thread {
unsigned long ip;//point to cpu run address
unsigned long sp;//point to the thread stack's top address
//todo add other attrubte of system thread
};

这是一个进程的结构体,其中的ip标记的是该进程的eip,而sp标记的是该进程的esp。

typedef struct PCB{
int pid; // 进程的id
volatile long state; /*进程的状态 -1 unrunnable, 0 runnable, >0 stopped */
char stack[KERNEL_STACK_SIZE];//进程的堆栈 each pcb stack size is 1024*8
/* CPU-specific state of this task */
struct Thread thread;
unsigned long task_entry;//进程的入口
struct PCB *next;//以链表的形式连接下一个PCB
unsigned long priority;//进程的优先度////////
//todo add other attrubte of process control block
}tPCB;

这是一个进程控制块的结构体,记录着进程的的各种状态与信息。

void my_schedule(void);

该函数的功能为调度器,用于进程之间的调度运行。

接着分析mymain.c即进程的启动机制

void __init my_start_kernel(void)
{
int pid = 0;
/* Initialize process 0*/
task[pid].pid = pid;
task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
// set task 0 execute entry address to my_process
task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
task[pid].next = &task[pid]; 因为系统中仅有一个进程(初始化),所以下一个进程指向自己
/*fork more process */
for(pid=1;pid<MAX_TASK_NUM;pid++) 创建进程
{
memcpy(&task[pid],&task[0],sizeof(tPCB));
task[pid].pid = pid;
task[pid].state = -1;
task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
task[pid].priority=get_rand(PRIORITY_MAX);//each time all tasks get a random priority
}
task[MAX_TASK_NUM-1].next=&task[0];
printk(KERN_NOTICE "\n\n\n\n\n\n system begin :>>>process 0 running!!!<<<\n\n");
/* start process 0 by task[0] */
pid = 0;
my_current_task = &task[pid];
asm volatile(
"movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */
"pushl %1\n\t" /* push ebp */
"pushl %0\n\t" /* push task[pid].thread.ip */
"ret\n\t" /* pop task[pid].thread.ip to eip */
"popl %%ebp\n\t"
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
);
}
  • 首先是先创建第一个进程,定义其进程编号为0,状态为可执行,设置进程入口的地址为该进程的eip,设置进程的堆栈为该进程的堆栈,task[0].next=&task[0];因为此时系统之中仅仅只要一个进程,该进程的下一个进程就只能暂且指向自己了。
  • 接着是创建更多的进程,步骤是通过一个for循环进程依次创建进程的编号、状态和堆栈。将新创建的进程加入上一个进程的尾部。
  • 接下来就是要启动0号进程也就是第一个进程,将该进程的堆栈标记so存入寄存器esp中,紧接着将ebp压入栈中,再将该进程的eip压入栈中,接着将myprocess的eip弹出给eip,也就是启动0号进程。
  • 当运行完后则需要运行popl %ebp退回上一状态。

*1f指的是标号1所在的位置。

	void my_process(void)
{
int i = 0;
while(1)
{
i++;
if(i%10000000 == 0)
{ if(my_need_sched == 1)
{
my_need_sched = 0;
sand_priority();
my_schedule();
}
}
}
}//end of my_process
  • 在myprocess的函数之中输出时采用了主动调度,循环1000万次才有一次机会判断一下是否需要调度。
  • 调度完切换回来时,则从myschedule();函数继续执行。

最后分析myinterrupt.c即进程的切换机制

void my_timer_handler(void)
{
#if 1
// make sure need schedule after system circle 2000 times.
if(time_count%2000 == 0 && my_need_sched != 1)
{
my_need_sched = 1;
//time_count=0;
}
time_count ++ ;
#endif
return;
}
  • 该函数的作用是设置时间片的大小,时间片用完时设置一下调度标志,当进程调度发现状态为1时,则会执行一次my_process,而后执行一次my_schedule。

      void my_schedule(void)
    {
    tPCB * next;
    tPCB * prev;
    // if there no task running or only a task ,it shouldn't need schedule
    if(my_current_task == NULL
    || my_current_task->next == NULL)
    {
    printk(KERN_NOTICE " time out!!!,but no more than 2 task,need not schedule\n");
    return;
    }
    /* schedule */ next = get_next();
    prev = my_current_task;
    printk(KERN_NOTICE " the next task is %d priority is %u\n",next->pid,next->priority);
    if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
    {//save current scene
    /* switch to next process */
    asm volatile(
    "pushl %%ebp\n\t" /* save ebp */
    "movl %%esp,%0\n\t" /* save esp */
    "movl %2,%%esp\n\t" /* restore 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)
    );
    my_current_task = next;//switch to the next task
    printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n",prev->pid,next->pid,next->pid); }
    else
    {
    next->state = 0;
    my_current_task = next;
    printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n\n",prev->pid,next->pid,next->pid); /* switch to new process */
    asm volatile(
    "pushl %%ebp\n\t" /* save ebp */
    "movl %%esp,%0\n\t" /* save esp */
    "movl %2,%%esp\n\t" /* restore esp */
    "movl %2,%%ebp\n\t" /* restore ebp */
    "movl $1f,%1\n\t" /* save eip */
    "pushl %3\n\t"
    "ret\n\t" /* restore eip */
    : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
    : "m" (next->thread.sp),"m" (next->thread.ip)
    );
    }
    return;
    }//end of my_schedule
  • 当前进程的下一个进程赋给next,当前进程赋给prev。如果下一个进程状态为0的话,即正在执行,则需要在两个正在运行的进程之间做进程上下文切换:

    • 保存当前进程的ebp
    • 将当前进程的esp赋给prev->thread.sp,从而将其保存起来
    • 将下一个进程的sp放置esp中
    • 保存当前进程的eip至当前进程所属的pcb中
    • 将下一个进程的eip保存至当前栈中
    • 通过ret,即可调用下一个进程
  • 若下一个进程的状态还未执行过,则进行以下步骤:

    • 将下一个进程设置为运行时状态,并将其设置为当前进程
    • 保存当前进程的ebp,将其压入栈中
    • 保存当前进程的esp至当前进程的pcb中
    • 由于该程序并未执行过,所以它的堆栈为空栈,空栈的创建则是通过将ebp和esp都赋予同一个值。
    • 保存当前进程的eip
    • 将当前进程的eip压入栈中
    • 通过ret,即可调用下一个进程

操作系统是如何工作的

  • 操作系统是控制应用程序执行和充当硬件系统和应用程序之间的界面的软件。
  • 操作系统是一个大型、复杂的系统软件,它负责计算机的全部软、硬件资源的分配、调度工作,控制和协调并发活动,实现信息的存取和保护。它提供用户接口,使用户获得良好的工作环境。操作系统使整个计算机系统实现了高效率和自动化,是现代计算机系统最关键和最核心的软件系统。
  • 操作系统能够完成程序创建、程序执行、、I/O设备访问、控制对文件的访问、系统访问、查错和纠错、簿记。
  • 操作系统的核心任务之一就是管理各种可获得的资源以及合理地调度它们。
  • 操作系统保存了一些队列,每个队列都是一组等待某些资源的进程。
    • 短程队列是由在主存中并处于就绪状态的进程组成。这些进程中的任何一个进程都可以作为处理器的下一个选用者,具体选择哪个进程由分派程序决定。(中断上下文)
    • 长程队列是一列等待使用系统的新进程。操作系统通过将一个进程从长程队列转移到短程队列,向系统增加任务。每个I/O设备都有一个I/O队列。等待使用某个设备的所有进程都排在该设备队列中。当中断发生时,操作系统得到处理器的控制权。一个进程可能会求助于操作系统提供的一些服务。在这种情况下,服务调度处理程序成为进入操作系统的入口。

实验楼截图

池彬宁 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

linux内核分析--操作系统是如何工作的?的更多相关文章

  1. Linux内核分析——操作系统是如何工作的

    万子惠 + 原创作品转载请注明出处 + <Linux内核分析> 实验部分 使用实验楼的虚拟机打开shell 然后cd mykernel 您可以看到qemu窗口输出的内容的代码mymain. ...

  2. Linux内核分析— —操作系统是如何工作的(20135213林涵锦)

    mykernel实验指导(操作系统是如何工作的) 实验要求 运行并分析一个精简的操作系统内核,理解操作系统是如何工作的 使用实验楼的虚拟机打开shell cd LinuxKernel/linux-3. ...

  3. Linux内核分析--操作系统是如何工作的

    “平安的祝福 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 一.初 ...

  4. 20135239益西拉姆 Linux内核分析 操作系统是怎样工作的?

    益西拉姆+ 原创作品+ <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 堆栈 堆栈是C语言程序运行时 ...

  5. LInux内核分析——计算机是如何工作的进行

    万子惠 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 " 实 ...

  6. linux内核分析--计算机是如何工作的

    实验部分 使用gcc -S -o main.s main.c -m32命令将源代码编译成汇编代码. 源代码如下: int g(int x) { return x + 9; } int f(int x) ...

  7. Linux内核分析— —计算机是如何工作的(20135213林涵锦)

    实验部分 (以下命令为实验楼64位Linux虚拟机环境下适用,32位Linux环境可能会稍有不同) 使用 gcc –S –o main.s main.c -m32 命令编译成汇编代码, int g(i ...

  8. Linux内核分析——计算机是如何工作的

    马悦+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.计算机是如何工作的 ( ...

  9. Linux内核分析 计算机是如何工作的——by王玥

    1.冯诺依曼体系结构:也就是指存储程序计算机 硬件(存储程序计算机工作模式): 软件(程序员角度): 2.API:程序员与计算机的接口界面 ABI:程序与CPU的接口界面 3.X86的实现: 4.X8 ...

随机推荐

  1. 卷积神经网络入门:LeNet5(手写体数字识别)详解

    第一张图包括8层LeNet5卷积神经网络的结构图,以及其中最复杂的一层S2到C3的结构处理示意图. 第二张图及第三张图是用tensorflow重写LeNet5网络及其注释. 这是原始的LeNet5网络 ...

  2. SDN期末

    一.项目描述 负载均衡程序 二.小组分工 组名:我们真的很弱 组员:李佳铭.吴森杰.张岚鑫.薛宇涛.杨凌澜 三.代码演示及过程描述 四.课程总结

  3. centos7装机教程

    U盘启动电脑进入安装界面 正常情况下你应该会看到下面的这个界面: 选择第一项,然后按TAB键,然后会看到下面这个: 3.修改第二步中按TAB键出来的命令 将命令修改为:>vmlinuz init ...

  4. 网络编程_TCP协议_客户端与服务端

    客户端发数据到服务端 Tcp传输,客户端建立的过程. 1,创建tcp客户端socket服务.使用的是Socket对象.建议该对象一创建就明确目的地.要连接的主机. 2,如果连接建立成功,说明数据传输通 ...

  5. python openpyxl.md

    Openpyxl 创建一个工作簿 下面是创建一个工作簿.而每个工作簿至少一个工作表我们可以通过active获取正在运行的工作表. In [1]: from openpyxl import Workbo ...

  6. HTML标签之marquee

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/zkn_CS_DN_2013/article/details/25229719 <html> ...

  7. Redis系列一:reids的单机版安装

    环境:VM+centos6+xshell 1.准备好linux的虚拟机,这里用的是centos6,具体方法百度 2.进入software目录,创建一个redis的目录来存放下载的redis包 cd s ...

  8. Odoo开发调试技巧

    转载请注明原文地址:https://www.cnblogs.com/cnodoo/p/9307490.html  一:Odoo控制台error日志一般为以下格式 Traceback (most rec ...

  9. 写出js内存泄漏的问题?

    回答一: (1)IE7/8 DOM对象或者Active对象循环引用导致内存泄漏 a.多个对象循环引用 b.循环的DOM泄漏 (2)基础的DOM泄漏 当原有的DOM被移除时,子节点引用没有被移除则无法回 ...

  10. 20165302Exp0 Kali安装 Week1

    一,下载 下载网址https://www.kali.org/downloads/ 二,安装(安装过程中有一部分没有截图,因此没有贴上) 创建虚拟机 选择Linux,版本Ubuntu 一直下一步,最后点 ...