L10_用户级线程
线程与进程
:::info
- 进程 = 资源 + 指令执行序列
- 将资源和指令执行分开
- 一个资源 + 多个指令执行序列
- 线程: 保留了并发的优点, 避免了进程切换代价
- 实质就是映射表不变而PC指针变
切换线程——指令切换(PC指针切换),资源不切换(不更换映射表) :::
一个例子:网页浏览器
void WebExplorer() {char URL[] = “http://cms.hit.edu.cn”;char buffer[1000];pthread_create(..., GetData, URL, buffer); //GetDatapthread_create(..., Show, buffer); //Show}void GetData(char *URL, char *p){...};void Show(char *p){...};
如何切换?

跳转时会将当前程序下一个程序的地址压入栈中
程序结束遇到
}会将栈中的地址弹出,转到该地址; :::danger 若是两个程序只是用一个栈,会发生混乱; :::
Yield切换时要先切换栈,进程1的地址压在TCB1的栈,进程2的地址压在TCB2的栈;ThreadCreate
线程的核心便是TCB、栈、切换的PC;因此在创建线程时,需要创建这三者;

void ThreadCreate(A){TCB *tcb=malloc();*stack=malloc();*stack = A;//100tcb.esp=stack;}
用户级线程和核心级线程的差异
用户级线程仍会出现阻塞的现象;
某个线程进入内核并阻塞,CPU并不知道TCB的信息,也不会调用其他的线程,因此会卡住;
- 与之区别的是核心级线程,由系统调用,会进入内核,知道TCB(但是不用跳转进程)
L11_内核级线程
内核级线程与用户级线程的差别
:::info
- 用户级:两个栈
内核级:两套栈 :::
:::info
用户栈会和内核栈组成一种关联的关系;
*当用户调用INT中断时,会进入内核; :::如何切换
用户调用int指令,进入中断
- 中断处理,启动磁盘读或是时钟中断:
schedule()引发切换 - schedule内使用swith to函数,实现内核切换
- 切换后执行一段中断函数,最后应该右iret指令,从中断弹出,进入切换后的线程
:::info
直观上讲,有两级切换,先是内核级的切换,从线程S的代码段切到线程T的代码段
然后中断结束回到线程T(因为栈也换了)
:::
Summar
L12_内核级线程的实现
用户栈进入内核
main(){A();B();}A(){fork();}//遇到fork系统调用,通过中断进入内核mov %eax,__NR_forkINT 0x80mov res,%eax
从main()进入A()时,用户栈会压入下一条指令的地址:ret=B
从内核进中断
调用中断进入内核后,内核栈要和用户栈联系起来,栈寄存器指向用户栈,因为要去执行中断函数,因此也要保存内核下一条指令的地址,即mov res,%eax的地址。然后调用中断处理函数system_call
_system_call:push %ds..%fspushl %edx... //首先保存现场,此时保存的这些内容还是当前的用户栈的call sys_fork //调用fork创建一个子进程//========================pushl %eaxmovl _current, %eaxcmpl $0,state(%eax)jne reschedulecmpl $0, counter(%eax)je reschedule//========================ret_from_sys_call:
:::info
系统判断当前进程是否发生阻塞、需要切换到新的进程是通过上面的两个cmpl来进行的;
- 第一个比较状态信息是否为非零,若是非0则说明阻塞了,执行
schedule切换 -
切换到其他进程
这里先解释切换五段论中的最后一个,如何从内核回到用户栈。
reschedule:pushl $ret_from_sys_calljmp _schedule
reschedule首先在当前内核栈中保存返回后的下一条指令,即弹栈ret_from_sys_call然后才执行切换线程(PCB的切换)
void schedule(void){next=i;switch_to(next);}
:::tips
switch_to切换到下一个就绪的进程,实际上是PCB的切换!也就是说现在内核栈换了!switch_to末尾的ret会执行弹栈,这时因为已经换成新的内核栈了,ret后会跳到新的用户栈。 :::ret_from_sys_call:popl %eax //返回值popl %ebx.hpop %fs...iret //返回到哪里呢?INT 0x80后面执行!//res的值是返回值,实际上是%eax的值
:::info 若是从schedule出来的话也会执行弹栈,弹栈后会执行
ret_from_sys_callret_from_sys_call会执行弹栈操作,因为内核栈已经切换了,所以这时候弹栈实际上是弹的另一个进程的用户栈,执行结束后iret也会进入到新进程的用户栈 :::fork创建线程
