zoukankan      html  css  js  c++  java
  • 操作系统:线程总结

     

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

  • 相关阅读:
    10-12
    8-10
    5.2-5.3
    四则运算 测试与封装
    第5-7章
    汉堡包
    1-5章
    实验二
    实验一
    Controller方法是如何与请求匹配上的及参数如何填充的
  • 原文地址:https://www.cnblogs.com/sinkinben/p/10808579.html
Copyright © 2011-2022 走看看