Enterprise Just Builder

Solution for Enterprise Software Architecture

初窥 Windows Container 和 Docker

抽象地说所有的计算都是在一系列物理资源上(包括处理器、 内存、 磁盘、 网络等)运行某些”功能”以达成特定任务,这些计算可以是简单的数学运算,比如“1+1”,也可以是跨越多台机器的复杂应用比如 Exchange。随着时代的进步,物理资源越来越强大,结果应用程序常常只利用了物理机所提供的资源的一小部分。因而,工程师们通过创建“虚拟”资源来模拟底层物理硬件,从而使得多个应用可以并发运行 — — 每个应用使用同一物理机器的部分物理资源。

通常这些模拟技术被称为虚拟化。听到“虚拟化”一词,很多人立即会想到虚拟机,不过这只是虚拟化的一种实现。容器是另一种类型的虚拟化,也被称为操作系统虚拟化。比如 对应用程序来说,Linux 上的容器创建了一个完全隔离和独立的操作系统。从运行中的容器的角度上看,本地磁盘看起来像保存着 OS 文件的原始副本、 内存驻留的文件和数据就宛如刚启动的操作系统,唯一运行的东西只有操作系统。要做到这一点,创建容器的”宿主”机要实现一系列技术构想。1

The first technique is namespace isolation. Namespaces include all the resources that an application can interact with, including files, network ports and the list of running processes. Namespace isolation enables the host to give each container a virtualized namespace that includes only the resources that it should see. With this restricted view, a container can’t access files not included in its virtualized namespace regardless of their permissions because it simply can’t see them. Nor can it list or interact with applications that are not part of the container, which fools it into believing that it’s the only application running on the system when there may be dozens or hundreds of others.

第一种技术是名称空间隔离。名称空间包含着应用程序可以交互的一切资源,包括文件、 网络端口和正在运行的进程列表。名称空间隔离可以让宿主为每个容器呈现一个虚拟名称空间,只包括该容器所应看到的资源。在该受限的视图中,无论权限如何,容器都不能访问其虚拟名称空间之外的文件,因为它对容器不可见。容器既无法查看该容器外的应用,也无法与之交互,这让应用误以为自己是系统中运行的唯一程序(其实同时还可能有数十个甚至上百个其他应用在运行)。

为了提高效率,许多操作系统文件、 目录和正在运行的服务容器之间共享,并投影到每个容器的命名空间。只有当应用程序进行更改到其容器,例如通过修改现有文件或创建一个新,容器会不同的副本从底层主机操作系统 — — 但只有的那些部分的改变,使用码头的”副本上写”优化。 这种共享是什么让部署效率极高的单个主机上的多个容器的一部分。

>>> 阅读全文

, ,

Windows Process 内存组织结构及重要字段解析

Windows 操作系统中的进程,实际上是用位于内核管理层(Executive)的 _EPROCESS 进程块来表示的。_EPROCESS 中不仅包含了进程相关的一些重要的数据结构,同时还包含了进程的环境变量等等东西。_EPROCESS 块的大部分数据存储在操作系统的内核空间,不过进程环境块 PEB 则是位于用户空间。这是因为 PEB 包含了一些用户可能频繁修改的数据,放在内核空间中会导致频繁的模式切换,也不利于安全。

图1-1 源自《Windows Internal》第六章里,它显示了一个完整进程的不同部分,这些部分被分别存放在系统地址空间和进程地址空间之中,以适应不同的操作模式的需要。

在内核调试模式下,我们可以用 Windbg 的 dt 命令列出 _EPROCESS 的结构:(例子基于 Windows 10 Build 10240)

