线程

本章节的习题分数占比很小,但是对于比较深入理解线程很有帮助,请至少尝试做一下本章习题。我们提供的信息可能不多,需要大家动手上网合理搜索。

什么是线程?

进程和线程是分不开的,下面我们介绍一下线程是什么。线程(英语:thread)是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

线程占据哪些资源?

运行的程序可以看做是状态机,这些状态由全局变量,堆区,栈区,pc,寄存器......组合而成。

不同的进程之间是被完全隔离的,它们拥有独立的全局变量,堆区,栈区,pc,寄存器。所以我们在实验中,你会发现,每次创建一个新的进程,就要给它分配一块独立的空间。

而线程是同一进程内更小的计算单位。它们之间共享同一进程的全局变量和堆区,但是不共享栈区,pc和寄存器。创建一个线程,只需要为它分配一块栈区即可,不需要像fork一样进行地址空间拷贝。所以说,线程是轻量级进程。

那么,为什么线程只占据这些资源呢?(或者说,既然线程是最小计算单位,那只要寄存器不就好了?)

实际上,我们的线程往往是执行一个函数(请看看pthread_create这些线程库函数),而我们在ics里学过,函数的调用和返回实际上就是栈帧的创建和释放,所以,函数所需的最小资源就是线程所需的最小资源。

exercise7:那么,线程为什么要以函数为粒度来执行?(想一想,更大的粒度是......,更小的粒度是......)

动手操作

光看教程,你很难对线程是什么有一个很好的理解,所以,请自行搜索以下几个函数的用法,和一些经典程序,亲身体会一下线程究竟是什么。

  • pthread_create

  • pthread_join

  • ......

线程和进程的对比

我们来引用Wikipedia的内容:

Threads vis-à-vis processes

Threads differ from traditional multitasking operating-system processes in several ways:

  • processes are typically independent, while threads exist as subsets of a process

  • processes carry considerably more state information than threads, whereas multiple threads within a process share process state as well as memory and other resources

  • processes have separate address spaces, whereas threads share their address space

  • processes interact only through system-provided inter-process communication mechanisms

  • context switching between threads in the same process typically occurs faster than context switching between processes

Advantages and disadvantages of threads vs processes include:

  • Lower resource consumption of threads: using threads, an application can operate using fewer resources than it would need when using multiple processes.

  • Simplified sharing and communication of threads: unlike processes, which require a message passing or shared memory mechanism to perform inter-process communication (IPC), threads can communicate through data, code and files they already share.

  • Thread crashes a process: due to threads sharing the same address space, an illegal operation performed by a thread can crash the entire process; therefore, one misbehaving thread can disrupt the processing of all the other threads in the application.

一些挑战

线程的实现方式多种多样,分别是内核级线程,用户级线程,两级线程。

用户级线程就是在用户态下进行堆栈分配(我们提供了一个用户级线程库),然后进行切换,而不需要使用系统调用。

内核级线程是通过系统调用来实现,具体过程跟fork这些差不多。

两级线程模型有点复杂,感兴趣可以自行了解。

我们今年提供了一个用户级线程库的实现(水兵gg写的),请看thread文件夹,程序不需要qemu就可以运行,在Linux命令行中下面命令即可编译运行:

gcc -m32 --no-warnings -g main.c thread.c && ./a.out

线程库里面包含了四个函数,并且运用了一些宏和内联汇编,大家只需要理解代码即可。

  • 创建线程:int create_thread(__uint32_t func, int argNum, ...);

    • 创建一个线程来运行函数func,argNum是func的参数个数,变长参数列表是func的参数列表

    • 它的作用是:找到一块合适的tcb(注意,tcb里面包含了线程的stack),在栈区里面放置好函数的参数,然后等待调度运行。

  • 继续运行线程:int launch_thread(int id);

    • id是需要运行的线程编号

    • 它会调用launch宏,把当前的esp和pc保存起来,恢复id线程的esp和pc

  • 暂停线程:void yield_thread(int ret);

    • 线程主动交出控制权,把ret作为返回值(注意,观察是怎么返回的)

  • 销毁线程:void destory_thread();

  • 获取线程状态:enum threadStatus getThreadStatus(int id);

下面有一个challenge(如果觉得有难度可以在写完实验代码后再来理解)

Last updated