跳转至

4.Threads

约 1448 个字 预计阅读时间 5 分钟

1. Motivation

  • 线程可以被操作系统内核单独调度,又天生可以共享内存;

  • image-20221124092738298

2. What is Thread

  • A thread is an independent stream of instructions that can be scheduled to run as such by the kernel.
  • 有些资源(pre-thread resources)比如(stack、registers、pc、thread- specific data)线程之间不隔离也不共享,所以其实是可以访问的;
  • 线程都是可以被单独调度的实体;
  • image-20221124093428235

  • 单线程和多线程

  • image-20221128081535899
  • 当程序运行在用户态时,就使用User Stack,当运行在内核态时,就使用Kernel Stack(内核栈在高位),因为如果调用了系统调用,那么成为了内核态运行内核代码之后,如果内核代码有函数调用,那么就需要栈(通过MMU保证内核栈不被用户态访问,来保护内核);
  • image-20221128081659757

3. Thread Benefits

  • 响应性好、资源共享、经济(轻量级)、可以并行(多个CPU core同时运行一个程序的多个线程);

  • image-20221128082400692

4. Multithreaded Server Architecture

  • 为了增加系统吞吐量:

    • 服务端fork()出来进程,父进程接受新的客户端连接,子进程执行要求比如读取网页;(但是需要系统调用,开销比较高)
    • 对于每一个用户的请求,都创建一个线程,执行请求后,直接把数据丢给客户端(因为共享资源);
    • 更高效的服务器往往使用多进程多线程的方式;
  • image-20221128082640196

5. Concurrent Execution on a Single-core System(Concerrency 并发)

  • 将一个任务切分成多个部分;

  • image-20221128083522711

  • image-20221128083738525

6. Concerrent Execution on a Single-core System(并行执行)

  • 同时执行;

  • image-20221128083534213

  • image-20221128083950722

7. Implement Threads

  • image-20221128090934839

  • 如果操作系统内核不支持线程,不妨碍用户态有线程,但是操作系统还是只能调度进程,至于用户态内部哪个线程获得了CPU资源,这是由用户态线程库自己决定;

  • 而只有操作系统内核有内核线程这个概念,他会为每个用户态线程创建一个内核线程进行绑定,从而对每个用户态线程进行调度;
7.1 Kernel-Level Threads
  • image-20221128091927104

  • 好处很明显了,缺点是需要消耗操作系统内核更多的资源,而针对缺点的低一点,实际上总的来说内核线程调度是会使程序变得高效,他的意思是创建一个内核态线程比创建用户态线程要慢,但是一旦创建完成,实际上给系统带来的好处是非常明显的;

  • image-20221128091942271
7.2 User-Level Threads
  • image-20221128092000828
  • 用户态不一定需要内核的支持才能创建线程, 在用户态将其切分成多个线程,如果没有内核线程,那么用户态线程只能并发而不能并行;
  • image-20221128092017808

8. Multithreading Models

8.1 Many-to-One
  • 多个用户态线程绑定到一个内核态线程;
  • 每次内核线程被调度的时候,回到用户态之后,先回 到用户态的调度器(运行时系统),由他来确定运行哪个用户态线程;
  • 这种模型在今天已经不存在了;
8.2 One-to-One
  • 一个用户态线程绑定到一个内核态线程;
  • image-20221128132109882
8.3 Many-to-Many
  • image-20221128132243205

9. Threading Issues

  • fork和exec的语义,信号处理都是进程的概念;终止线程;如何让线程都有自己的data;

  • image-20221128092943442

9.1 Senmatics of Fork and Exec
  • 本来fork的意思是duplicate整个进程里的所有资源;
  • 那么当线程去调用的时候,duplicate线程还是进程呢?
  • 有些系统是两个选择都有的;

  • 如果fork之后直接进行exec,那说明只用复制一个线程就好了;

  • image-20221128132801809

  • image-20221128132947287

9.2 Signal Handling
  • Call back(回调),可以为某个信号注册一个回调函数;比如segment fault,然后用户态可以注册一个回调函数,当内核发现访问了无效地址后,把信号发送给用户态,就会调用回调函数;
  • image-20221201081923168
  • default就是默认信号处理函数,比如直接退出;user-defined,就是用户自己注册的函数;
  • image-20221201082027637
9.3 Thread Cancellation
  • 如何结束一个线程?

  • image-20221201134838062

  • asychronous cancellation:就是立即结束;

  • image-20221201134908005

  • 关键区域:当多个线程要对一个共享区域进行访问的时候,我们称这个代码区域为关键区域;当一个代码被多个线程执行的时候(一串代码可以被多个线程执行),而当这个代码要对globle data或者heap进行访问时,如果不同步那就乱了,所以把访问globle data的代码段称为critical section;

  • 在同一个时刻只有一个线程能执行关键区域代码 ;
  • image-20221201134926347
  • target thread会周期性地检查自己是否被发送了结束请求;
9.4 Thread Specific Data
  • Thread-local storage(TLS) ,当我们使用线程时,想要全局变量的拷贝,使线程逻辑变简单;
  • 与local variable区别

    • 局部变量(存在栈上)只在function被调用时才存在;
    • 而TLS在所有function都是生效的,但是每个线程都有自己的一个拷贝;
    • 全局变量是每个进程有一个拷贝;
  • image-20221201082814238

  • 简单的实现方式:在globe区域划一个哈希表,然后通过pid去索引,得到data,这样可以实现不同线程访问同名变量是不同的拷贝;

9.5 Lightweight Process & Scheduler Activations
  • Lightweight process(LWP),每个LWP和内核态线程绑定,对于内核态来说,起着虚拟处理器的作用;

  • image-20221201084439233

10. Operating System Examples

10.1 Windows XP threads
10.2 Linux Threads
  • linux可以控制一些资源共享,一些资源不共享(通过flag来控制);

  • image-20221201210402806

11. Thread Libraries

  • image-20221201210630673

  • Pthreads: 用于线程的创建与同步,具体如下;在unix操作系统里很常见;

  • image-20221201210820945

  • image-20221201210913433

12. Pthreads Example

  • 进程从main函数开始运行,线程从thread_start开始运行;
  • 我们不希望多个线程同时对物理内存(全局变量)进行修改;

本文总阅读量