Electronic Joint Business

Solution for E-Business

posix

IRP 操作详解

每个操作系统都需要一个显性或隐性的 I/O 模型来处理进出外设的数据流。而 Windows I/O 模型的特性之一就是支持异步 I/O,其它一些特点包括:1

  • I/O 管理器为所有内核驱动提供了一致的接口,无论是底层驱动,中间层驱动或者文件系统驱动。所有请求都以 IRP (I/O 请求包)的形式发送给驱动。
  • I/O 操作是层叠的。I/O 管理器提供 I/O 系统服务供用户模式下子系统调用来执行 I/O 操作。I/O 管理器负责解释这些调用,并创建一个或多个 IRP,然后将它们路由给堆叠的驱动层层传递到物理设备。
  • I/O 管理器定义了一整套标准例程,其中一些要求驱动必须支持,另一些则是可选的。所有驱动遵循相对一致的实现模型,并根据不同的外设而有所差异,另外总线驱动、功能驱动、过滤驱动和文件系统驱动等也有不同的功能要求。
  • 正如 Windows 操作系统一样,驱动也是基于对象的。驱动、设备和物理硬件都被表示为对象。I/O 管理器和其他系统组件提供了内核态的支持例程,驱动通过例程调用来操作相关对象使得工作得以完成。

除了用来传递传统的 IO 请求外,I/O 管理器也可以和PnP 管理器、电源管理器一起发送包含 PnP 和电源请求的 IRP。

用户的 I/O 请求和文件对象
由于内核驱动被环境子系统所隐藏,因此用户是不可见的,这些子系统实现了一些大家熟悉的编程接口,比如 Windows 或 POSIX。对用户态代码(包括子系统)来说,可见的只是 I/O 管理器所控制的命名文件对象(named file object) 。

图1-1 显示了用户、子系统和 I/O 管理器的关系。

诸如 Win32 子系统这样的受保护子系统,将 I/O 请求通过 I/O 系统服务传递给相应的内核驱动。上图所示的子系统依赖于显示、显卡适配器、键盘和鼠标设备驱动的支持。

>>> 阅读全文

 

, ,

为 Windows NT 内核扩展 API

与 Unix 基于中断的系统调用接口类似, Windows NT 提供了大量未经文档化的基础系统服务,即原生 API (Native API)。这些内核模式的系统服务被 Win32、 Posix 和 OS2 等环境子系统用于在 NT 微内核(Windows NT 是个修改过的微内核体系结构的操作系统)之上实现其运行环境。在 Win32 下,原生 API 通过 NTDLL.DLL 的导出函数提供给其他 API 和程序使用,如 KERNEL32.DLL 中的许多函数只是简单地重定向到 NTDLL.DLL 中。

事实上 NTDLL.DLL 那些调用系统服务的函数不过是个存根函数,它们只是对 SysEnter/SysCall(早些版本使用 INT 2EH)1等系统调用接口的简单包装。以 NtCreateFile 函数为例:(Windows 7 SP1 64位):2 ,它的具体实现如下:

mov     r10,rcx ;保存 rcx 的值,rcx 将被置为 rip(返回值)
mov     eax,52h ;系统调用号
syscall
ret
nop     dword ptr [rax+rax]

存根函数负责在 EAX 中填入系统调用号,随后执行本机调用接口实现从用户态向内核态的跳转。EAX 中的调用号将用以查找并调用系统服务以完成 API 的最终功能。每个原生 API 的调用号各不同,例子中 NTCreateFile 的调用号是 0x52h,且随着 Windows 的版本和架构不尽相同,有兴趣的话可以查询 Windows X86 系统调用表Windows X86-64 系统调用表

执行 SYSCALL 或 SYSENTER 时,CPU 并不会象执行 CALL一样保存返回地址和状态信息,所以需要存根函数来保存返回信息。3

当从原生 API 进入内核态以后,后续则交由 NCI 派发器 (NCI Dispatcher)处理。 NCI 派发器在微内核中(NTOSKRNL.EXE) 实现的, NCI 派发器( _KiSystemService ) 本质上就是 INT 2EH 的处理函数(handler)。NCI 派发器通过系统服务描述符表 (System Service Descriptor Table — SSDT) 来查找并调用某个服务的相应处理函数,函数参数则通过 NCI 传递。(一些内核调试器比如 IMHO 或者 NuMega Soft-ICE 可以通过 NTCALL 命令来显示 SSDT)。 微软并未提供文档来说明如何扩展原生 API,并通过 NCI 来调用自己的内核服务。所以本文试图通过变通的办法来解决这一问题。不过在此之前,我们还是大致看一下来如何通过 NCI 来调用原生 API 服务,或者其他 NCI 服务。

>>> 阅读全文

 

, , , ,

并行程序中的同步与通讯

在任何程序中,总会有些执行阶段要等待操作系统或者其他进程完成 I/O 操作或者同步操作。这时候,程序就无法处理其它工作,因为实际上是操作系统或者其它进程占用了宝贵的 CPU 时间。这就是滥用文件读/写操作的程序会如此之慢的原因。1

