图片来自: Windows内核原理与实现
Windows内核有"任务"概念吗?
从技术术语来说,Windows内核并没有"任务"。"任务"一般被认为是抽象的需要执行的事情。在不同操作系统上,"任务"所代表的官方名称有所差异。
Linux "struct task" 符合"任务"的中文描述
Windows Process/Thread表述"任务"的容器和实体
Process的集合称为Job
Process是静态资源, 可执行的"任务"被称为Thread.
macOS有任务术语, 代表一个线程组集合,Mach将任务中的每个线程一一映射到BSD层的进程。
除了线程,还有什么方式可以让内核做事情?
一般来说,操作系统以线程为调度单位。Linux的"task"很像线程, 以task为调度单位和线程类似。
在Windows上,有如下几种方式:
- (最基本的方式)应用程序创建自己的线程
- 驱动程序和执行体给调用者进程创建线程
- System进程创建系统线程。
- Work Item机制
- 因线程切换本身占用资源,内核提供Work Item机制将事情交给系统辅助线程帮忙。函数ExQueueWorkItem和IoQueueWorkItem就为了完成Work Item而产生。
进程和线程的关系
Windows内核对于进程和线程的区分是很明显的,不像Linux, 进程可能和线程很像兄弟。
进程是静态资源集合
进程包含了私有地址空间、句柄表和线程等讯息,它相当于一个容量,把程序执行该要的资源都放在里面。但它,没能力执行,因为内核不会调度进程运行(参见本页最上图片)。
线程是真正调度单位
在某些书籍上,线程被称为"控制流"。按照传统代码执行的3要素,数据/代码和堆栈。数据和代码都在线程所属进程资源包含,线程不用特别关心。堆栈是和线程控制流息息相关,具体分来有用户栈和内核栈两种。
用户态堆栈容易理解,用户应用程序创建后必然有堆栈保存当前状态,内核栈用于线程切入到内核态后需要暂存的讯息,也是必须的。
进程和线程创建和消亡
创建进程默认创建一个线程(主线程)
CreateProcess源代码
参见: Windows内核--CreateProcess到内核NtCreateProcess(2.3)
线程退出是通过APC实现
进程的最后一个线程结束进程才结束吗?
不是,如果主线程提前结束了,整个进程就结束了。如此设计避免仅留的子线程无处安放。可参考main函数反汇编,编译器会在函数最后插入exit等函数终结进程。
父进程和子进程
Windows父进程和子进程不像类Unix内核那样,如此正式隆重。Windows平台,父进程创建子进程之后,其实父进程就可以该干嘛去干嘛了。子进程结束与否和它没什么必然关系了。
父进程存在的意义?
类似面向对象继承,子进程继承父进程属性,可减少额外的复制动作。比如CreateProcess函数参数bInheritHandles可继承句柄信息.
桌面双击一个应用程序A
父进程是explorer.exe, 此进程A和explorer进程有什么关系呢? 从界面上看,没任何关系。
explorer进程终止后,A还能继续运行吗? A和explorer进程似乎完全没关联。
子进程终结,父进程还会收到通知吗?
如上示例, A终结后,explorer会收到通知吗?Windows内核把进程当作同步对象,同步对象终结会发出信号,只会通知等待此对象的内核对象。一般来说,explorer创建进程A之后,不会等待A结束的信号,父进程不会收到通知。
Job(作业)
如上所说,一批进程当成整体处理,可以加入到Job。Job和进程或线程一样也是内核对象,可以管理:
- 进程的处理器亲和性
- 工作集最大和最小值
- ......
Job在Windows的应用
哪些情况下通过作业来处理?有什么优势?Job可以有效管理一系列进程,控制在指定的范围内。对于需要有效准确管控系统程序运行有很大作用。比如,避免某个进程占用太多CPU导致关键进程失败。
纤程Fiber
Windows还提供一种特殊的"线程", 被称作纤程,是用户模式线程。
它在Kernel32.dll中实现,Fiber不是内核对象,调度需要用户调用。正因为如此,它被称为轻量级线程,它没有线程切换带来的开销,当然有用户层切换带来的微小开销。