用户级线程的设计和实现

1 用户级线程的概念

线程是在一个地址空间下启动并交替执行的程序,用户级线程用用户管理。

2 用户级线程的设计和实现

2.1 用户级线程切换Yield()

线程1,线程2如下所示,在线程1中调用Yield()来切换到线程2
在这里插入图片描述
进程的执行过程为
A() -> B() -> Yield() ->线程2-> C() -> D() -> Yield()
下面讨论如何实现Yield()

2.1.1 为什么先设计Yield()而不是ThreadCreate()

只要把一段程序做成可以用Yield切换进来的样子,并且CPU的进入点是这段程序的首地址,这段程序就可以执行了。所以创建一个用户级线程就是创建一个可以让CPU切换进去的初始样子。

2.1.2 Yield的第一个版本和缺陷

在B()中Yield()直接执行jmp 300。

Yield(2){
   
   
    jmp 300;//300可以用TCB记录,jmp TCB(1).EIP
}

在C()中Yield()直接执行jmp 204。

Yield(1){
   
   
	jmp 204;//204可以用TCB记录
}

此时进程的返回地址栈变化如下
在这里插入图片描述
D中执行完Yield(1)之后,进入204往下执行,一直执行到B函数的“ } ”,执行ret指令,弹出的是404,发生了错误!(应该是回到A函数中继续执行)。所以,直接jmp是行不通的!

2.1.2 Yield的第二个版本和缺陷

上述问题显然是由于两个线程共用一个栈产生的,解决办法当然是给每个线程都分配一个栈。用线程的TCB来存储线程的esp(不用存储ebp,因为可以公用一个ebp)
两个栈的图示如下
在这里插入图片描述
B中的Yield代码自然的想法就是不仅要切换PC到C的地址300去执行,还要修改当前esp为线程2的esp,代码如下

Yield(2){
   
   
    tcb1.esp = esp;
    esp = tcb2.esp;
    jmp 300;//jmp esp.EIP
}

D中的Yield代码为

Yield(1){
   
   
    tcb2.esp = esp;
    esp = tcb1.esp;
    jmp 204;//jmp esp.EIP
}

执行过程中的返回地址压栈如图所示
在这里插入图片描述
此时又出现问题了,D()中执行Yield()时遇到jmp 204,便开始顺着204朝下执行,但是在遇到B的" } “时,执行ret,栈弹出的是204,又返回到了204执行,这出现了错误。这个错误的原因在于Yield的 } 没有被执行。回顾刚刚的过程,204被压栈是因为要去执行Yield,但是Yield的” } "并没有被执行。

2.1.3 Yield的第三个版本

解决的办法就是删去Yield()中的jmp 204,因为这样就可以使得Yield的" } “得到执行,最重要的,这个” } "与jmp 204有着同样的效果。
B中的yield代码如下

Yield(2){
   
   
	tcb1.esp = esp;
	esp = tcb2.esp;
}

D中的yield代码如下

Yield(1){
   
   
	tcb2.esp = esp;
	esp = tcb1.esp;
}

返回地址栈的过程如下
在这里插入图片描述
总结如下:

  • 用户级线程的切换就是在切换位置上调用Yield()
  • Yield干的事情如下
    • 将当前的执行现场保存在当前线程的TCB中
    • 找到下个线程的的TCB的栈指针
    • 在下个线程的栈中弹出下个线程的执行现场
      用C语言来描述如下
Yield(){
   
   
	next = FindNext();
	push %eax
	push %ebx
	....
	mov %esp,TCB[current].esp
	mov TCB[current].esp,%esp
	....
	pop %ebx
	pop %eax
}

2.2 用户级线程创建函数ThreadCreate()

ThreadCreate(funcA)的核心功能如下:

  1. 创建一个TCB,用来存储funcA的执行现场信息
  2. 创建一个funcA的执行栈,栈的第一个压入的元素应该是funcA的起始地址,这样当别的线程执行Yield(A)时候,使用“ } ”的ret指令便可以开始执行funcA
  3. 把TCB和funcA的栈关联起来
    在这里插入图片描述
    ThreadCreate的核心代码如下
void ThreadCreate(A){
   
   
	TCB * tcb = malloc();
	Stack * stack = malloc();
	*stack = A;
	tcb.esp = stack;
}

3 (精华)工程上真正的实现用户级线程

3.1 环境配置

名称 配置
操作系统 win 10(64位)
编译器 MingGW 3.2.3(32位 老古董了)

3.2 实现目标

在屏幕上交替打印出A和B。即要让以下代码

void A(void * args){
   
   
	int A = (long)args;
	while(A-- > 0){
   
   
		printf("A");
		Yield();
	}
}
void B(void * args){
   
   
	int A = (long)args;
	while(A-- > 0){
   
   
		printf("B"
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值