这时候进程不得不等待操作系统服务例程将 CPU 从其他进程那里释放出来。这种等待也被称为进程处于阻塞状态。诸如 EDA 这类串行计算,整个算法作为单个任务被执行,如果该进程在等待任一种阻塞操作,整个算法就会完全停顿。在处于阻塞状态的这段时间里,另一个进程占用了 CPU 的所有权,所以算法根本没办法进行。

并行程序提供了一种手段,能在同一计算中调用另一个进程以提高 CPU 利用率,当一个进程被阻塞的时候(如等待 I/O 操作或者同步操作),另一个进程则继续处理共同工作的其它部分。这种情况在单 CPU 或者多 CPU 机器上皆可发生。

反之,对于串行程序(即非并行算法)来说,而无论有多少可用的 CPU ,在任何情况下都只能用到单个 CPU。除非有编译器将源程序从串行转成并行,不过能实现这一目标的实属特例。 因此如果想同时利用多个 CPU ,编码时要手工运用某种并行技术。(在源代码中采用特殊指令,如 OpenMP)。

并行程序能将工作划分为较小的部分,因此多个进程可以一起工作并互相协作以便能在更短的时间内计算出同样的结果。这时,在等待 I/O 设备完成操作的同时,并行程序能可以继续处理另一部分工作。此外,并行程序还可以同时使用系统所有可用的 CPU。

>>> 阅读全文

 

, , , , , , ,

ELF 对线程局部储存的处理 (一)

线程使用的增加使得开发者期望有更好的方式来处理线程局部数据 (read-local data)。 POSIX 线程接口的有关定义允许在每个线程中独立存储 void * 对象。不过使用该接口很麻烦。你需要在运行时为对象分配一个键(key),如果这个键不再使用就需要释放它。这项工作繁琐而且易出错。如果和动态加载代码(dynamically loaded code)一起使用,就真的成了个大麻烦。

应对这些问题的方法是扩展编程语言,让编译器接手这些工作。以 C/C++ 来说,可以在定义与声明变量时使用新关键字 __thread。这虽然不是个官方扩展,编译器开发者被鼓励实现它以支持新 ABI。用这个关键字定义及声明的变量会自动地局部分配到每个线程。

__thread int i;
__thread struct states;
extern __thread char *p;

不仅一般用户程序可以受益,运行时环境(runtime)亦能从中获得好处 (如全局变量 errno 必须是线程局部的),而且编译器可以执行构建非自动变量( non-autimatic variable)的优化。注意:用 __thread 来定义自动变量毫无意义也不被允许,这是因为自动变量总是线程局部的。而且,函数作用域中的静态变量应是首选。

 

, ,

LXC (Linux Container) 初览

LXC 是 Linux Container的缩写。Linux 容器技术是一种内核虚拟化技术(也叫操作系统虚拟化),它提供了轻量级的虚拟化技术,可以在单一控制主机上同时提供多个虚拟环境(即容器)以隔离进程和资源,每个虚拟环境拥有自己的进程和独立的网络空间。在基于容器的虚拟化技术中,进程不再是个全局概念,而是从属于某个特定的容器。理想情况下,进程跟容器之间是动态关联的,进程可以在容器之间迁移。在基于容器的虚拟化技术中,容器既是资源容器,也是隔离的命名空间,它能有效地将由单个操作系统管理的资源划分到隔离的组中,以更好地在隔离的组之间平衡有冲突的资源的占用需求。从用户的角度上看,容器的运行与表现与独立拥有一台 Linux 服务器并无二致。

容器技术原理是基于操作系统内核对不同的进程提供了不同的系统视图, 它可以在本地 CPU 核心运行指令,避免了全虚拟化指令级模拟或即时编译造成的系统开销。同时也避免了准虚拟化(Para-Virtualization)和系统调用替换的复杂性。但是,容器技术强制所有客户必须使使用与控制主机(Control Host) 相同的内核,用户既不能运行其他种类操作系统,也不能运行不同的内核。图 1-1 显示了容器技术和其他虚拟化技术的不同。

容器虚拟化起源于 1982 年发布的 CHROOT 工具,这是一个特殊的基于文件子系统的容器虚拟,最早由 Sun 公司创始人 Bill Joy 开发并且作为 BSD 4.2 的组成进行发布。此后,大量的容器虚拟化技术不断涌现,主要有:

  • Solaris Zones
  • FreeBSD Jails
  • Linux VServer
  • OpenVZ

但是这些技术都没有被 Linux 内核所接受。相反 Linus 选择使用一系列新的内核特性来实现这一目标。Linux Container (LXC) 就是利用这些新特性实现的下一代容器虚拟化技术,并随着 Linux 内核而发布。

下面我们先看一下容器虚拟化的鼻祖 CHROOT 是如何工作的。

>>> 阅读全文

 

, , , , ,