内核术语--服务,函数,Routine,进程,线程,作业,Fiber,虚拟内存

Windows API


Windows API 是用户模式下的编程接口,在64位Windows 出来之前,32位Windows上的Windows API 叫做Win32 API,主要是与原来的16位Windows上的 API区分。

Windows API 包括上千个函数,主要分为以下几部分:

  • 基础服务
  • 组件服务
  • 用户接口服务
  • 图形和多媒体服务
  • 消息和协作
  • 网络
  • WEB服务
具体的书可以参考《Windows Via C++》。

.NET

.NET Framework包括一个叫做FCL(Framework Class Library)的类库和一个提供托管代码运行环境的CLR(Common Language Runtime)组成。
CLR支持JIT(即时编译),类型验证,垃圾回收,代码访问安全等功能。
所有的.NET组件都是在非托管的Windows API层之上的用户模式的Windows DLL,没有任何.NET代码支行在内核模式。


具体的书可以参考《CLR Via C#》。

服务,函数,Routines

  • 本地系统服务(或者叫做系统调用):用户模式下可调用的服务,比如NtCreateUserProcess,用来创建一个进程。
  • 内核支撑函数(或者叫做routines):内核模式下可调用的子程序,比如ExAllocatePoolWithTag,驱动用来从堆(内存池)中申请内存。
  • Windows服务:由Windows服务控制管理器启动的进程。
  • DLL:一组可以被动态链接并加载的程序,例如Msvcrt.dll, Kernel32.dll。优点是多个程序调用一个DLL,Windows只需要一份DLL的内存。.NET也可以编译DLL但不会导出任何函数,CLR将编译后的元数据解析成可访问的相应的类型和成员。

进程,线程,作业

程序和进程很像,但本质上是不同的。一个程序是一串静态的指令,而进程只是一组资源的容器,用于执行程序的实例。
进程包括:
  • 一个虚拟地址空间,也就是进程可用的虚拟内存地址。
  • 一个可执行程序,定义了初始化代码和数据,并映射到进程的虚拟地址空间中。
  • 一些系统资源的句柄,例如 信号量,通信端口,文件,这些资源可以被进程内所有线程共用。
  • 一个叫做访问令牌的安全上下文,用来识别用户,安全组,权限,用户账号控制(UAC)的虚拟化状态,session,以及用户账号相关的状态。
  • 一个唯一的进程ID。
  • 至少一个执行的线程,虽然也可以没有任何线程,但没有意义。
每个进程有一个指针指向其余进程或创建这个进程的进程,如果父进程不存在了,这个指针也不会更新。也就是说,可能会指向一个不存在的父进程。
这倒也无所谓,因为没啥依赖的数据,可以用系统自带的任务管理器查看进程信息,
也可以用第三方的进程浏览器来查看,此工具相当强大,下载地址: http://download.sysinternals.com/files/ProcessExplorer.zip
可以查看的内容包括:
  • 进程安全令牌。
  • 高亮进程和线程的变化。
  • 列出服务宿主中的服务。
  • 作业中的进程。
  • .NET程序的宿主的细节,比如AppDomain,加载的DLL,性能。
  • 进程和线程的启动时间。
  • 完整的内存映射列表。
  • 挂起线程或进程。
  • 杀死一个线程。
  • 检测占用CPU时间较多的进程。
  • 进程树。
  • 打开进程的句柄。
  • 进程中加载的DLL。
  • 进程中的活跃线程。
  • 用户模式和内核模式的线程栈。
  • CPU使用率。
  • 峰值内存量。
线程是进程中可被Windows调度的执行实体,没有线程程序就不会运行。线程包括以下一些基本的组件:
  • 一些代表CPU状态的内容。
  • 两个栈,分别用在内核模式和用户模式下。
  • 一个叫做TLS(thread-local storage )的私有存储区,各子系统,运行时库和DLL都会用到。
  • 一个唯一的线程ID,不会与进程ID相同。
  • 线程有时也有自己的安全上下文,或令牌,常用在多线程服务端程序模拟客户端安全上下文的时候。
