浅谈进程与线程

进程

概念上讲,进程就是程序的一次运行,即存放到磁盘里就叫程序,加载到内存中就是进程 。

每个进程都有属于自己的虚拟地址空间,其包括代码段,数据段,堆,栈,共享链接段,和内核段。进程在自己空间中做的任何改变都不会影响到其他的进程,也就是说,进程之间是相互独立的。

为了实现进程模型,操作系统维护着一张表格,即进程表,每一个进程占用一个进程表项,进程表项也称为进程控制块,该表项包含了进程状态的重要信息,包括程序计数器,堆栈指针,内存分配状况和所打开的文件状态等。操作系统可以根据该表进行进程的调度,在创建一个进程时,操作系统会在进程表中添加一个进程表项 。

使用系统调用fork,vfork,clone都可以创建一个新的进程,这三者的区别我在另一篇文章已经介绍过,这里不在赘述 。在创建一个新的进程时,子进程几乎是对父进程的翻版,它会拷贝父进程的寄存器,堆栈指针,程序计数器等资源,但是父子进程仍然是两个不同的进程,他们拥有自己独立的,不同的虚拟地址空间 ,对自己空间的任何改变不会影响到另一个进程。

介于创建一个新的进程常用于执行exec函数,即让其执行一个完全不同的任务,这样,子进程对父进程的完全拷贝显的徒劳并且还费时,所以使用写时复制技术加以改进。在父进程创建子进程后,刚开始子进程不会拷贝父进程的任何资源,即读共享,只要当任何一个进程企图对部分内存进行修改时,这块内存首先被明确的复制,以确保修改发生在私有内存区域 。

一句话说,不可写的内存区域时共享的 ,可写的内存区域是不共享的

线程

在传统操作系统重,每个进程都有一个地址空间和一个控制线程。这几乎就是进程的定义。在许多应用中同时发生着多种活动,其中某些活动随着时间的推移会被阻塞,将这些应用程序分解成可以准并行运行的多个顺序线程,可以显著提高程序的并发性。

同一个进程的线程共享地址空间以及所有可用数据。每个线程拥有属于自己的程序计数器 ,寄存器和堆栈指针 。显然,线程比进程要轻量的多,以至于在线程的上下文切换和创建以及撤销过程中非常的快,几条机器指令就可以完成。而进程的切换,创建和撤销涉及到内存地址空间的分配以及资源运行状态的保存。

换个角度来讲,对统一进程中的线程的模拟类似于对统一计算机中的进程的模拟,前一种模型中线程共享统一进程中的资源类似于后一种模型中的所有进程共享计算机的内存,磁盘,打印机等资源。

线程可以分为用户级线程和内核级线程。区别在于线程表由谁维护 。线程表中记录着各个线程的属性,比如程序计数器,堆栈指针,寄存器和状态等(类似于操作系统维护的进程表)。
用户级线程的线程表存与所属进程中,而内核级线程的线程表存储与内核中。

  • 用户级线程是由用户进程来管理的,用户级线程在调度,运行,切换,撤销等一系列动作对于内核来说是不可见的,一切由其所属进程说了算。

其优点在于:

  1. 可以在不支持线程的操作系统上运行
  2. 线程切换速度快,不需要陷入内核,不需要上下文切换。不过只能切换到该进程中的其他线程。而不能切换到其他进程的线程。
  3. 允许进程制定自己的线程调度算法。
  4. 具有较好的扩展性,因为内核空间中内核线程需要一些固定表格空间和堆栈空间,如果线程数量过大会导致问题。

缺点在于:

  1. 阻塞系统调用无法实现,如果某个线程调用某系统调用被阻塞,则整个进程都会被阻塞。CPU空转造成浪费。
  2. 如果某个线程开始运行,则其他线程都不可以运行,只有当其放弃CPU或者运行完毕,其他线程才可以被调度,类似一种顺序性执行。
  • 内核级线程的线程表由操作系统内核进行维护,当某个线程创建一个新的线程或者撤销一个线程时,执行一次系统调用,该系统调用通过更新线程表完成对线程的创建或撤销工作 。当一个线程发生阻塞时,内核可以根据其选择运行该进程中的另一个线程或者其他进程中的线程。由于内核中创建线程的开销比较大,所以采用线程回收的方法,用一个可以运行与否的标志位实现 。

优点在于:

  1. 内核线程不需要任何新的,非阻塞系统调用,当某个线程发生阻塞系统调用时,内核可以调度另一个线程继续运行而不会导致CPU空转 。
    缺点在于:
  2. 线程切换开销较大,每次都要执行一次系统调用。

进程&线程

  • 每个进程拥有自己独立的地址空间以及程序所需的各种资源;每个线程共享其进程的地址空间以及进程的所有资源 。这就导致了进程之间的独立性非常好,共享性差;线程之间的同步性非常好,独立性差。所以就引出了进程间通信方式的目的是为了加大同步,线程间通信是为了加大独立。

  • 进程之间的切换,创建,撤销等动作首先需要保存当前进程的运行状态,然后加载待运行的进程资源,最后交出CPU控制权 ;线程的切换,创建,撤销只需要保存线程当前的寄存器状态,堆栈指针和程序计数器状态,加载另一个线程的相关资源,交CPU控制权。过程类似,但是线程的切换要比进程的切换快的多,几条指令就可以完成。

  • 进程的调度是由操作系统来控制的,有各种调度算法,比如先来先服务,短作业优先,轮转调度和优先级调度等,他们之间可以存在抢占;线程的调度可以是操作系统控制,也可以是由其所属进程控制,并且他们之间不存在抢占,只有当一个线程运行完毕或者其主动让出CPU,下一个线程才可以运行,这里的下一个线程可以是当前进程的其他线程,也可以是其他进程的线程,这取决于线程模型是用户级线程还是内核级线程。


参考资料:
《现代操作系统 3th》