WINDBG>dt nt!_eprocess
   +0x000 Pcb              : _KPROCESS
   +0x2d8 ProcessLock      : _EX_PUSH_LOCK
   +0x2e0 RundownProtect   : _EX_RUNDOWN_REF
   +0x2e8 UniqueProcessId  : Ptr64 Void
   +0x2f0 ActiveProcessLinks : _LIST_ENTRY
   +0x300 Flags2           : Uint4B
   +0x300 JobNotReallyActive : Pos 0, 1 Bit
   +0x300 AccountingFolded : Pos 1, 1 Bit
   +0x300 NewProcessReported : Pos 2, 1 Bit
   +0x300 ExitProcessReported : Pos 3, 1 Bit
   +0x300 ReportCommitChanges : Pos 4, 1 Bit
   +0x300 LastReportMemory : Pos 5, 1 Bit
   +0x300 ForceWakeCharge  : Pos 6, 1 Bit
   +0x300 CrossSessionCreate : Pos 7, 1 Bit
   +0x300 NeedsHandleRundown : Pos 8, 1 Bit
   +0x300 RefTraceEnabled  : Pos 9, 1 Bit
   +0x300 DisableDynamicCode : Pos 10, 1 Bit
   +0x300 EmptyJobEvaluated : Pos 11, 1 Bit
   +0x300 DefaultPagePriority : Pos 12, 3 Bits
   +0x300 PrimaryTokenFrozen : Pos 15, 1 Bit
   +0x300 ProcessVerifierTarget : Pos 16, 1 Bit
   +0x300 StackRandomizationDisabled : Pos 17, 1 Bit
   +0x300 AffinityPermanent : Pos 18, 1 Bit
   +0x300 AffinityUpdateEnable : Pos 19, 1 Bit
   +0x300 PropagateNode    : Pos 20, 1 Bit
   +0x300 ExplicitAffinity : Pos 21, 1 Bit
   +0x300 ProcessExecutionState : Pos 22, 2 Bits
   +0x300 DisallowStrippedImages : Pos 24, 1 Bit
   +0x300 HighEntropyASLREnabled : Pos 25, 1 Bit
   +0x300 ExtensionPointDisable : Pos 26, 1 Bit
   +0x300 ForceRelocateImages : Pos 27, 1 Bit
   +0x300 ProcessStateChangeRequest : Pos 28, 2 Bits
   +0x300 ProcessStateChangeInProgress : Pos 30, 1 Bit
   +0x300 DisallowWin32kSystemCalls : Pos 31, 1 Bit
   +0x304 Flags            : Uint4B
   +0x304 CreateReported   : Pos 0, 1 Bit
   +0x304 NoDebugInherit   : Pos 1, 1 Bit
   +0x304 ProcessExiting   : Pos 2, 1 Bit
   +0x304 ProcessDelete    : Pos 3, 1 Bit
   +0x304 ControlFlowGuardEnabled : Pos 4, 1 Bit
   +0x304 VmDeleted        : Pos 5, 1 Bit
   +0x304 OutswapEnabled   : Pos 6, 1 Bit
   +0x304 Outswapped       : Pos 7, 1 Bit
   +0x304 FailFastOnCommitFail : Pos 8, 1 Bit
   +0x304 Wow64VaSpace4Gb  : Pos 9, 1 Bit
   +0x304 AddressSpaceInitialized : Pos 10, 2 Bits
   +0x304 SetTimerResolution : Pos 12, 1 Bit
   +0x304 BreakOnTermination : Pos 13, 1 Bit
   +0x304 DeprioritizeViews : Pos 14, 1 Bit
   +0x304 WriteWatch       : Pos 15, 1 Bit
   +0x304 ProcessInSession : Pos 16, 1 Bit
   +0x304 OverrideAddressSpace : Pos 17, 1 Bit
   +0x304 HasAddressSpace  : Pos 18, 1 Bit
   +0x304 LaunchPrefetched : Pos 19, 1 Bit
   +0x304 Background       : Pos 20, 1 Bit
   +0x304 VmTopDown        : Pos 21, 1 Bit
   +0x304 ImageNotifyDone  : Pos 22, 1 Bit
   +0x304 PdeUpdateNeeded  : Pos 23, 1 Bit
   +0x304 VdmAllowed       : Pos 24, 1 Bit
   +0x304 ProcessRundown   : Pos 25, 1 Bit
   +0x304 ProcessInserted  : Pos 26, 1 Bit
   +0x304 DefaultIoPriority : Pos 27, 3 Bits
   +0x304 ProcessSelfDelete : Pos 30, 1 Bit
   +0x304 SetTimerResolutionLink : Pos 31, 1 Bit
   +0x308 CreateTime       : _LARGE_INTEGER
   +0x310 ProcessQuotaUsage : [2] Uint8B
   +0x320 ProcessQuotaPeak : [2] Uint8B
   +0x330 PeakVirtualSize  : Uint8B
   +0x338 VirtualSize      : Uint8B
   +0x340 SessionProcessLinks : _LIST_ENTRY
   +0x350 ExceptionPortData : Ptr64 Void
   +0x350 ExceptionPortValue : Uint8B
   +0x350 ExceptionPortState : Pos 0, 3 Bits
   +0x358 Token            : _EX_FAST_REF
   +0x360 WorkingSetPage   : Uint8B
   +0x368 AddressCreationLock : _EX_PUSH_LOCK
   +0x370 PageTableCommitmentLock : _EX_PUSH_LOCK
   +0x378 RotateInProgress : Ptr64 _ETHREAD
   +0x380 ForkInProgress   : Ptr64 _ETHREAD
   +0x388 CommitChargeJob  : Ptr64 _EJOB
   +0x390 CloneRoot        : _RTL_AVL_TREE
   +0x398 NumberOfPrivatePages : Uint8B
   +0x3a0 NumberOfLockedPages : Uint8B
   +0x3a8 Win32Process     : Ptr64 Void
   +0x3b0 Job              : Ptr64 _EJOB
   +0x3b8 SectionObject    : Ptr64 Void
   +0x3c0 SectionBaseAddress : Ptr64 Void
   +0x3c8 Cookie           : Uint4B
   +0x3d0 WorkingSetWatch  : Ptr64 _PAGEFAULT_HISTORY
   +0x3d8 Win32WindowStation : Ptr64 Void
   +0x3e0 InheritedFromUniqueProcessId : Ptr64 Void
   +0x3e8 LdtInformation   : Ptr64 Void
   +0x3f0 OwnerProcessId   : Uint8B
   +0x3f8 Peb              : Ptr64 _PEB
   +0x400 Session          : Ptr64 Void
   +0x408 AweInfo          : Ptr64 Void
   +0x410 QuotaBlock       : Ptr64 _EPROCESS_QUOTA_BLOCK
   +0x418 ObjectTable      : Ptr64 _HANDLE_TABLE
   +0x420 DebugPort        : Ptr64 Void
   +0x428 Wow64Process     : Ptr64 Void
   +0x430 DeviceMap        : Ptr64 Void
   +0x438 EtwDataSource    : Ptr64 Void
   +0x440 PageDirectoryPte : Uint8B
   +0x448 ImageFileName    : [15] UChar
   +0x457 PriorityClass    : UChar
   +0x458 SecurityPort     : Ptr64 Void
   +0x460 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
   +0x468 JobLinks         : _LIST_ENTRY
   +0x478 HighestUserAddress : Ptr64 Void
   +0x480 ThreadListHead   : _LIST_ENTRY
   +0x490 ActiveThreads    : Uint4B
   +0x494 ImagePathHash    : Uint4B
   +0x498 DefaultHardErrorProcessing : Uint4B
   +0x49c LastThreadExitStatus : Int4B
   +0x4a0 PrefetchTrace    : _EX_FAST_REF
   +0x4a8 LockedPagesList  : Ptr64 Void
   +0x4b0 ReadOperationCount : _LARGE_INTEGER
   +0x4b8 WriteOperationCount : _LARGE_INTEGER
   +0x4c0 OtherOperationCount : _LARGE_INTEGER
   +0x4c8 ReadTransferCount : _LARGE_INTEGER
   +0x4d0 WriteTransferCount : _LARGE_INTEGER
   +0x4d8 OtherTransferCount : _LARGE_INTEGER
   +0x4e0 CommitChargeLimit : Uint8B
   +0x4e8 CommitCharge     : Uint8B
   +0x4f0 CommitChargePeak : Uint8B
   +0x4f8 Vm               : _MMSUPPORT
   +0x5f0 MmProcessLinks   : _LIST_ENTRY
   +0x600 ModifiedPageCount : Uint4B
   +0x604 ExitStatus       : Int4B
   +0x608 VadRoot          : _RTL_AVL_TREE
   +0x610 VadHint          : Ptr64 Void
   +0x618 VadCount         : Uint8B
   +0x620 VadPhysicalPages : Uint8B
   +0x628 VadPhysicalPagesLimit : Uint8B
   +0x630 AlpcContext      : _ALPC_PROCESS_CONTEXT
   +0x650 TimerResolutionLink : _LIST_ENTRY
   +0x660 TimerResolutionStackRecord : Ptr64 _PO_DIAG_STACK_RECORD
   +0x668 RequestedTimerResolution : Uint4B
   +0x66c SmallestTimerResolution : Uint4B
   +0x670 ExitTime         : _LARGE_INTEGER
   +0x678 InvertedFunctionTable : Ptr64 _INVERTED_FUNCTION_TABLE
   +0x680 InvertedFunctionTableLock : _EX_PUSH_LOCK
   +0x688 ActiveThreadsHighWatermark : Uint4B
   +0x68c LargePrivateVadCount : Uint4B
   +0x690 ThreadListLock   : _EX_PUSH_LOCK
   +0x698 WnfContext       : Ptr64 Void
   +0x6a0 Spare0           : Uint8B
   +0x6a8 SignatureLevel   : UChar
   +0x6a9 SectionSignatureLevel : UChar
   +0x6aa Protection       : _PS_PROTECTION
   +0x6ab HangCount        : UChar
   +0x6ac Flags3           : Uint4B
   +0x6ac Minimal          : Pos 0, 1 Bit
   +0x6ac ReplacingPageRoot : Pos 1, 1 Bit
   +0x6ac DisableNonSystemFonts : Pos 2, 1 Bit
   +0x6ac AuditNonSystemFontLoading : Pos 3, 1 Bit
   +0x6ac Crashed          : Pos 4, 1 Bit
   +0x6ac JobVadsAreTracked : Pos 5, 1 Bit
   +0x6ac VadTrackingDisabled : Pos 6, 1 Bit
   +0x6ac AuxiliaryProcess : Pos 7, 1 Bit
   +0x6ac SubsystemProcess : Pos 8, 1 Bit
   +0x6ac IndirectCpuSets  : Pos 9, 1 Bit
   +0x6ac InPrivate        : Pos 10, 1 Bit
   +0x6b0 DeviceAsid       : Int4B
   +0x6b8 SvmData          : Ptr64 Void
   +0x6c0 SvmProcessLock   : _EX_PUSH_LOCK
   +0x6c8 SvmLock          : Uint8B
   +0x6d0 SvmProcessDeviceListHead : _LIST_ENTRY
   +0x6e0 LastFreezeInterruptTime : Uint8B
   +0x6e8 DiskCounters     : Ptr64 _PROCESS_DISK_COUNTERS
   +0x6f0 PicoContext      : Ptr64 Void
   +0x6f8 TrustletIdentity : Uint8B
   +0x700 KeepAliveCounter : Uint4B
   +0x704 NoWakeKeepAliveCounter : Uint4B
   +0x708 HighPriorityFaultsAllowed : Uint4B
   +0x710 EnergyValues     : Ptr64 _PROCESS_ENERGY_VALUES
   +0x718 VmContext        : Ptr64 Void
   +0x720 Silo             : Ptr64 _ESILO
   +0x728 SiloEntry        : _LIST_ENTRY
   +0x738 SequenceNumber   : Uint8B
   +0x740 CreateInterruptTime : Uint8B
   +0x748 CreateUnbiasedInterruptTime : Uint8B
   +0x750 TotalUnbiasedFrozenTime : Uint8B
   +0x758 LastAppStateUpdateTime : Uint8B
   +0x760 LastAppStateUptime : Pos 0, 61 Bits
   +0x760 LastAppState     : Pos 61, 3 Bits
   +0x768 SharedCommitCharge : Uint8B
   +0x770 SharedCommitLock : _EX_PUSH_LOCK
   +0x778 SharedCommitLinks : _LIST_ENTRY
   +0x788 AllowedCpuSets   : Uint8B
   +0x790 DefaultCpuSets   : Uint8B
   +0x788 AllowedCpuSetsIndirect : Ptr64 Uint8B
   +0x790 DefaultCpuSetsIndirect : Ptr64 Uint8B

