操作系统学习笔记(三) ---线程

例题

1.What are two differences between user-level threads and kernel-level threads? Under what circumstances is one type better than the other?

Answer:

用户线程和内核线程的不同是:

1) 站在操作系统的角度,用户线程是不被操作系统知道的,它由用户进程创建与管理;而内核线程由操作系统的调度算法调度,因此是被操作系统所得知的。

2) 针对多对一或多对多的线程模型,用户线程可以直接由线程库调度,而内核线程则需要由内核调度,也就是上文所说的调度算法。 对于用户线程和内核线程在不同的状况下的优劣比较:

内核线程比用户线程好的情况:

① 针对内核是单线程的情况,内核线程优于用户线程。因为任何执行阻塞系统调用的用户线程都会导致整个进程阻塞,即使应用程序中可以运行其他线程。例如,有两个进程a和b。进程a有2个内核线程,而进程b有2个用户线程。如果a中的一个线程被阻塞,它的第二个线程不会受到影响。但是对于b,如果一个线程被阻塞(比如I/O),整个进程b和第二个线程就会被阻塞。

② 针对多处理器环境,内核线程优于用户线程,因为内核线程可以在不同的处理器上同时运行,而即使有多个处理器可用,进程的用户线程也只能在一个处理器上运行。 

用户线程比内核线程好的情况:

对于时间共享型的内核,用户线程优于内核线程,因为共享系统上下文切换经常发生。内核线程之间的上下文切换具有很高的开销,几乎与进程相同,而与内核线程相比,用户线程之间的上下文切换几乎没有开销。

 

2.Describe the actions taken by a kernel to context-switch between kernel- level threads.

Answer:

切换内核线程上下文时,首先操作系统会从用户态转到内核态,然后内核在内核态保存正在关闭的线程所保存的cpu的寄存器,并恢复正在调度的新线程的cpu的寄存器。

内核线程的上下文切换需要先保存被切换线程的状态信息(包括cpu各寄存器的值和程序计数器在某一时间点的内容等)并加载切换线程的状态信息。

与进程上下文切换的区别:不需要切换PCB,但两者都要记录寄存器信息。

 

3.Which of the following components of program state are shared across threads in a  multithreaded proces?

a. Register values

b. Heap memory

c. Global variables

d. Stack memory

Answer:

线程之间共享堆和全局变量。因为堆是在进程空间中开辟出来的,所以肯定是跨线程共 享的;同理,全局变量是整个程序所共享的,也应由线程共享。而每个线程都会独立地维护属于自己的寄存器与栈。

 

4.Amdahl's Law

 

5.

Consider the following code segment:

pid t pid;

pid = fork();

if (pid == 0) { /* child process */

fork();

thread create( . . .);

}

fork();

 

a. How many unique processes are created?

b. How many unique threads are created?

Answer:

如果一个进程中有多个线程,fork()函数会将这些线程copy吗?

参考链接http://linux.die.net/man/2/fork

The child process is created with a single thread--the one that called fork(). The entire virtual address space of the parent is replicated in the child, including the states of mutexes, condition variables, and other pthreads objects; the use of pthread_atfork(3) may be helpful for dealing with problems that this can cause.

可以知道除了调用fork()的线程外,其他线程都会“消失”。

另外,一个进程本身就算一个线程。(如果不算的话这题的答案可能不一样)

下图是进程的创建过程

(a)可以知道一共有6个进程。

(b)P2 和 P3 会调用thread create ,所以这两个进程有两个线程 , 一共有8个线程

(之后的fork根据上述理由不会一次复制两个线程)

PS:关于线程的fork问题书上是这么解释的

如果程序中的一个线程调用fork(),那么新进程会复制所有的线程,还是新进程只有单个线程?有的UNIX系统有两种形式的fork(),一种复制所有线程,另一种只复制调用了系统调用fork()的线程。

如果一个线程调用了exec(),那么其指定的程序会替换整个进程,包括所有线程。

fork()的两种形式的使用与应用程序相关。如果fork之后执行exec,那么复制所有的线程是没有必要的。

 

6.The program shown in Figure 4.16 uses the Pthreads API. What would be the output from the program at LINE C and LINE P?

value为全局变量,父进程会等待子进程完成再打印,子进程在线程完成后打印value,

子进程会复制父进程的堆、栈、数据段等信息,两者是独立的(在子进程修改全局变量不会影响父进程中的同名全局变量),而进程中的线程会与该进程共享数据段(里面包括全局变量)和堆内存。

所以LINE C打印value值5       LINE P 打印value值0

概念部分

线程的特点

1)轻型实体

线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源。

线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB(Thread Control Block)描述。TCB包括以下信息:

(1)线程状态。

(2)当线程不运行时,被保存的现场资源。

(3)一组执行堆栈。

(4)存放每个线程的局部变量主存区。

(5)访问同一个进程中的主存和其它资源。

(6)用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈

2)独立调度和分派的基本单位。

在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小(在同一进程中的)。

3)可并发执行。

在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力。

4)共享进程资源。

