0x00:进程

计算机程序知识存储在磁盘上的可执行的二进制文件,只有把他们加载到内存中才被操作系统调用,才拥有生命周期。 进程则是一个执行中的程序。 每个进程都有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。 操作系统管理所有进程的执行,并为这些进程合理的分配时间。

进程也可以通过派生(fork或者 spawn)新的进程来执行其他任务,不过因为每个新的进程拥有自己的内存和数据栈等,只能采用进程间通信的方式共享信息。

0x01:线程

线程(有时候被称为轻量进程)与进程类似, 不过它们实在同一个进程下执行的,并共享相同的上下文。可以将它们认为是在一个主进程或 “主线程” 中并行运行的一些 “迷你进程”。

线程包括 开始、执行顺序和结束三部分。 它有一个指令指针,用于记录当前运行的上下文。当其他线程运行时,它可以被抢占(中断)和临时挂起(也称为睡眠)——这种做法称为让步(yielding)。

一个进程中的各个线程与主线程共享同一片数据空间,因此相对于独立的程序而言,线程之间的信息共享通信更加容易,线程一般是以 并发 执行的。在单核CPU 系统中, 因为真正的 并发 是不可能的,所以线程的执行是这样规划的,每个线程运行一小会 ,让后让步给其他线程(再次排队等待更多的CPU时间)。在整个线程的执行过程中,每个线程执行它自己特定的任务,在必要时和其他线程进行通信。

当然,共享同一片数据空间也是有风险的。 如果两个或多个线程访问同一块数据,由于数据访问的顺序不相同,可能导致结果不一样。这种情况称为竞态条件(race condition)。 幸运的是,大多数线程库都有一些同步原语,以允许线程管理器控制执行和访问。

还有一个注意的问题是,线程无法给与公平的执行时间。这是因为一些函数会在完成前保持阻塞状态,如果没有专门为多线程情况进行修改,会导致CPU的时间分配向这些贪婪的函数倾斜。

当一个线程完成函数的执行时,它就会退出。还可以顶用 thread.exit()之类的退出函数。

0x02:全局解释器锁

Python 代码的执行是由 Python 虚拟机(又名解释器主循环)进行控制的。 Python 在设计时是这样考虑的,在主循环中同时只能有一个控制线程在执行,就像单核 CPU 系统中的多进程一样。内存中可以有许多程序,但是在任意给定时刻只能有一个程序在运行。同理,尽管Python 解释器可以运行多个线程,但是在任意给定时刻只能有一个线程会被解释器执行。

Python 虚拟机的访问是由 全局解释器锁(GIL)控制的。这个锁就是用来保证同时只能有一个线程运行的。在多线程环境中,Python 虚拟机将按照下面所述的方式执行。

  1. 设置GIL。(全局解释器锁)
  2. 切换进一个线程去执行。
  3. 执行下面操作之一。

a. 指定数量的字节码指令。
b. 线程主动让出控制权(可以调用time.sleep(0)来完成)。

  1. 把线程设置回睡眠状态(切换出线程)。
  2. 解锁GIL。
  3. 重复上述步骤。

当调用外部代码(即,任意C/C++扩展的内置函数)时,GIL会保持锁定,直至函数执行结束(因为这期间没有Python 字节码技术)