也可以用 dt nt!_eprocess –r1 命令来展开成为下一级的结构体的定义。

Linux和Windows设备驱动架构比较

本文将考查最为广泛使用的 Windows 和 Linux 操作系统的设备驱动架构。我们首先将展示并比较在这些操作系统中实现设备驱动所需的组件,然后讨论实现驱动程序的过程,包括对内核缓冲区执行 I/O 操作。在文章的末尾我们还将了解这些操作系统所提供的开发环境和基础工具。1

1.引言
现代操作系统一般都包含多个模块,如内存管理器、进程调度器、硬件抽象层和安全管理器等等。想进一步了解这两种操作系统的内核,这里有一些参考资料:关于 Windows 内核的细节,可翻阅 Russinovich 著的《Windows Internal》一书;而 Linux 内核则可以参考 Rusling 的《The Linux Kernel》或者 Beck et al 等合著的《Linux Kernel Internals》。对和各类型硬件设备交互来说,内核可被视为一个黑盒。那么能否将和所有已知设备交互的功能内建到内核中呢?有可能,但没有实际意义,因为这会无谓地消耗太多系统资源。

内核模块化
对于各种新设备,内核无法在创建时就预知如何与之交互。现代操作系统的内核允许通过运行时添加设备驱动模块来扩展系统功能,该模块实现的功能使得内核可以与某种特定的新设备交互。这些模块通常都会实现一个例程供内核在模块被加载时调用,此外还有例程则在模块被移除时调用。模块还会实现各种不同的例程以便实现 I/O 功能,如将数据传送到设备或从设备接收数据;同时还有例程以便向设备发送 I/O 控制指令。以上所述对 Linux 和 Windows 两种驱动架构均适用。

