进程和线程的区别与联系

进程(process)

进程是一个执行中程序的实例,每个程序都运行在其进程的上下文中。比如我打开了一个qq,那么这就是一个进程。在win系统的任务管理器中可以看到进程列表。

上下文是由程序运行所需的状态组成,包括程序的代码和数据,堆栈,寄存器,程序计数器等。

上文(过去):已执行过的进程指令和数据在相关寄存器与堆栈中的内容;

正文(现在):正在执行的指令和数据在寄存器与堆栈中的内容;

下文(未来):待执行的指令和数据在寄存器与堆栈中的内容;

进程的状态:

  • 就绪
  • 运行
  • 阻塞

多进程的目的是提高CPU的利用率。倘若没有多进程的话,计算机的使用体验就很差了。比如有下面两个进程:

A进程的工作是从硬盘读取文件,花费10秒钟

B进程的工作是计算1+1

假设A先执行,那么B需要等待10秒之后才能计算,即这10秒的时间CPU是空闲的。

为了提高CPU的利用率,我们可以在这10秒CPU空虚的时候来执行进程B,原本CPU运行的是A进程,然后切换运行B,这个过程叫做上下文切换(context switch)。上下文切换是有开销的,这个在后面来介绍。

线程(thread)

线程是运行在进程上下文中的逻辑流,一个进程可以包含多个线程,比如在qq中进行视频聊天和传输文件,这相当于是2个线程。每个线程有自己的上下文且有唯一的线程id,叫做tid,线程的上下文跟进程类似。多个线程可以运行在一个进程的上下文中,因此这些线程共享进程的虚拟地址空间

线程的状态:

  • 运行
  • 就绪
  • 阻塞

从上面来看,线程和进程很像,线程有时也被叫做轻量级进程,那为什么还需要线程呢?主要原因是一个应用中可能会同时发生多种活动,比如我用qq一边跟小美视频聊天,一边跟小芳传送文件,这两个操作造成阻塞,此时多线程会更好的利用cpu,这期间会发生线程的上下文切换,与进程类似,这个切换也是会有开销的。即如果存在较多的计算和IO处理时,通过多线程让他们重叠执行,可以更好的利用cpu,从而提高程序的运行速度。这里就不能使用多进程来实现了,因为不能同时在一个设备上登录两个相同的qq账号。

如下图所示展示了进程和线程的关系,有2个进程,里面分别运行了3个线程

进程和线程的比喻

1.把CPU看作是电源,假设电源一次只能供给工厂的一个车间使用。即一个车间开工的时候,其他车间都必须停工。相当于是一个CPU每次只能运行一个任务。这里的车间就相当于是进程

2.一个车间里,可以有很多工人,他们协同完成一个任务。这里的工人好比是线程,一个进程可以包含多个线程。

3.车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存

4.每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。

5.一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫”互斥锁“(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。

6.还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。

7.要限制厨房中的人数,可以在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后面的人没有取到钥匙,就必须在门口排队等待。这种做法叫做信号量“(Semaphore),用来保证多个线程不会互相冲突。
不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。

以上比喻引自:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

一个进程最多可以创建多少个线程

进程中可以创建线程的数量这个主要跟下面两个因素有关:

  1. 进程的虚拟内存空间上限,创建线程会为其开辟私有的栈内存,随着线程数量的增长,占用的栈内存会越大,所以进程的虚拟内存空间就决定了可以创建线程的个数。
  2. 系统参数限制,系统中有控制最大线程的参数。

在linux系统中内存占用空间如下

  • 32 位系统的内核空间占用 1G ,位于最高处,剩下的 3G 是用户空间。
  • 64 位系统的内核空间和用户空间都是 128T ,分别占据整个内存空间的最高和最低处。

一个linux的线程大概占8M内存,以32位系统为例,有3G空间可以使用,那么可以创建大约3G/8M = 384个。对于64位系统来说有系统参数限制和硬件等因素,肯定创建不了128T/8M数量的线程。

进程和线程上下文切换开销

linux中进程的上下文开销有下面几项:

  • 地址空间的转换,如切换页表全局目录,刷新TLB
  • 硬件上下文切换,如切换内核栈,载入寄存器数据

对于同一个进程内的线程来说,上下文切换,跟进程类似,相对开销略低一些。

另外上下文的切换有可能会导致L1、L2、L3缓存中的数据会失效,新的进程会穿透到内存,破坏了数据的局部性,这也是会有开销的。

进程和线程的对比

1.进程之间的内存是不共享的,同一个进程中的线程会共享内存;

2.从时间开销上来看,Linux 下创建进程跟线程差不多。从空间开销来看,Linux 下创建进程开销大于线程;

3.进程是资源分配的基本单位;线程是程序执行的基本单位;

4.一个进程由若干线程组成,线程是进程中代码的不同执行路线;

5.线程上下文切换的开销小于进程的上下文开销;

参考资料:

《深入理解计算机系统》

《现代操作系统》

https://www.zhihu.com/question/19903801

https://zhuanlan.zhihu.com/p/387673151