《Linux内核分析》 week2作业-时间片轮转

一.基于时间片轮转调度代码的解读

代码结构主要由三个文件组成:

1.mypcb.h

2.myinterrupt.c

3.mymain.c

1.进程控制块(mypcb.h)

/* CPU-specific state of this task */
struct Thread{
   unsigned long ip;  //eip,程序入口地址
   unsigned long sp;  //堆栈esp栈顶地址
};

typedef struct PCB{
   int pid;  //进程pid号
   volatile long state; //进程运行状态,-1 unrunnable,0 runnable,>0 stopped
   char stack[KERNEL_STACK_SIZE]; //进程的栈空间
   /* CPU-specific state of this task */
   struct Thread thread;  //CPU的相关状态
   unsigned long task_entry; //进程运行对应的函数
   struct PCB *next;  //下一个进程块地址
}tPCB;

void my_schedule(void);

这里进程控制块(PCB)是采用链表的形式链接起来的。

2.进程的切换(myinterrupt.c)

extern tPCB task[MAX_TASK_NUM]; //进程控制块数组
extern tPCB *my_current_task;  //当前对应的进程控制块
extern volatile int my_need_sched; //标志字段,来表示是否需要对进程进行调度

//时钟中断,周期性调用这个函数
void my_timer_handler(void){
#if 1
    if(time_count%1000==0 && my_need_sched!=1){
        printk(KERN_NOTICE">>>my_timer_handler here<<<\n");
        my_need_sched=1;
    }

    time_count++;
#endif
    return;
}

接下来是进程上下文切换最关键的代码(my_schedule函数),采用的是内嵌汇编代码

当下一个进程的状态是正在运行时,则

if(next->state==0){// -1 unrunnable,0 runnable,>0 stopped
    /*switch to next process */
    asm volatile(
        "pushl %%ebp\n\t"    //保存当前进程的栈基址指针 ebp
        "movl  %%esp,%0\n\t" //保存当前进程的栈顶指针 esp
        "movl %2,%%esp\n\t"  //回复下一个进程的栈顶指针 esp
        "movl $1f,%1\n\t"    //将当前进程的下一个指令地址保存到thread.ip中
        "pushl %3\n\t"       //将下一个进程的指令执行地址入栈
        "ret\n\t"            //ip出栈,
        "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)
    );
} 

若下一个进程是新的进程,还没有执行过,基本方式与上面差不多,也是基于内嵌汇编方式实现上下文切换。

3.内核代码运行(mymain.c)

 内核从my_start_kernel开始执行,my_start_kernel主要是进行进程控制块的初始化,同时启动0号进程。