本文的组织结构
本论文分成下面几节:

  • 对两种驱动架构的一般介绍 (第二节)
  • 详细论述两种驱动架构的各个组件 (第三节)
  • 驱动程序范例,包括如何对内核缓冲区执行 I/O 操作(第四节)
  • 相关驱动开发环境和基础工具(第五节)

相关著作
Windows 设备驱动架构相关文档可在 Windows 驱动开发包(DDK)中找到. 此外 Walter Oney 和Chris Cant 在 《Writing Windows WDM Device Drivers》一书中对 Windows 驱动架构进行了详细介绍。Linux 设备驱动架构可以参考 Rubini 的《Linux Device Drivers》,该书可免费获取。

>>> 阅读全文

, , , , ,

理解 ACPI (一) ACPI 简介及加电过程

在引入高级配置和电源接口 (ACPI — Advanced Configuration and Power Interface) 之前, 高级电源管理 (APM) BIOS 被广泛用于电源管理。在 APM 中,大部分的电源管理控制逻辑都驻留在 APM BIOS 代码中。支持 APM 的操作系统通过固定的 BIOS API 与 APM BIOS 进行通信,这些 API 提供了 BIOS 功能的基本访问。支持 APM 的操作系统必须定期轮询 APM 以处理 APM 相关事件。

APM 存在着以下三个缺陷: 首先,除非供应商提供专有程序,否则很多 APM 功能都要在操作系统启动前,进入 BIOS 菜单来配置,比如设置显示器关闭前的空闲时间。而且 APM 电源管理配置的具体策略是由 BIOS 供应商设定的,比如某 APM BIOS 的策略是在关闭显示器的同时降低 CPU 时钟频率或休眠网卡等设备,如果不想这么做,就必须修改 BIOS。 其次,APM 是 BIOS 级别的代码,运行在操作系统之外,因此开发和调试 APM 代码都十分困难,更糟糕的是用户只能通过重刷 ROM 的方式来解决 APM 的错误,而重刷 BIOS 相当危险,一旦发生错误,系统就再无法启动了。最后,由于 APM 是各个供应商专有的,为相同的功能而进行的重复开发投入是种巨大浪费。

不同于 APM 直接基于 BIOS 的管理方式,ACPI 中不再由 BIOS 代码来作出决策,而是通过操作系统内核来统一管理所有设备,从而解决了 APM 配置机制的局限性,换句话说,ACPI 允许操作系统直接对电源和散热进行管理,还允许系统硬件产生的 Hot-Plug 事件,让操作系统从用户的角度上直接支配即插即用设备。因而 ACPI 比其他机制更加灵活。

