- 本系列博客为观看哈工大李治军老师的操作系统视频而做的整理总结。
- 本文为第五篇,主要是内核级线程及编码实现
关键词索引:
多核、内核栈、用户态、内核态
一、内核级线程的引入
多线程才能发挥多核的优势。 因为共用了一套资源。
多进程不能发挥优势。
核心是实现两套栈 (用户栈+内核栈)
带着中断才能进入内核 int 0x80
EFLAGS 中断返回标志
304 是IP 地址, 即中断后的返回地址。
cs是段基址。 此处为100。
进了线程T ,执行一小段后要执行用户态的代码。
所以????应该是一段包含iret的代码,从中断返回到用户态
进入内核,通过TCB切换完成内核栈的切换,完成用户态的切换
如果不是同一进程,还有内存地址映射表的切换
如果是同一进程,那就是线程
二、内核级线程的代码实现
fork() 是创建资源的系统调用
此处,先是用户态执行
函数A()发生调用,要在用户栈中保存恢复场景:
将A()返回后将要执行的函数入口地址压入栈中 A:ret=B
在A()中执行,遇到fork (),又发生了系统调用:
要转到系统调用,先要在内核栈中保存恢复场景:
先在内核栈中将SS:SP关联用户栈 (用户栈的基址和指针偏移量,如箭头所示)
因为要调用中断,将中断标志位EFLAGS压入,以便处理完代码后完成中断返回
要执行中断处理函数,所以需将中断后的PC指针地址压入栈中
要用中断要发生system _call调用, 在这之前先将中断后的指令地址压入
即ret =?? 指针指向箭头所示。
接下来是0x80进内核:
system_call 又是一系列压栈的过程
call sys_fork 过程中,看PCB控制块(记录进程信息的数据结构)中的进程状态,
看多进程图像一文中关于PCB的部分
判断是否需要发生调度。 阻塞或时间片用完均需要调度。 否则可以正常返回。
ret_from_sys_call
如果发生重新调度
首先是把返回地址入栈,然后跳转执行schedule ,完成中间的切换
中间切换后相当于线程切换了。
_schedule 是C函数 ,当执行到 } ,函数返回后弹出 ret_from_sys_call ,开始中断出口部分:
共用用户栈,但内核栈分开
中断返回后,父进程子进程都执行MOV res ,%eax
eax 非零时执行一段代码(父进程)
eax=0时执 行子进程的代码。 (子进程中将eax置0了,在填写栈的时候)