可变的寄存器,栈,私有存储区都叫做线程的上下文。因为这些信息在不同的机器架构下是不同的。可以用GetThreadContext()函数访问。

虽然线程有自己的执行上下文,但进程中的所有线程都共享进程的虚拟地址空间(除了属于进程的资源)。意味着所有线程都可以的读-写进程的虚拟地址空间。
线程不可能引用其他进程的地址空间,但是,如果其他进程将其私有地址空间标记为共享内存段(在Windows API中叫做文件映射对象),或者进程有权限打开其他进程的的跨进程内存,例如用ReadProcessMemory 和WriteProcessMemory函数。
除了私有地址空间和线程,进程里还有安全上下文和一些内核对象的句柄,比如共享内存段,同步对象(如互斥量,事件,信号量)。
进程的安全上下文文件存储在叫做“访问令牌”(access token)的对象里,访问信息包括安全识别信息和凭证。默认情况下线程没有访问令牌,但是线程可以有,线程可以模拟其他进程的安全上下文,包括远程系统的进程。
VAD(虚拟地址描述符)是内存管理器用来维护虚拟地址空间的数据结构。
如下图所示:



Windows 提供了一个进程模型的扩展,叫做作业(Job),job的主要功能是将一组进程作为一个单元进行管理。
job对象允许对进程的某些特定属性进行控制,也记录了一些基本的信息,包括已经终止了的进程。job对象填补了进程结构化的空缺。


Fibers(有的翻译成纤程,个人觉得这么翻译容易混淆概念)与UMS

因为在线程间的切换换需要引入内核调度,所以这是很费资源的操作。尤其是在两个频繁转换的线程中切换。Windows提供了两个机制用来减少资源的消耗,分别是:
  • Fibers
  • UMS(用户模式线程调度)
Fibers允许程序自己调度自己的线程而不用依赖于线程的优先级机制。
Fibers也叫做轻量级线程,它们在用户模式中的Kernel32.dll中实现,所以他们对于内核是不可见的。
要使用Fibers,首先调用 ConvertThreadToFiber() 函数将线程转换成Fiber。然后这个Fiber可以用CreateFiber() 创建新的Fiber,每个Fiber可以有自己的一组Fiber。
不像线程,Fiber不会自动执行,直到手动调用SwitchToFiber()才开始执行。

UMS线程是64位Windows专属的,与Fiber的优点相同,但没什么缺点。
UMS线程有自己的内核线程状态且内核可见,允许多个UMS线程阻塞系统调用,共享和竞争资源。
然而,两个以上的UMS线程只需要在用户模式下,可以不用内核调度就定期转换执行上下文。

虚拟内存

Windows实现了一个基于线性地址的虚拟内存系统,为每个进程提供一个幻觉,让其觉得自己有很大的可用内存空间。
虚拟内存提供了一个对于物理内存的逻辑视图,但可能与物理内存的布局不一致。运行时,内存管理器在硬件,翻译,映射的帮助下,将虚拟地址映射到物理地址。
为了保护数据,操作系统保证每个进程不会越界访问到其他进程的数据,下图表示3块连续的虚拟内存页映射到不连续的3块物理内存页。


因为大多数系统都的物理内存都小于进程使用的虚拟内存,内存管理器会将数据存储到硬盘上。
当线程访问分布到硬盘上的虚拟地址时,虚拟内存管理器再将其加载回内存。
虚拟内存空间的大小在不同平台上也不一样,在32位x86系统上,最大4GB,默认下系统用2G,用户模式用2G。
也可以通过配置BIOS让用户模式用3GB,系统用1GB。


如果3GB还不够用,Windows提供了一个叫做AWE(Address Windows Extension)的机制,允许32位应用程序申请最大64GB的物理内存映射到2GB的虚拟内存空间中。
虽然用AWE将管理内存映射的负担压在了码农身上,但是它提供了更多福利。
64位Windows提供了更大的地址空间,IA64位可以用7152GB,X64可以用8192GB。 64位地址空间大于170亿GB,但是硬盘限制了。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值