进程
Windows作为多任务操作系统,允许多个程序同时在系统中运行。这些程序被称为进程,进程运行在一片独立
的空间中,受到操作系统保护,操作系统的很多资源都是围绕着进程来进行分配,可以理解为操作系统维度下资源分配的最小粒度
.
一个进程由两部分构成
- 一个内核对象
内核对象保存进程的各种统计信息,操作系统依靠这些统计信息对进程进行管理 - 一个空间地址
包含了exe,dll的代码和数据,以及线程栈与堆。
在Windows操作系统中,进程拥有如下资源(节选):
- 一片虚拟地址,也就是进程空间
- PID
即ProcessId - Image
即将程序加载到内存并使其能够运行所需的所有信息,也就死exe在内存中的表示 - 线程
1-N个线程数量 - EPROCESS(Executive Process Block)
位于内核态,记录着内核态的关键信息 - 句柄表
位于内核态,记录进程所创建/打开的内核对象。 - DirBase
进程的起始基地址 - PEB(Process Environment Block)
位于用户态,记录着用户态的关键信息 - Access token
表示进程的用户,安全组以及优先级
眼见为实
- SessionId
指进程所在的windows会话Id,当多个用户登录时,有不同的会话Id - Cid/ParentCid
即PID,以及创建该进程的那个进程Id
眼见为实:句柄表
即ObjectTable
眼见为实:Token
EPROCESS,内核态数据结构
每一个进程都会持有一个EPROCESS的结构,如果一个档案一样记录着进程的所有信息。上面所展示的信息,本质上就是对EPROCESS信息的提取
眼见为实
PEB,用户态数据结构
包含了进程的用户态信息,与EPROCESS位于内核态不同,PEB是先在内核态中创建,再映射到用户态之中
眼见为实
进程空间
操作系统为了保护进程的安全,为不同的进程分配了独立的进程空间虚拟地址。目的旨在一个进程的代码与数据不会受到其它进程的修改,它们之间互相隔离。
32位系统进程空间
64位系统进程空间
内核态与用户态
Windows定义了两种访问模式,用户模式(user mode)与内核模式(kernel mode).
应用程序运行在user mode下,操作系统运行的kernel model下,他们之间互相隔离,无法直接访问,即便应用程序知道了在内核态中的某个数据的正确指针,也会被终止访问。
虽然不可以直接访问,但Windows提供了一道桥梁
,用于程序可以通过调用Win32 API来间接的访问内核态。
眼见为实
线程
如果把操作系统比作一个国家,那么进程就是这个国家的家庭,而线程则是家庭的成员。
懒惰的进程
进程是非常懒惰
的,从不执行任何东西,它只是线程与统计信息的一个容器,具体的工作需要由线程来执行。
从上面的比喻也可以看出,家庭是一个抽象的概念,家庭的成员才是物理意义上的执行单位。
臃肿的进程
从上面的介绍可以看出,进程使用的资源会更多,其原因在于地址空间。
为一个进程创建一个虚拟地址空间需要大量资源。
- 大量的日志记录
这需要用到大量内存 - 加载文件
加载exe,dll到内存中,也是消耗大户
相比之下,线程所使用的资源就要少得多了。一个线程实际上只有一个内核对象和一个栈。也不涉及日志记录。
与进程类似,线程也由两个部分组成
- 线程的内核对象
内核对象保存线程的各种统计信息,操作系统依靠这些统计信息对线程进行管理 - 一个线程栈
用于维护线程执行时所需要的函数参数与局部变量
ETHREAD,内核态数据结构
与EPROCESS结构来描述进程内核态的信息一样,线程也有一个类似的结构。名字叫做ETHREAD
ETHREAD的结构也很庞大,包含着线程各种属性。其中第一行的TCB(Thread Control Block)信息尤为重要,里面的字段主要是供内核调度线程时使用。
ETHREAD 结构字段太多了,一般使用!thread来简化输出。
PEB,用户态数据结构
与描述进程用户态信息的PEB一样,NT内核定义了线程环境块(Thread Environment Block)来描述线程的用户态信息。
主要有用户态的栈空间,异常信息,线程本地存储等
创建进程的流程
- 打开执行文件,确定其名称,类型等前置资料,类似于C#中的EEClass
- 为新进程创建EPROCESS,进程空间,PEB
- 创建初始线程,状态为susepend
这被称为主线程,然后这个线程会去创建更多的线程 - 通知CSRSS,类似新生儿上户口
- 初始线程开始执行
- 线程执行初始化动作,主要是加载依赖的dll