操作系统:线程总结
日期:2019/5/3
关键词:操作系统;线程。
一、线程与进程
进程的特点:
- 资源所有权:进程对资源(内存、I/O通道、I/O设备、文件等)具有控制权。
- 调度/执行:进程是OS调度和分派的实体。
1.1 多线程模型
关键点:
- TCB控制块:寄存器的值、程序计数器、栈指针、优先级等与线程相关的状态信息。
- 所有线程共享进程的状态和资源。例如,全局变量,文件描述符表。当一个线程以读权限打开文件,其他线程也能读取(无需重复open)
线程的优点:
- 创建或者终止线程时空效率高。(Unix中,创建线程比创建进程的时间快10倍)
- 线程切换比进程切换所花时间少。
- 通信效率高。进程需要内核介入才能通信。
二、线程模型分类
2大类:用户级线程(User Level Thread)和内核级线程(Kernel Level Thread)。
2.1 ULT
ULT线程管理由应用程序完成,内核意识不到线程的存在。(通信、调度等通过线程库实现)
ULT线程调度与进程调度的关系,也是ULT与KLT的本质区别。
初始状态: 线程库中:线程2运行 OS内核中:进程B运行 | |
假设1:线程2进行了一个将会阻塞进程B的系统调用(例如IO操作)。那么: 内核把B阻塞,切换到另外一个进程。 但在线程库的角度看来,线程2仍处于运行状态。(但从CPU的角度看,不是真的运行) | |
假设2:时钟中断到来,B用完其时间片,内核进行进程切换,B从运行转为就绪。但在线程库的角度看来,线程2仍处于运行状态。 | |
假设3:线程2运行到需要线程1执行某些动作的一个点。(例如,1负责数据输入,2负责计算和输出这种情况) 线程2阻塞,线程库调度线程1运行。 |
总结:
- b-d的三种状态其实都是a的过渡。
- 在b和c中,内核把控制权切换到进程B,线程2就会回复执行。
- 进程在执行线程库代码时可被中断,在中断时可能处于线程切换状态。(进程被恢复时,才能完成线程切换)
ULT优点:
- 线程库在用户空间当中,线程切换不需要陷入内核。(节省2次上下文切换)
- 可以自定义线程调度算法。
- ULT可在任意OS中使用,不需要修改内核。
ULT缺点:
- 有一个ULT进行系统调用,那么所有线程都会阻塞。
- 纯ULT策略,多线程的程序不能使用CPU的多核技术。(因为内核调度的是进程,所以在纯ULT策略下,多线程并不是真的在并行)
2.2 KLT
纯KLT下,线程管理由内核完成,应用级没有线程管理的代码(只提供API)。
如上图4.5(b),此时内核的调度单位是线程,一个线程阻塞,其他线程仍然在真的执行(前提CPU是多核)。
但线程调度需要陷入内核,时间花销更大。
一组对比实验:
- Null Fork:创建、调度、执行和完成调用一个空过程的进程/线程的时间。(派生一个线程/进程的开销)
- Signal-Wait:进程/线程给正在等待的进程/线程发信号,然后在某个条件上等待需要的时间。(完成一次同步的开销)
2.3 ULT+KLT组合方式
三、Linux的线程和进程管理
3.1 Linux进程
Linux中的进程/任务由一个task_struct表示,它包含以下信息:
- 进程的状态(执行、就绪、挂起、停止、僵死)
- 调度信息:一个进程可能是普通或实时的,并具有优先级,实时进程在普通进程之前调度。
- 标识符:PID,用户标识符和组标识符。组标识符用于给一组进程指定资源访问权。
- 进程通信:支持Unix SVR4通信。
- 链接:到达父子进程的链接。
- 时间和计时器:包括进程创建时刻和进程消耗CPU的时间总量。一个进程还可以有若干个间隔计时器,通过系统调用来定义,计时器满,OS会给该进程发送一个信号。计时器可以只使用一次或者多次。
- 文件系统:包括被该进程打开的任何文件的指针和指向该进程当前目录的指针。
- 地址空间:定义分配给该进程的虚拟地址空间。
- CPU专用上下文:寄存器、栈指针。
3.2 Linux线程
传统Linux内核:
- 单线程进程,不支持多线程
- 多线程程序需要用户线程库实现(也就是纯ULT策略),最著名的就是pthread库了。(所以即使在多核CPU环境下,使用pthread库写的多线程排序时间效率上并不比单线程好,因为这种情况下CPU调度进程(在CPU看来这个进程只有一个线程),实际上就是多个线程轮流使用CPU的某个核)
现代Linux内核:
- 不区分进程和线程
- 提供内核级线程的实现
- 将ULT映射到内核级进程
- 组成一个用户级进程的多个ULT则映射到共享同一个组ID的多个Linux内核进程上(这些进程可共享文件和内存等资源,使得同一个组的进程调度不需要上下文切换)。
fork底层是通过clone系统调用实现,将所有标志位清零。标志位如下:
当进行进程切换时,内核先检查当前进程的页目录地址是否与被调度的进程相同。若相同,则他们共享一个地址空间,此时上下文切换仅仅是PC指针的跳转。注意:clone可以使同一个进程组的克隆进程共享同一内存空间,但不能共享同一个用户栈,每个clone进程都有独立的栈。