ACPI 主要提供下列功能:1

  • 系统电源管理(System power management):ACPI 提供了让系统作为一个整体进入或离开睡眠状态的机制,让设备唤醒系统。
  • 设备电源管理(Device power management):ACPI 表描述了电源信息(包括主板设备、设备电源状态以及设备所连接的电源层– Power Planes) 和让设备进入低功耗状态的控制信息,这样操作系统就可根据资源占用来控制设备进入低功耗状态。
  • 处理器电源管理(Processor power management):当操作系统处于空闲且非睡眠状态,可以通过 ACPI 提供的命令让处理器进入低功耗状态。
  • 设备和处理器性能管理(Device and processor performance management):操作系统在工作时可以根据 ACPI 的定义,让设备和处理器进入不同的性能状态
  • 配置/即插即用(Configuration/Plug and Play):ACPI 指定了用来枚举和配置主板设备的信息,这些信息按层次结构排列,当发生诸如 docking/undocking 之类的事件时,操作系统就可准确知道该事件影响了哪个设备。
  • 系统事件(System Event): ACPI 提供了通用的事件机制操作用于处理如散热、电源管理、坞站或设备的插拔等事件。
  • 电池管理(Battery management):兼容 ACPI 的电池设备需要具有智能电池子系统接口(Smart Battery subsystem interface)或者控制型电池接口(Control Method Battery interface),前者通过嵌入式控制器的接口控制,后者则完全由 AML 控制方法进行操控。
  • 温度管理(Thermal management):ACPI 允许 OEM 自由定义散热温度区间。
  • 嵌入式控制器(Embedded Controller):ACPI 提供了标准接口让操作系统和嵌入式控制器进行通讯。
  • SMBus 控制器(SMBus Controller): ACPI 提供了标准接口让操作系统和 SMBus 进行通讯。

当然为此操作系统内核中要添加额外的 ACPI 支持。支持 ACPI 的操作系统就可以叫做 OSPM (Operating System-directed configuration and Power Management) 。 支持 OSPM 的至少要求实现:

>>> 阅读全文

, , , ,

Windows CSRSS 详解 (1) 基础概念

微软之所以引入 “Windows 环境子系统” 这一概念,其理念是为了用户程序提供一个严格定义的原生函数子集。这是因为 Windows 操作系统被设计成同时支持原生 Windows 和 POSIX 类型的可执行程序(在 Windows 10 Red Stone 中还新添加了 Windows Subsystem for Linux 子系统),因此对于每一类应用,开发者必须区隔其所调用的 API。每个环境子系统通常包含两个主要部分:1

  • 子系统进程:一个正常的 ring-3 级应用程序,负责处理某些子系统特定的功能。
  • 子系统 DLL: 特定子系统专用的系统类库集,是额外添加在用户程序与原生系统调用间的调用层。

事实上 Windows 子系统 — — 也称为 CSRSS (客户端/服务器运行时子系统 Client/Server Runtime Subsystem) 是系统强制的执行环境,换句话说,如果没有没有 CSRSS.exe 在后台运行, Windows 不能正确工作。由于 CSRSS 所绑定的职责,该子系统对每个 Windows 都是必须的,包括服务器版本 (它可以不处理交互式用户会话)。另一方面,POSIX (psxss.exe) 和 Windows Subsystem for Linux 属于可选子系统 — — 因此,只在需要才启动相关进程。

子系统的启动信息被保存在注册表键 HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems 下面。默认的启动信息配置如下:

  • 名称: Debug 类型: REG_EXPAND_SZ 数据:
  • 名称: Kmode 类型: REG_EXPAND_SZ 数据: \SystemRoot\System32\win32k.sys
  • 名称: Optional 类型: REG_MULTI_SZ 数据: Posix
  • 名称: Posix 类型: REG_EXPAND_SZ 数据: %SystemRoot%\system32\psxss.exe
  • 名称: Required 类型: REG_MULTI_SZ 数据: Debug Windows
  • 名称: Windows 类型: REG_EXPAND_SZ 数据: %SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,20480,768 Windows=On ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ServerDll=sxssrv,4 ProfileC

大多数人对支持 Windows API 的子系统 DLL 都有所耳闻 — 即 kernel32.dll、 user32.dll、 gdi32.dll、 advapi32.dll 等等,无数的 Windows 软件开发者在日常工作中都会用到它们。而对于 POSIX 子系统来说,只需用到 psxdll.dll 这一个模块就可以实现所需的 Unix API。

此外,每一个进程都会与某个特定的子系统关联,该属性是由链接器在编译过程中设置的,并保存在 PE 结构的相关字段中:

>>> 阅读全文

, ,

Xperf 高手训练营 (1): NetTCPPortSharing 和 NLA Services 导致系统启动缓慢

本系列文章将通过一些现实生活中的例子来展示如何用 Xperf 工具来诊断一些常见的性能问题,并如何实现系统优化的。1

我们的第一个案例是某台笔记本花了2分钟才能显示出正常的桌面。要加速启动速度,我们必须先了解问题出在哪里。所以首先需要抓取系统痕迹(trace),xbootmgr 工具适用于系统开关机的场景,其命令如下:

