【操作系统】4、线程
内容概要:
用户级线程和核心级线程
第零部分:线程的概念
百度词条:线程(英语:thread)是操作系统能够进行运算调度的最小单位。
它被包含在进程之中,是进程中的实际运作单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
下面内容转载于:https://www.zhihu.com/people/lin-jia-jun-24-83-77/posts
第一部分:用户级线程
我们都知道进程,进程的切换我们需要,PCB保存当前进程状态,PC指针和映射表的切换。
那么有没有这种情况,我们只需要一份资源,却要用这份资源来做很多事。这种情况由于只需要一份内存资源,所以只需要一个映射表就够了。
这就产生了线程的概念,就是只切换pc指针却共用同一份内存。
接下来我们来谈谈线程间是怎么切换的。
就像进程的切换一样,我们需要保存当前执行到哪一步,然后再切换至下一个进程,进程的记录靠PCB(进程控制块),那么线程的记录靠什么呢?当然是TCB(线程控制块)了。
线程控制块是有栈组成的,这个栈保存着PC。当系统调用Yield(线程切换函数)方法。
100:A(){ 300:C(){ B(); D(); 104; 304 ; } } 200:B(){ 400:D(){ Yield(); Yield(); 204; 404; } }
esp: 104 204 304 404(出问题了,弹栈的时候地址跑到另一个线程去了)
所以,必须得要两个栈,我们在切换线程的时候,切换栈就好了。每个线程都有自己的栈,这样子执行方法就不会跑到别的线程去了。
esp:104 esp:304 204 esp:404
那么现在事情就简单了,我们将各个线程的栈存放到TCB里,TCB里存放栈的地址,我们切换就在TCB 来调度,选择一个线程的栈切过去,这样TCB 和栈相互配合,就可以进行线程的切换。于是Yield方法也很明显了
void Yield(){//切换栈就好了 TCB2.esp=esp; esp=TCB1.esp; }
于是两个线程的样子很明显了,两个线程两个栈,切换的pc在栈中。这样子的切换不需要操作系统,不需要进入内核就可以完成切换,用户自己就能搞定。
第二部分:内核级线程
前面我们提到用户级线程的切换,是通过切换线程栈来实现的,由TCB线程控制块来控制,用户自己就能实现,无需通过内核。这次我们来讲讲内核级线程以及它的切换。
内核级线程,顾名思义,就是处于内核态的线程,对于内核级线程,可以很好配合多核CPU,多核指的是MMU(映射)一样,但是有多个核可以并行的处理事件。
回顾上次所说的用户级线程的切换,是两个栈之间的切换,那核心级线程的切换是什么呢?不是两个栈,而是两套栈。
因为核心级线程必须到内核态,在用户态使用用户栈,在内核态不也得调用函数,也得使用一个栈,既要在用户态又得在内核态跑,一个核心级线程要两个栈组成的一套栈。
对比用户级线程的切换使用TCB进行切换,核心级线程也用TCB切换,不过这个TCB在内核中,而且根据TCB的切换来切换一套栈,内核栈和用户栈都得切换。
那么具体的如何从一个核心级线程切到另一个核心级线程呢?这就是大名鼎鼎的五段论了。
首先我们得从当前的用户栈切到内核栈,只有通过系统中断才可以进入内核,INT就是系统中断,通过在内核栈里面存储SS,SP来记录切换回来的指针,我们就可以切入内核栈了,如果想返回,可以调用IRET的系统调用返回,这就是一套栈了。
100:A(){ B(); 104; } 200:B(){ read(); 204; } 用户程序300:read(){ | int 0x80; | 304; | } | system_call; 内核程序call sys_call 1000; 2000:sys_read(){ }
如上图,我们调用read()调用系统中断进入内核态,现在的栈的情况是
用户栈:104 204 内核栈:SS SP(记录返回指针) EFLAGS 304 CS 1000
可以看到这两个栈配合的非常好,这就是一套栈的精髓所在,也完成了切换的第一段。
由于read需要读磁盘,这会使线程进入堵塞阶段,引发CPU调度,switch_to通过TCB的切换(第二段)找到另一个线程B的内核栈的指针,然后切到另一套栈(第三段),也就是线程的切换,这是切换的第二、三段。
但是我们执行的是用户的函数,所以我们还得切到线程B的用户栈(第四段),这里我们执行完内核栈的函数,通过弹栈中断返回到SS SP弹回线程B的用户栈,这就是切换的第五段。
如果是进程的切换的话,我们另外切换地址映射表就可以了。