网站做qq发送链接,东莞网站搜索排名,电商网站建设参考文献,软件开发培训机构推荐【README】
1.本文内容总结自 B站 《操作系统-哈工大李治军老师》的《操作系统的那棵树》#xff0c;内容非常棒#xff0c;墙裂推荐#xff1b;
2.思维僵化与发散 the mind is not a vessel that needs filing, but wood that needs igniting. 头脑不是需要归档的容器内容非常棒墙裂推荐
2.思维僵化与发散 the mind is not a vessel that needs filing, but wood that needs igniting. 头脑不是需要归档的容器而是需要点燃的木头。 3.发散思维与各位共勉 单向思维灯丝材料出了问题换其他材料吧发散思维会不会是外部环境的问题而不是灯丝本身问题。如 真空环境 问题的维度发生改变【1】cpu运转起来
要管理cpu就要使用cpu 如何使用cpu 为pc寄存器设置初值然后cpu进行取指执行 【2】cpu运转效率低
cpu执行一会就需要等待一段时间效率低如 操作iocpu需要阻塞等待io响应
以操作磁盘为例cpu向磁盘控制器发送请求磁盘数据请求磁盘磁头需要寻址到具体位置读取数据到缓冲区准备好数据后磁盘控制器才会通知cpu说数据准备好了
在 磁盘被请求到磁盘准备好数据这一段时间cpu都只能等待阻塞所以效率低 【3】 解决cpu运行效率低的问题
1多个程序交替执行解决cpu低效问题
在程序1阻塞时cpu切换到程序2继续运行等待程序1阻塞结束后再切换到程序1执行
【注】本文程序指的是执行指令序列其中执行序列可以称为线程进程包含线程和执行资源 【4】多道程序交替执行的问题
从程序1跳转到程序2结合栈来修改cpu的pc寄存器值
当使用一个栈来实现程序切换从程序1某条指令A切换到程序2切换回程序1时无法正确切换到指令A的下一条指令
【例1】cpu使用同一个栈进行多道程序切换交替执行的问题
程序1-函数A 地址100的函数A在调用函数B前把104压栈以便返回后继续执行下一条指令即地址104上的指令下同接着调用函数B程序1-函数B 地址200的函数B执行时调用函数yield使得当前程序让出cpu给其他程序执行先把204 压栈再调用函数yield 程序2-函数C 因为程序1调用函数yield所以cpu切换到程序2执行调用程序2的地址300的函数C函数C先把地址304压栈再调用函数D程序2-函数D 执行函数D先把地址404压栈在调用函数yield 使得当前程序让出cpu给其他程序执行如程序1
【问题】函数D调用yield后cpu会切换到程序1执行
cpu执行程序1的下一条指令是栈顶弹出的404地址上的指令而不是程序1的下一条指令地址204 这显然是不对的因为404地址上的指令是程序2的这就会造成程序执行终止的情况因为整个程序状态不正确上下文不正确例1 如上图
【解决方法】基于各自栈的多道程序切换交替执行方式
每个程序各自单独使用一个内存栈多个交替执行的程序互不影响为了管理内存栈操作系统引入了线程控制块tcbtcb存储内存栈基址栈指针等栈元素对应地函数yield修改为 先找到程序2的tcb2通过tcb2找到新栈2进而切换到新栈2【小结】
操作系统引入的在多道程序中每道程序使用单独的内存栈解决了在用户态多道程序切换的问题 【5】内核态的多道程序切换问题
【5.1】背景
1线程会从用户态进入内核态内核态由于内存地址空间与用户态完全隔离所以内核态无法查看到用户态的栈也就无法切换到其他程序进程或线程 2当内核态线程在执行过程中阻塞cpu需要以某种方式切换到其他线程这种方式就是 内核态的栈切换
即 程序切换进程切换或线程切换需要切换一套栈包括用户栈和内核栈其中用户栈在用户态的内存地址空间内核栈在内核态的内存地址空间
3内核态线程切换步骤
Step1用户栈1切换到内核栈1Step2通过内核栈1找到tcb1Step3tcb1切换到tcb2Step4通过tcb2找到内核栈2并切换到内核栈2Step5内核栈2切换到用户栈2从而完成内核态的线程切换过程【6】多道程序切换用户态和内核态的具体代码实现
【代码例子】
在屏幕上交替打印出 A和 B
1业务C代码
main() {if (!fork()) {while(1) printf(“A”); }if (!fork()) {while(1) printf(“B”); }wait();
}2业务汇编代码
main() {mov __NR_fork, %eax // 系统调用编号 int 0x80 // 中断展开后调用系统调用进入内核
100: mov %eax, res // 子线程的eax是0父线程非0 cmpl res, 0 // res 与 0 比较 jne 208 // res不等于0则跳到208
200: printf(A) // 子线程代码 jmp 200
208: ... // 父线程代码
304: wait()
}3调用步骤 Step1INT中断进入内核 Int 0x80 中断调用 system_call system_call: call _sys_call_table(%eax,4)
Step2system_call 调用 sys_fork Step3sys_fork 调用 copy_process sys_fork pushl …… call copy_process ret
Step4copy_process 代码细节
copy_process根据父线程的模样做出了子线程包括TCB新的内核栈把TCB中的tss都初始化好把用户栈与内核栈关联起来tss存储了父线程执行时的物理寄存器的值包括eip100父进程当前执行指令的下一条指令的地址esp栈指针eax0
copy_process(... long eip, ...) // 参数列表为寄存器值列表
{p (PCB*) get_free_page();p-tss.esp0 p4k;p-tss.esp esp;p-tss.eax 0;p-tss.eip eip;...
}Tss 赋值 Tss-eip100 父线程当前执行指令的下一条指令的地址 Tss-espp4k 根据pcb内存起始地址偏移4k得到内核栈起始地址 Tss-espesp Tss-eax0 子线程tss的eax元素等于0与父线程非0区分开 【6.1】父线程创建完第一个子线程后返回
1业务C代码
main() {if (!fork()) {while(1) printf(“A”); } // 创建第1个子线程if (!fork()) {while(1) printf(“B”); } // 创建第2个子线程 wait();
}2业务汇编代码
main() {mov __NR_fork, %eax // 系统调用编号 int 0x80 // 中断展开后调用系统调用进入内核
100: mov %eax, res // 子线程的eax是0父线程非0 cmpl res, 0 // res 与 0 比较 jne 208 // res不等于0则跳到208
200: printf(A) // 子线程代码 jmp 200
208: ... // 父线程代码
304: wait()
}3父线程调用fork 创建完第1个子线程后接着调用fork创建第2个子线程 4最后父线程执行wait() 等待让出CPU让子线程执行
main()
{...... wait();
}C代码wait()函数的汇编代码
mov __NR_waitint 0x80
system_call:call sys_waitpid
sys_waitpid() // exit.c 文件中current-state TASK_INTERRUPTIBLE;schedule(); // 调度5schedule()调度函数的汇编代码调用switch_to() 函数
schedule()
{if ((*p)-state TASK_RUNNING (*p)-counter c) c (*p) - counter;next i; // 选择一个线程作为切换到的目标线程 ...switch_to(next);
}6switch_to()函数 主程序运行线程A打印字符A 但目标是交替打印A和B而不仅仅打印A 【6.2】交替打印A和B时钟中断
1借助时钟中断把线程A切换到线程B 2时钟中断C代码
void sched_init(void) // 在 sched.c 中
{set_intr_gate(0x20, timer_interrupt);
}void timer_interrupt:...call do_timer void do_timer(...)
{// 当前线程时间片计数先减去1然后判断其值 是否大于0若大于0则返回 if ( (--current-counter 0) ) return ; current-counter 0; // 若等于或小于0 则 切换到其他线程 schedule();
}【代码解说】
若 线程A时间片等于0则切换到线程B打印字符B
【补充】
只要为每个线程如线程A线程B设置时间片初值每次调用该值都会减1减1后若时间片值小于等于0则切换到其他线程执行进而实现线程交替切换交替执行的场景即交替打印AB【小结】操作系统演进过程
第一阶段 让cpu运行起来第二阶段 多道程序交替运行解决cpu运行低效问题第三阶段 引入了栈切换每道程序独享一套栈用户栈内核栈切换栈用户栈内核栈达到切换多道程序的目的使得程序可以交替运行 也可以说切换栈就是切换内核栈因为 内核栈切换包含了 用户栈的切换第四阶段 程序切换的触发条件有很多本文引入了时钟中断来实现 为每个程序1设置一个时间片初始值时钟每拨一次时间片值减1若值等于0则切换到其他程序2同样程序2的运行也有一个上限时间片一旦时间片等于0则切换到其他程序3