xbootmgr -trace boot -traceFlags Latency+DISPATCHER -postBootDelay 120 -stackWalk Profile+ProcessCreate+CSwitch+ReadyThread+Mark+SyscallEnter+ThreadCreate

注意这里的 -stackwalk 开关。 stackwalking 使得我们可以在痕迹中查看模块以及函数调用。对于 64位机器,在收集痕迹文件之前,须先禁止执行换页以防部分内核被换出。你可以执行以下命令来达到这一目的。

Reg.exe add “HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management” -v DisablePagingExecutive -d 0x1 -t REG_DWORD -f

收集好痕迹文件,接着打开 Performance Analyzer。Performance Analyzer (又称 xperfview) 是用来分析 XPERF 痕迹的工具。它是 Windows 性能工具箱(Windows Performance Toolkit) 中的利器,该工具箱是 Windows SDK 的一部分。

, ,

CPU C-State 详解

如果你对计算机电源管理方面有所了解,一定会接触到 S-States、C-States 和 P-States 这三种工作状态。其中S-States (Sleeping states)指系统睡眠状态 ,由 ACPI 规定,C-States(CPU Power state) 则是 CPU 电源状态,而 P-States (CPU Performance states) 则指 CPU 性能状态。当然除了这三种外,还有 G-States(全局状态)和 D-States (设备状态)。

S-States 很好理解,就是用户手动点击“睡眠”,或者达到一定的待机时间(由系统电源管理设置而定)进入睡眠状态,S0 就是指正常运作。而 C-States 和P-States 看起来也很类似,都会调节处理器的核心电压、电流以及频率,因此经常被混淆。我们通过图1-1 来梳理一下上面这三种状态的关系。

S-States 中的 S0 指非睡眠状态,即系统正常运作状态或待机状态(idle)。只有在 S0 状态下,C-States 才会存在。同样的,C0 代表 CPU 正常工作状态,而P-States 正是处理器正常运作时的状态,所以 P-States 只存在于 C0 状态下。

因此简单来说,P-States 是根据系统的负载情况调节处理器核心电压和频率,处理器仍在运作当中;而 C-States 则是改变处理器各个部分的状态,包括核心、缓存、总线以及各种后来集成进来的模块,此时处理器应该是处于工作或待机状态。我们日常使用电脑的时候,系统就是频繁地在这些状态下切换,以达到提高续航和降低功耗的目的。1

C-States 的 11 个工作状态
目前 C-States 有以下这11个状态:

>>> 阅读全文

, ,

Windows 调试集锦 <1>

调试 WinLogon1
WinLogon 是一个用户模式进程,负责处理用户登入登出的交互任务,以及处理 CTRL+ALT+DELTE。要调试 Winlogon 最简单的办法是使用 NTSD 然后通过内核调试器来控制并调试。

