windows用户模式和内核模式

运行 Windows 的计算机中的处理器有两个不同模式:用户模式 和内核模式 。

用户模式和内核模式都是针对CPU运行状态来说的,在用户模式下,CPU无法执行一些特权指令,而在内核模式下是可以的。

根据处理器上运行的代码的类型,处理器在两个模式之间切换。 应用程序在用户模式下运行,核心操作系统组件在内核模式下运行。 虽然许多驱动程序以内核模式运行,但某些驱动程序可能以用户模式运行。

R3 用户态 所有api都通过ntdll调用到内核Api

上面所说的两种模式不仅仅是字面上的区别,它们是受CPU严格制约的。如果在用户模式下,程序尝试做些其权限以外的事情,比如说,执行一条高权限的CPU指令,修改其被禁止访问的内存,这时一个可捕获的异常就会抛出。但这只会导致这个倒霉的应用程序崩溃,而不会让你的整个系统崩溃。这就是用户模式的意义所在。

x86 CPU提供4种 保护层级 : 0, 1, 2 and 3. 实际上,只用0级(内核)和3级(用户程序)被使用到了

启动用户模式应用程序时,Windows 会为该应用程序创建进程 。 进程为应用程序提供专用的 虚拟地址空间 和专用的句柄表 。 由于应用程序的虚拟地址空间为专用空间,因此一个应用程序无法更改属于其他应用程序的数据。 每个应用程序都隔离运行,如果一个应用程序发生故障,则故障仅局限于该应用程序。 其他应用程序和操作系统不会受该故障的影响。

除了专用之外,用户模式应用程序的虚拟地址空间也受到限制。 在用户模式下运行的处理器无法访问为操作系统保留的虚拟地址。 限制用户模式应用程序的虚拟地址空间可防止应用程序更改以及可能损坏关键的操作系统数据。

在内核模式下运行的所有代码都共享单个 虚拟地址空间 。 这意味着内核模式驱动程序不会与其他驱动程序和操作系统本身隔离。 如果内核模式驱动程序意外写入错误的虚拟地址,则属于操作系统或其他驱动程序的数据可能会受到安全威胁。 如果内核模式驱动程序发生故障,整个操作系统就会发生故障。

此图说明了用户模式组件与内核模式组件之间的通信。

以上内容来源: https://docs.microsoft.com/zh-cn/windows-hardware/drivers/gettingstarted/user-mode-and-kernel-mode

以下是内核的层次划分:

1、硬件抽象层(HardwareAbstraction Layer) (HAL) (hal.dll)
最底层隔离硬件的, 底层的第三方驱动程序就执行在这层。

2、内核(Kernel)
实现操作系统的一些底层服务。比方线程调度, 多处理器的同步,中断/ 异常处理等。

