标准答案: 进程是资源分配的最小单位,线程是CPU调度的最小单位
CPU
线程是CPU调度的基本单位,那CPU能区分线程么?
- 实际情况是:CPU并不知道线程、进程之类的概念。
CPU只知道两件事:
- 1.从内存取指令
- 2.执行指令,重复1
指令怎么取呢?
- 学过计算机的同学都知道从PC寄存器,PC寄存器指向内存中指令的地址。
指令怎么形成的呢?
- 程序或者说我们写的函数编译才会形成指令。
我们该如何让CPU执行一个函数呢?
- 显然我们只需要找到函数被编译后形成的第一条指令就可以了,第一条指令就是函数入口。
说了半天CPU和线程有什么关系呢?
操作系统和进程
根据已上的结论,我们想让CPU执行某个函数的话,只需要把函数对应的第一条机器执行装入PC寄存器就可以了。
我们需要:
-
在内存中找到一块大小合适的区域装入程序;
-
找到函数入口,设置好PC寄存器让CPU开始执行程序。
-
这两个步骤绝不是那么容易的事情,如果每次在执行程序时程序员自己手动实现上述两个过程会疯掉的,因此聪明的程序员就会想干脆直接写个程序来自动完成上面两个步骤吧。(操作系统)
机器指令需要加载到内存中执行,因此需要记录下内存的起始地址和长度;同时要找到函数的入口地址并写到PC寄存器中,想一想这是不是需要一个数据结构来记录下这些信息。
数据结构大致如下:
struct *** {
void* start_addr;
intlen;
void* start_point;
...
};
复制代码
- 这个数据结构总要有个名字吧,这个结构体用来记录什么信息呢?记录的是程序在被加载到内存中的运行状态,程序从磁盘加载到内存跑起来叫什么好呢? (进程)
CPU执行的第一个函数也起个名字 (main函数)。
我们是不是忘了线程了?
多核时代
怎么利用多核呢?
起多个进程?但是主要存在这样几个问题:
- 进程是需要占用内存空间的(从上一节能看到这一点),如果多个进程基于同一个可执行程序,那么这些进程其内存区域中的内容几乎完全相同,这显然会造成内存的浪费;
- 计算机处理的任务可能是比较复杂的,这就涉及到了进程间通信,由于各个进程处于不同的内存地址空间,进程间通信天然需要借助操作系统,这就在增大编程难度的同时也增加了系统开销。
进程到线程
让我们再来仔细的想一想这个问题,所谓进程无非就是内存中的一段区域,这段区域中保存了CPU执行的机器指令以及函数运行时的堆栈信息,要想让进程运行,就把main函数的第一条机器指令地址写入PC寄存器,这样进程就运行起来了。
进程的缺点在于只有一个入口函数,也就是main函数,因此进程中的机器指令只能被一个CPU执行,那么有没有办法让多个CPU来执行同一个进程中的机器指令呢?
聪明的你应该能想到,既然我们可以把main函数的第一条指令地址写入PC寄存器,那么其它函数和main函数又有什么区别呢?
答案是没什么区别,main函数的特殊之处无非就在于是CPU执行的第一个函数,除此之外再无特别之处,我们可以把PC寄存器指向main函数,就可以把PC寄存器指向任何一个函数。
当我们把PC寄存器指向非main函数时,线程就诞生了。
还记得线程是资源分配的基本单位么?
后记
最后摘抄某知乎大佬的比喻,感觉还是比较合理:进程=火车,线程=车厢
- 线程在进程下行进(单纯的车厢无法运行)
- 一个进程可以包含多个线程(一辆火车可以有多个车厢)
- 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
- 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
- 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
- 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
- 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
- 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-”互斥锁”
- 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”