董涛
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
本文通过编写一个简单的时间片轮转多道程序内核代码,阐述操作系统进程的启动和进程的切换机制。
首先,进入实验楼(www.shiyanlou.com)的Linux内核分析实验平台,该实验平台已经预装了部分linux操作系统的内核,主要用来初始化硬件,这部分内核在文件夹linux-3.9.4中,打开linux源代码文件夹linux-3.9.4,并进入该文件夹下的mykernel文件夹,如图所示:
在mykernel文件夹中创建头文件mypcb.h,在mypcb.h中我们主要定义了线程结构体和进程结构体,为操作系统管理线程和进程提供框架。程序源代码截图如下:
/*
* linux/mykernel/mypcb.h
*
* Kernel internal PCB types
*
* Copyright (C) 2013 Mengning
*
*/
#define MAX_TASK_NUM 4
#define KERNEL_STACK_SIZE 1024*8
/* CPU-specific state of this task */
struct Thread {
unsigned long ip;
unsigned long sp;
};
typedef struct PCB{
int pid;
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
char stack[KERNEL_STACK_SIZE];
/* CPU-specific state of this task */
struct Thread thread;
unsigned long task_entry;
struct PCB *next;
}tPCB;
void my_schedule(void);
接下来,我们在mykernel文件夹中编辑操作系统的两个文件mymain.c和myinterrupt.c,mymain.c文件是操作系统函数的入口,myinterrupt.c文件实现操作系统进程之间的切换。现在我们逐个分析这两个文件中的关键代码,先看mymain.c,其关键代码截图如下所示:
在mymain.c文件中,关键内核代码是期中的汇编段,首先是把某个进程的线程栈顶指针存到esp寄存器中,第二步,将栈基址指针压入栈中,第三步,将该进程中某个线程的ip指针压入栈中,第四步,线程结束后,将该线程的ip指针出栈,第五步,该进程结束后,将基址指针出栈。
再来看myinterrupt.c文件,在myinterrupt.c文件中,关键的代码有两部分,一部分代码针对的情况是:当前进程切换到下一个进程时,下一个进程本身已经在运行,另一部分代码针对的情况是:当前进程切换到下一个进程时,下一个进程还没有运行,我们只分析第二部分的关键代码,其关键代码截图如下所示:
在myinterrupt.c文件的这部分代码中,现将当前的进程堆栈栈底和栈顶指针保存,然后调用下一个进程并运行。
总结一下:操作系统内核在对硬件完成初始化以后,搭建了堆栈的框架,并定义了程序的入口,同时实现了进程间切换的功能。