3、运行体(Executive) (ntoskrnl.exe
实现主要的操作系统服务,比方主要的线程进程管理,内存管理, IO 及进程间通讯等。

4、窗体图形子系统(Windows Graphics Subsystem)
由win32K.sys 在内核层实现, 用户界面相关都依赖该层,User32.dll 的大部分功能都由该层实现


用户层关键进程

Windows 系统在用户层有几个关键的系统进程:

Smss.exe(session manager Subsystem)
关于Session 的概念能够參考我的这篇 Sessions, Window Stationsand Desktops 在操作系统启动时会创建一个不与不论什么Session 关联的Smss.exe 管理者实例。 然后当实用户登录时它会为每一个Sessin 拷贝一份与之关联的Smss.exe 实例,然后由该关联的Smss.exe 实例启动winlogon.exe 和csrss.exe.

WinLogon.exe
该进程管理用户的登录和注销, 我们按Ctrl+Alt+Del 出现的界面和登录后出现的桌面窗体都是由它启动的。



Csrss.exe( Client/Server Runtime Subsystem)
我们能够看到我们的桌面窗体(GetDesktopWindow) 是由该进程创建的, 该进程主要负责Win32 子系统的用户模式部分( 内核模式部分由win32k.sys 实现)

Lsass.exe(Local Security Authority Subsystem)
WinLogon.exe 通过该进程验证用户登录, 登录后产生安全訪问令牌对象。 通过该令牌创建Explorer.exe, 我们其它用户进程都由Explorer.exe 启动。而且继承了该令牌权限。
Services.exe
该进程简称为SCM(NT Service Control Manager), 该进程负责启动用户态一些特殊进程。 也就是我们通常所说的服务程序。

3、用户模式调用内核模式的方式

4、内核模式调用用户模式

能够通过IOCTL 的上下文传递, 也能够通过APC (Asynchronous Procedure Call) 直接调用。

5、进程间的通信

第二种很强大的用户模式与内核模式通讯方式, 同一时候也支持进程间通讯, 该方式就是ALPC(Advanced Local Procedure Call) 该方式被操作系统大量使用。 WinRT 中的Broker 进程也用到了它
该方式实际上就4 个核心函数:nt!NtAlpcSendWaitReceivePort,nt!NtAlpcCreatePort, nt!NtAlpcConnectPort, Nt!AplcAcceptConnectPort, 大概原理例如以下:

在多任务环境中,有许多进程都不允许应用程序去做。所以CPU以两种模式运行,即用户模式和内核模式。

①内核模式
当CPU运行于内核模式时,一切程序都可运行。任务可以执行特权级指令,对任何I/O设备有全部的访问权,还能够访问任何虚地址和控制虚拟内存硬件。这种模式对应80×86的ring0层,操作系统的核心部分,包括设备驱动程序都运行在该模式。
②用户模式
这个模式中,硬件防止特权指令的执行,并对内存和I/O空间的访问操作进行检查。这就允许WindowsNT4.0限制任务对各种I/O操作的访问,并捕捉违反系统完整性的任何行为。在用户模式中,运行的代码如果不通过操作系统中的某种门机制,就不能进入内核模式。在80×86处理器上,这个模式对应于 ring3层,操作系统的用户接口部分以及所有的用户应用程序都运行在该级别。

windows的用户栈和内核栈

普通的 一个Win32线程有两个栈 :一个是用户栈,另一个是内核栈;而如果是内核中创建的系统工作线程,则只有内核栈。只要代码在内核中运行,线程就一定是使用其内核栈的。栈的主要作用是维护函数调用帧,以及为局部变量提供空间。

在Windows里,一个线程的用户空间的信息都记录在了TEB中,而TEB中又有一个域叫做NtTib,这里面就存放着有关用户站的信息。由于TEB结构过于复杂,这里不列举。

一些需要注意的地方

一个线程用户栈可以指定其大小,默认是1MB,通过编译指令/stack可改设其他值。

一个线程普通内核栈的大小是固定的,由系统根据CPU架构而定,x86系统上为12KB,x64系统上为24KB,安腾系统上为32KB。对于GUI线程,普通内核栈空间可能不够,所以系统又定义了“大内核栈”概念,可以在需要的时候增长栈空间。只有GUI线程才能使用大内核栈,这   也是系统规定的。

Windows将GDI和USER模块,即“窗口与图形模块”的实现移到了内核中,称为Windows子系统内核服务,并形成一个win32k.sys内核文件。而用户层仅留调用接口,由User32.dll和GDI32.dll两个文件暴露出来。判断一个线程是不是GUI线程的依据,竟非常的简单:线程初建时,都是普通线程,第一次调用Windows子系统内核服务(只要用户程序调用了User32.dll和GDI32.dll中的函数,并导致相关内核服务在内核中被执行),系统即立刻将之转变为GUI线程,并从而切换到“大内核栈”;倘若至线程结束,并未有任何一个子系统内核服务被调用,那么它一直都是普通线程,一直使用普通内核栈。

用户模式切换到内核模式的情况