在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的地址空间(进程的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核

线程是被系统独立调度的基本单位

 

优缺点分析

优点:

①响应度高:如果对一个交互程序采用多线程,那么即使其部分阻塞或执行较冗长的操作,该程序仍能继续执行,从而增加了对用户的响应程度。例如,多线程 Web 浏览器 在用一个线程装入图像时,能通过另一个线程与用户交互。

②资源共享:线程默认共享它们所属进程的内存和资源。代码和数据共享的优点是它能允许一个应用程序在同一地址空间有多个不同的活动线程。

③经济:进程创建所需要的内存和资源的分配比较昂贵。由于线程能共享它们所属进程的资源,所以创建和切换线程会更为经济。实际地测量进程创建和管理与线程创建和 管理的差别较为困难,但是前者通常要比后者花费更多的时间。

④多处理器体系结构的利用:多线程的优点之一是能充分使用多处理器体系结构, 以便每个进程能井行运行在不同的处理器上。不管有多少 CPU,单线程进程只能运行在1个 CPU 上。在多 CPU 上使用多线程加强了并发功能。

 

缺点:

安全性问题,一个线程奔溃会导致同进程的其他线程崩溃

多线程模型(用户线程&内核线程)

用户级线程和内核线程(进程不分用户级和内核)

用户级线程库提供对用户级线程的管理

内核线程由内核管理

OS只能处理内核线程,所以用户级线程需要一个对内核线程的映射机制(因为用户级线程对操作系统而言是不可见的)

用户线程:

在用户空间实现的线程机制,它不依赖于操作系统的内核,由一组用户级的线程库函数来完成线程的管理,包括线程的创建、终止、同步和调度等

  1. 由于用户线程的维护由相应进程来完成(通过线程库函数),不需要操作系统内核了解用户线程的存在,可用于不支持线程技术的多进程操作系统。
  2. 每个进程都需要它自己私有的线程控制块(TCB)列表,用来跟踪记录它的各个线程的状态信息(PC、栈指针、寄存器),TCB由线程库函数来维护。
  3. 用户线程的切换也是由线程库函数来完成,无需用户态/核心态切换,所以速度特别快。
  4. 允许每个进程拥有自定义的线程调度算法。

用户线程的缺点:

  1. 阻塞性的系统调用如何实现?如果一个线程发起系统调用而阻塞,则整个进程在等待。
  2. 当一个线程开始运行后,除非它主动交出CPU的使用权,否则它所在的进程当中的其它线程将无法运行(单CPU?)
  3. 由于时间片分配给进程,故与其他进程比,在多线程执行时,每个线程得到的时间片较少,执行会较慢。

内核线程:

  1. 是指在操作系统的内核当中实现的一种线程机制,由操作系统的内核来完成线程的创建。终止和管理。
  2. 在支持内核线程的操作系统中,由内核来维护进程和线程的上下文信息(PCB和TCB)
  3. 线程的创建、终止和切换都是通过系统调用/内核函数的方式来运行,由内核来完成,因此系统开销较大;
  4. 在一个进程当中,如果某个内核线程发起系统调用而被阻塞,并不会影响其他内核线程的运行。
  5. 时间片分配给线程,多线程的进程获得更多CPU时间

 

轻量级线程(LightWeight Process)

 

小结:程序在内存中的分布&进程与线程比较

程序在内存中的分布(参考网上的)

代码段(.text),也称文本段(Text Segment),存放着程序的机器码和只读数据,可执行指令就是从这里取得的。如果可能,系统会安排好相同程序的多个运行实体共享这些实例代码。这个段在内存中一般被标记为只读,任何对该区的写操作都会导致段错误(Segmentation Fault)。

BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。( 在《Programming ground up》里对.bss的解释为:There is another section called the .bss. This section is like the data section, except that it doesn’t take up space in the executable.

数据段(data segment):通常是指用来存放程序中已初始化的全局变量和静态变量的一块内存区域。数据段属于静态内存分配。

堆栈段

  • (Heap):用来存储程序运行时分配的变量

       堆的大小并不固定,可动态扩张或缩减。其分配由malloc()new()等这类实时内存分配函数来实现。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减) 堆的内存释放由应用程序去控制,通常一个new()就要对应一个delete(),如果程序员没有释放掉,那么在程序结束后操作系统会自动回收。

  • (Stack)是一种用来存储函数调用时的临时信息的结构,如函数调用所传递的参数、函数的返回地址、函数的局部变量等。 在程序运行时由编译器在需要的时候分配,在不需要的时候自动清除

动态存储区域    

静态存储区域 存储静态变量和全局变量以及代码段的部分

 

进程与线程的比较

①进程是资源分配单位,线程是CPU调度单位

②进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈

③线程同样具有就绪、阻塞和执行三种基本状态,同样具有状态之间的转换关系

④线程能减少并发执行的时间和空间开销

-线程的创建时间比进程短

-线程的终止时间比进程短

-同一进程内的线程切换时间比进程短

-由于同一进程的各线程共享内存和文件资源,可直接进行不通过内核的通信。

 

猜你喜欢

转载自blog.csdn.net/qq_37205708/article/details/86518889