操作系统:线程总结

 

日期: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进程都有独立的栈。

posted @ 2019-05-04 15:11  sinkinben  阅读(1431)  评论(0编辑  收藏  举报