linux内核分析--操作系统是如何工作的?
一个简单的时间片轮转多道程序
- 操作系统的“两把剑”:中断上下文(保存现场和恢复现场)和进程上下文的切换
源代码的分析
*使用的源代码为视频中所使用的精简内核的源代码
首先分析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内核分析--操作系统是如何工作的?的更多相关文章
- Linux内核分析——操作系统是如何工作的
万子惠 + 原创作品转载请注明出处 + <Linux内核分析> 实验部分 使用实验楼的虚拟机打开shell 然后cd mykernel 您可以看到qemu窗口输出的内容的代码mymain. ...
- Linux内核分析— —操作系统是如何工作的(20135213林涵锦)
mykernel实验指导(操作系统是如何工作的) 实验要求 运行并分析一个精简的操作系统内核,理解操作系统是如何工作的 使用实验楼的虚拟机打开shell cd LinuxKernel/linux-3. ...
- Linux内核分析--操作系统是如何工作的
“平安的祝福 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 一.初 ...
- 20135239益西拉姆 Linux内核分析 操作系统是怎样工作的?
益西拉姆+ 原创作品+ <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 堆栈 堆栈是C语言程序运行时 ...
- LInux内核分析——计算机是如何工作的进行
万子惠 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 " 实 ...
- linux内核分析--计算机是如何工作的
实验部分 使用gcc -S -o main.s main.c -m32命令将源代码编译成汇编代码. 源代码如下: int g(int x) { return x + 9; } int f(int x) ...
- Linux内核分析— —计算机是如何工作的(20135213林涵锦)
实验部分 (以下命令为实验楼64位Linux虚拟机环境下适用,32位Linux环境可能会稍有不同) 使用 gcc –S –o main.s main.c -m32 命令编译成汇编代码, int g(i ...
- Linux内核分析——计算机是如何工作的
马悦+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.计算机是如何工作的 ( ...
- Linux内核分析 计算机是如何工作的——by王玥
1.冯诺依曼体系结构:也就是指存储程序计算机 硬件(存储程序计算机工作模式): 软件(程序员角度): 2.API:程序员与计算机的接口界面 ABI:程序与CPU的接口界面 3.X86的实现: 4.X8 ...
随机推荐
- Django商城项目笔记No.18商品部分-数据表创建
数据库表设计 在电商中对于商品,有两个重要的概念:SPU和SKU SPU = Standard Product Unit (标准产品单位) SPU是商品信息聚合的最小单位,是一组可服用.易检索的标准化 ...
- Python接口自动化--Json数据处理 5
1.Json模块简介,全名JavaScript Object Notation,轻量级的数据交换格式,常用于http请求中. Encoding basic Python object hierarch ...
- 死磕nginx系列--使用nginx做负载均衡
使用nginx做负载均衡的两大模块: upstream 定义负载节点池. location 模块 进行URL匹配. proxy模块 发送请求给upstream定义的节点池. upstream模块解读 ...
- BZOJ2815:[ZJOI2012]灾难(拓扑排序,LCA)
Description 阿米巴是小强的好朋友. 阿米巴和小强在草原上捉蚂蚱.小强突然想,如果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的生态灾难. 学过 ...
- Android Bitmap Drawable byte[] InputStream 相互转换方法
用android处理图片的时候,由于得到的资源不一样,所以经常要在各种格式中相互转化,以下介绍了 Bitmap Drawable byte[] InputStream 之间的转换方法: import ...
- numpy库数组拼接np.concatenate的用法
concatenate功能:数组拼接 函数定义:numpy.concatenate((a1, a2, ...), axis=0, out=None)
- CentOS中配置CDH版本的ZooKeeper
三台CentOS:Host0,Host1,Host2 在三台中分别安装zookeeper-server yum install zookeeper-server -y 修改zookeeper的配置文件 ...
- python中安装Tensorflow
执行命令:pip install --upgrade tensorflow 即可.
- 20155209林虹宇 Exp7 网络欺诈防范
Exp7 网络欺诈防范 简单应用SET工具建立冒名网站 kali要作为web服务器让靶机访问冒名网站,所以要使用阿帕奇web服务器软件. 要阿帕奇使用80端口.进入配置文件/etc/apache2/p ...
- Eclispe与JDK
一.Eclispe 1.1 Eclispe发布版本 下面是目前已知的版本代号 (Release)[2015年2月] Eclipse 3.1 版本代号 IO [木卫1,伊奥] Eclipse 3.2 版 ...