void __init my_start_kernel(void)
{
    int pid = 0;
    int i;
    /* Initialize process 0*/
    task[pid].pid = pid;
    task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
    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(i=1;i<MAX_TASK_NUM;i++)
    {
        memcpy(&task[i],&task[0],sizeof(tPCB));
        task[i].pid = i;
        task[i].state = -1;
        task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
        task[i].next = task[i-1].next;
        task[i-1].next = &task[i];
    }
    /* 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号进程开始执行my_process函数。

4.程序运行的结果

二.实验总结

通过完成这个简单的时间片轮转多道程序的实验,让我更加深刻的理解了进程上下文切换的原理,以及内核启动的初始化过程。

时间: 2024-10-20 00:10:06

《Linux内核分析》 week2作业-时间片轮转的相关文章

linux内核分析作业:操作系统是如何工作的进行:完成一个简单的时间片轮转多道程序内核代码

计算机如何工作 三个法宝:存储程序计算机.函数调用堆栈.中断机制. 堆栈 函数调用框架 传递参数 保存返回地址 提供局部变量空间 堆栈相关的寄存器 Esp 堆栈指针  (stack pointer) Ebp 基址指针 (base pointer) 堆栈操作 Push:pop Ebp用作记录当前函数调用基址- 其他关键寄存器   中断 Call指令:1.将eip中下一条指令的地址A保存在栈顶:2.设置eip指向被调用程序代码开始处 1.Call xxx 2.进入xxx pushl %ebp mov

魏昊卿——《Linux内核分析》第二周作业:了解操作系统是怎样工作的

魏昊卿——<Linux内核分析>第二周作业:了解操作系统是怎样工作的 一.实验部分 使用实验楼的虚拟机打开shell cd LinuxKernel/linux-3.9.4 qemu -kernel arch/x86/boot/bzImage 然后cd mykernel 您可以看到qemu窗口输出的内容的代码mymain.c和myinterrupt.c 使用自己的Linux系统环境搭建过程参见mykernel,其中也可以找到一个简单的时间片轮转多道程序内核代码 mymain.c myinterr

魏昊卿——《Linux内核分析》第三周作业:Linux系统启动过程

魏昊卿——<Linux内核分析>第三周作业:Linux系统启动过程 一.实验部分 实验指导 使用实验楼的虚拟机打开shell 1 cd LinuxKernel/ 2 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img 内核启动完成后进入menu程序(<软件工程C编码实践篇>的课程项目),支持三个命令help.version和quit,您也可以添加更多的命令,对选修过<软件工程C编码实践篇>

Linux内核分析作业 NO.2

操作系统是如何工作的 于佳心 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000  本章所学内容主要围绕着三个方面. 1.函数调用堆栈 2.中断机制 3.mykernel上实际操作构建内核 操作系统的三个法宝指的是:存储程序计算机,函数调用堆栈,中断机制 其中函数调用堆栈是高级语言的起点,它的作用是记录调用路径和参数(调用框架,传递参数,保存返回地址,提供局部变量空间) 这里涉及到了esp,

Linux内核分析—完成一个简单的时间片轮转多道程序内核代码

---恢复内容开始--- 20135125陈智威 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 实验要求: mykernel实验指导(操作系统是如何工作的) 运行并分析一个精简的操作系统内核,理解操作系统是如何工作的 使用实验楼的虚拟机打开shell cd LinuxKernel/linux-3.9.4 qemu -kernel arch/x86/boot/bzImage 然后cd

Linux内核分析:完成一个简单的时间片轮转多道程序内核代码

PS.贺邦   原创作品转载请注明出处  <Linux内核分析>MOOC课程    http://mooc.study.163.com/course/USTC-1000029000 1.mykernel实验指导(操作系统是如何工作的) 使用实验楼虚拟机打开shell输入下列代码 1 cd LinuxKernel/linux-3.9.4 2 qemu -kernel arch/x86/boot/bzImage 可以看到初始的内核运行情况如下: 内核不停的执行my_start_kernel(),每

Linux内核分析第五周作业

分析system_call中断处理过程 这次的目标是通过gdb来跟踪上周选择的uname系统调用.因为系统调用是通过中断在内核态实现的,gdb无法调试本机的系统调用.所以必须像之前的内核跟踪那样,用gdb远程连接至qemu虚拟机进行跟踪. 1. 首先修改之前的MenuOS,添加一个myuname函数通过API的方式调用uname系统调用,直接复制了上周的代码,注意要在main函数中把这个函数添加到菜单中 用make命令编译以后,会生成一个test的可执行文件,可以先在本地运行试一试 可以看到命令

linux内核分析作业3:跟踪分析Linux内核的启动过程

内核源码目录 1. arch:录下x86重点关注 2. init:目录下main.c中的start_kernel是启动内核的起点 3. ipc:进程间通信的目录 实验 使用实验楼的虚拟机打开shell cd LinuxKernel/ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img 使用gdb跟踪调试内核 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initr

Linux内核分析作业(1)——计算机是如何工作得?

根据163MOOC学院中国科学技术大学孟宁孟老师课程所写得博客 作者:肖冲冲 原创作品请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一,计算机的工作过程 计算机的基本原理是存储程序和程序控制(冯﹒诺依曼体系),简单来说,我们需要先把需要进行操作的指令(程序)和数据先输入到计算机的存储设备中,然后计算机将严格执行需要执行的指令,包括从那个地址取数(或指令),进行什么操作(加减移位等),然后再送回到什

《linux内核分析》第二次课 实验作业

潘聪  原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这次的实验主要是研究线程调度中的主动调度. --------------------------------------------- 一.基础结构 (1)进程:在mypcb.h中,进程由ip(eip)和sp(esp)共同组成. (2)进程控制块(PCB):进程号pid,状态(错误/运行中/停止运行),进程堆栈(包括内核栈和用户栈,