首先要解决的是如何将用户态调试器 NTSD 的输出重定向到内核调试器,为此需先配置内核调试连接。详细可以参考[2. Controlling the User-Mode Debugger from the Kernel Debugger[

其次需要修改注册表,这样 Winlogon 进程一开始启动就可被调试。注册表键值如下:

[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\WinLogon.EXE]
"Debugger"="ntsd -d -x -g"

The -d option passes control to the kernel debugger. The -x option causes the debugger to capture access violations as second-chance exceptions. The -g option causes the WinLogon process to run after the attachment. Do not add the -g if you want to start debugging before Winlogon.exe begins (for example, if you want to set an initial breakpoint).

In addition, you should set the GlobalFlag value under the winlogon.exe key to REG_DWORD “0x000400F0”. This sets heap checking and FLG_ENABLE_KDEBUG_SYMBOL_LOAD. However, since this second flag only affects the kernel debugger, symbols must also be copied to the target computer before starting the debugger.

>>> 阅读全文

Windows 驱动开发 <1> 理解 Windows 驱动的执行上下文

要理解 Windows 内核驱动,很重要的是要了解一个驱动函数是在哪一种”上下文”中执行的。如果你从事文件系统的开发、或 Windows 内核驱动的编写,透彻理解执行上下文将使你获益匪浅。了解上下文并小心使用,可以让你设计出高性能、低开销的内核驱动。

本文将探讨执行上下文的概念。在文章末尾,沃恩还会介绍一个驱动作为概念演示,该驱动可以允许用户态程序运行在内核模式中,因此拥有内核模式的所有权限与特权。此外,我们还将讨论设备驱动程序的执行上下文及其实际用途。1

什么是上下文
当提到某个例程的上下文时,其实指的是它的线程和进程执行环境。在 Windows NT 中,该环境是由当前的 TEB (Thread Environment Block) 和 PEB (Processes Environment Block) 所建立的。因此,上下文中将包括虚拟内存配置 ( virtual memory settings — 用于说明物理内存页与虚拟内存地址的对应关系) 、句柄对照表 (handle translations, 因为句柄是特定于进程的)、分发器信息( dispatcher information)、栈(stacks) 、通用和浮点寄存器集等内容。

如果我们问“某个内核例程所运行的上下文是什么”,实际上的问题是“内核分发器所建立的当前线程是什么”。由于每个线程仅可属于一个进程,所以当前线程同时也就意味着某个当前进程。当前线程和当前进程合在一起,就意味着我们前面提到的句柄、虚拟内存、调度状态等等,这些东西使得线程和进程具有唯一性。

对于内核驱动程序员来说, 上下文中最有用的也许就是虚拟内存上下文。如果你还记得 Windows 将用户进程空间映射到低 2GB 的虚拟内存地址中,而操作系统代码则被映射到高 2GB 虚拟内存空间中。当用户进程中的某个线程被执行时,其虚拟地址将位于 0~2GB 的范围中。所有高于 2GB 的地址都被设置为“不可访问”,以防用户直接访问操作系统的代码和数据结构。当操作系统代码被执行时,其虚拟地址范围是 2~4GB。

>>> 阅读全文

,

USB 协议详解之一 基础知识

在 USB 出现前,计算机上就已经存在众多接口标准,比如串口、并口、PS2 等等,各接口从硬件形状和软件设置都不兼容,因此 USB 的设计目标之一,就是希望通过它方便地实现设备之间的互联。从而解决接口标准太多的弊端,这也是 USB 中的 Universal 一词的含义。

USB 使用一个 4 针的标准插头,采用菊花瓣形式把所有外设连接起来,它使用串行方式传输数据,支持多数据流和多个设备并行操作并允许外设热插拔。

基础知识
从物理结构上,USB 系统是一个分层的星形结构,它包含三类硬件设备:主机(HOST)、 设备(DEVICE)和集线器(HUB)。处于每个星形拓扑中央的是集线器,而在主机与集线器之间,集线器与集线器之间,集线器与设备之间都是点对点连接。1

各个设备在系统中的作用如下:

  • USB 主机 的主要作用包括: 提供电源,同时进行节电管理;检测设备,包括设备的接入与断开;管理数据传输,包括控制流和数据流;发送配置请求对 USB 设备进行配置操作;对总线上的错误进行管理和恢复等等。
  • USB 设备:在一个 USB 系统中,设备和集线器的总数不能超过 127 个。USB 设备接收 USB 总线上所有数据包,通过数据包的地址域来判断是不是发给自己的数据包:若地址不符,则简单地丢弃该数据包;若地址相符,则通过响应主机的数据包来和 USB 主机进行数据传输。
  • USB 集线器:集线器用于设备扩展连接,所有 USB 设备都连接在 USB 集线器的端口上。一个 USB 主机总与一个根集线器 (USB ROOT HUB)相连。USB 集线器为其每个端口提供 100mA 电流供设备使用。同时,USB 集线器可以通过端口的电气变化来判断出设备的插拔操作,并通过响应 USB 主机的数据包把端口状态汇报给主机。一般来说,USB 设备与USB 集线器间的连线长度不能超过 5米,USB 系统的级联不能超过5层(包括ROOT HUB)。

USB 功能
USB 是一种串行接口协议,它依靠 D+/D- 两条数据线构成的差分线来进行数据传输,这和我们熟悉两线 RS232/485 协议有何区别呢?了解这种区别有助于深入理解 USB 。所以首先让我们回想一下 RS232 的数据是如何传送的,如图1-2:

>>> 阅读全文

, ,

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 系统服务传递给相应的内核驱动。上图所示的子系统依赖于显示、显卡适配器、键盘和鼠标设备驱动的支持。

>>> 阅读全文

, ,

用 IDA Windbg 调试器实现 VMWare 上的 Windows 内核调试

文章评价:
IDA 是交互式反汇编器(Interactive Disassembler)的简称,它由总部位于比利时列日市(Liège)的 Hex-Rays 公司的一款产品。IDA是一种递归下降反汇编器。其主要优势在于呈现尽可能接近源代码的代码。它不仅使用数据类型信息,而且通过派生的变量和函数名称来尽其所能地注释生成的反汇编代码。这些注释将原始十六进制代码的数量减到最少,并显著增加了向用户提供的符号化信息的数量。

和 Windows 原生的调试器 windbg 相比, IDA 可以将程序结构和算法的核心以类似 C 语言的形式呈现,因此更容易通过阅读代码来定位问题的所在。

本文将介绍如何透过 IDA 的 Windbg 插件来实现 Windows 内核调试。

使用 WinDbg 进行 Windows 内核调试通常需要两台计算机,一台称为Debuggee,即目标机,另一台作为 Debugger,也称为主机 HOST。

目标机可以是物理机或虚拟机。如果是物理机,那么该机器至少需要一个串口、1394口或者支持 USB 调试的 USB 端口,另外你还需要与对应的调试连接线,如串口线、1394总线、USB 2.0 或 3.0 调试线等等。 Windows 8 之后的操作系统,普遍使用 USB 3.0 端口进行调试,通过 USBView 工具查看目标机,可以确定机器上哪一个 USB 端口支持调试。

>>> 阅读全文

, , , ,

Windows LPC 通信机制剖析

LPC (Local procedure calls) 是 NT 内核实现一种基于消息的高速通信机制,可用于用户进程之间、用户进程与内核驱动之间或者内核驱动之间的通信。举个用户态进程间通信的例子,比如在创建登录会话时,CSRSS.exe 就通过 SmssWinStationApiPort 和 SMSS.exe 沟通,或者进程可以通过 LsaAuthenticationPort 就安全问题与 LSASS.exe 沟通。又比如在读写加密文件时, KSecDD.sys 会因为 EFS 秘钥的加密和解密与 LSASS.exe 通信, 这是用户进程与内核驱动通信的例子。1

LPC 采用两种不同机制以便在服务端与客户端之间传递数据:如数据长度小于 304 字节,则使用 LPC 消息缓冲;反之,数据长度超过 304 字节,则使用共享内存 (通过 NtCreateSection ),该内存段会同时映射到服务端和客户端的地址空间中。

除了当作远程过程调用(Remote Procedure Calls)的备选协议外,LPC 还被广泛运用到整个系统中,比如 Win32 应用与 CSRSS.EXE 间的通信,SRM 和 LSASS间的通信以及 WinLogon 与 LSASS 的通信等等。

LPC 强制在客户端与服务端间采用同步通信模型。从 Vista开始, LPC 开始被废弃转而采用称为异步本地进程间通信(ALPC)的新机制。 和 LPC 相比,ALPC 固有的优势是:从客户端到服务端的所有调用都是异步的,即客户端不需要阻塞或等待服务器对消息作出响应。从 Vista 之后,如果某些老应用调用了 LPC API,则会被自动重定向到新的 ALPC API。

LPC API
LPC API 是原生 API,换句话说 API 分别通过 NTDLL.dll 和 NTOSKRNL.exe 导出到用户模式和内核模式。LPC API 并未被导出到 Win32 层,因此 Win32 应用不能直接使用 LPC。不过如果进行 RPC 通信时使用 “ncalrpc” 协议序列(协议序列代表 RPC 通信使用的网络协议。2,就可以指定 LPC 作为下层传输协议,从而让 Win32 应用间接使用 LPC。

>>> 阅读全文

, , ,

受保护进程的演进 <1>: Windows 8.1 如何抵御哈希传递攻击

6年多前我第一次发文讨论受保护进程的概念—《为什么受保护进程糟糕透顶》1,该标题已经清楚透露了我对这个未经深思熟虑的 DRM 方案的看法。显然之后微软对其内部机制进行了长考(的确,抛开 DRM,一个难以渗透的用户态进程在安全领域也是个有趣的课题),又创造了一类新进程:Protected Process Light,内核中通常缩写为 PPL。

和其笨重的兄弟(受保护进程)不同,Protected Process Light 作为安全屏障,为 Windows 平台带来了三种可以有效抵御威胁的安全增强。在接下来的系列文章里2,我们将会看到这些安全增强是如何实现的,本文先从如何抵御哈希传递攻击(Pass-the-Hash)3开始。

我们将涉及 LSASS 4 在 Windows 安全模型中的作用,然后讨论新的 PPL 模型背后的技术细节。要涵盖新的安全改进,我们也不得不涉及一些内部相关领域,所以我们也会对安全启动和保护变量稍加讨论。不过最重要的是,我们还将了解如何启用设置来抵御 PtH ,因为对于非 RT 的 Windows,该功能默认是禁用的。

LSASS 进程
Windows 使用众所周知的算法(NTLM)对本地用户帐户进行哈希运算并将结果保存到称为 SAM (安全帐户管理器) 的数据库中,SAM 本身是注册表的一个配置单元文件。和其他操作系统一样,总是存在各种脱机或在线攻击,目的不外是为了获取、重置,甚至重用保存在 SAM 中的哈希值,例如常见的”密码重置”应急启动盘,或者是恶意的权级提升等等。此外,还有其它一些加密数据存储在 SECURITY 数据库(也称为 LSASS 策略数据库),这是另一个注册表配置单元文件。该数据包括密文(secrets,如 LSA 密文信息),保存的纯文本密码等等。

Windows 通过被称为本地安全权威子系统(LSASS Local Security Authority Sub-System)的进程管理着这些信息的运行时状态,并最终负责所有登录操作 (包括远程登录到 Active Directory)。因此,为了要获得对这些数据的访问,主要通过用两种机制:

>>> 阅读全文

,

基于 ASP.NET 开发多租户应用

在本文中,我们将创建一个多租户(multi-tenant)的 SaaS 应用,用到的技术包括:

  • 应用框架:ASP.NET Boilerplate
  • Web 框架:ASP.NET MVC 和 ASP.NET Web API
  • ORM : Entity Framework
  • SPA 框架:Angularjs
  • HTML/CSS 框架:Bootstrap

在阅读本文之前1,读者可以先试试在线演示

创建应用模板
ASP.NET Boilerplate 提供了许多模板可以更快地创建启动项目。利用 http://aspnetboilerplate.com/Templates 的模板,创建启动项目如下:

这里选择的是 “ABP + module zero” 模板 ( zero 模块为框架添加了用户、角色、租户…等等管理基础件),该模板为我们创建了一个“即用”的解决方案,包括登录页面、导航以及一个基于 bootstrap 的布局。下载之后,你就可以通过 Visual Studio 打开该解决方案,其层级结构(包括单元测试项目)如下:

First, we select EventCloud.Web as startup project. Solution comes with Entity Framework Code-First Migrations. So, (after restoring nuget packages) we open the Package Manager Console (PMC) and run Update-Database command to create the database:

, , , ,

Previous Posts