土法炼钢兴趣小组的算法知识备份

【操作系统百科】Windows 内核与 Linux 的关键差异

文章导航

分类入口
os
标签入口
#windows#nt-kernel#iocp#irp#object-manager#wsl2#epoll#io-uring

目录

Linux 背景工程师维护 Windows 服务、或设计跨平台中间件时,常把 NT 内核当成”另一个 Linux”——结果在 I/O 模型、进程句柄、配置存储上踩坑。本文从 Russinovich、Solomon、Ionescu 所著 Windows Internals(第 7 版)与 Microsoft 官方文档出发,对照本站 Linux 内核系列 已有篇章,说明 NT 与 Linux 在架构、对象、I/O、进程、配置五条线上的关键分叉。

读完本文,你应能回答三个工程问题:

  1. 为什么 Windows 高性能网络服务普遍用 IOCP,而 Linux 传统路径是 epoll?
  2. 为什么 WSL2 选择跑真 Linux 内核的轻量 VM,而不是延续 WSL1 的 syscall 翻译?
  3. 跨平台中间件抽象 I/O 事件循环时,哪些语义不能照搬?

一、NT 分层与 Linux 子系统对照

1.1 三层内核 + 子系统

Windows NT 内核不是”一个平铺的 kernel/ 目录”,而是明确分为三层,再向上承接环境子系统(Environment Subsystem):

flowchart TD
  subgraph user [用户态]
    APP[Win32 / .NET 应用]
    DLL[子系统 DLL<br/>kernel32 / ntdll]
  end
  subgraph nt [Windows NT 内核态]
    EXEC[Executive<br/>对象管理/内存/IO/进程/安全]
    KRNL[Kernel<br/>调度/中断/DPC/同步原语]
    HAL[HAL<br/>APIC/时钟/DMA/总线]
  end
  HW[硬件]
  APP --> DLL
  DLL -->|Nt*/Zw* 系统服务| EXEC
  EXEC --> KRNL --> HAL --> HW

  classDef exec fill:#388bfd22,stroke:#388bfd,color:#adbac7;
  classDef kern fill:#3fb95022,stroke:#3fb950,color:#adbac7;
  classDef hal fill:#f0883e22,stroke:#f0883e,color:#adbac7;
  class EXEC exec
  class KRNL kern
  class HAL hal
NT 层 核心组件 职责摘要 Linux 近似路径
Executive Object Manager、Memory Manager、Process Manager、I/O Manager、Cache Manager、Security Reference Monitor 策略与对象语义:一切皆对象、IRP 分发、虚拟内存策略、访问控制 mm/fs/kernel/(进程部分)、security/drivers/base/
Kernel 调度器、DPC、定时器、IRQL 管理、内核同步 机制层:在 DISPATCH_LEVEL 及以下调度线程 kernel/sched/kernel/irq/kernel/time/
HAL 中断控制器、时钟源、DMA、固件接口 屏蔽主板/芯片组差异 arch/x86/drivers/acpi/drivers/clocksource/

Windows Internals, Part 1, Ch. 2 将 Executive 描述为”向子系统提供策略服务”的层;Kernel 提供”不依赖 Executive 策略”的底层机制;HAL 则把硬件差异收敛到少量接口。

Linux 是宏内核schedmmfsnetdrivers 同处内核态,没有 NT 那样严格的”Executive 服务”边界。对照 宏内核 vs 微内核 vs 混合内核:NT 常被归类为混合内核——Executive 子系统像一组紧耦合的内核服务,驱动通过 I/O Manager 的 IRP 模型接入,而不是像 Minix 那样以用户态服务器 + 消息传递为主。

1.2 Executive 子系统地图

Executive 不是单一模块,而是一组互相依赖的内核组件:

Executive 组件 管理对象/资源 与 Linux 对照
Object Manager 内核对象、handle、命名空间 fd 表 + VFS inode/dentry(部分语义)
Memory Manager 虚拟地址、工作集、段对象 mm_struct、VMA、do_mmap
Process Manager EPROCESSETHREAD、Job task_structcgroups v2
I/O Manager IRP、设备栈、完成端口 VFS + block layer + VFS I/O 路径
Cache Manager 文件数据缓存、懒写 page cache、页缓存深入
Security Reference Monitor token、ACL、完整性级别 uid/gid、capabilitiesSELinux
Plug and Play Manager 设备枚举、驱动加载 driver coreudev 用户态协作
Power Manager 休眠、设备电源状态 drivers/base/power/电源管理

Linux 工程师第一次读 Windows 文档时,常见误区是把 “Executive” 翻译成”执行体”后不知对应哪段代码——实际上它大致等于”除纯调度/中断外的内核策略层”。

1.3 Kernel 层:IRQL 与 Linux 抢占

NT Kernel 层用 IRQL(Interrupt Request Level) 约束可抢占性与锁使用:

IRQL 含义 典型活动
PASSIVE_LEVEL 普通线程上下文 系统服务、大部分 Executive 代码
APC_LEVEL 异步过程调用 用户 APC、部分 I/O 完成
DISPATCH_LEVEL 调度器、DPC 线程调度、延迟过程调用
DIRQL 设备中断级别 设备 ISR 上半部

在 DISPATCH_LEVEL 及以上不能发生线程切换,也不能访问分页内存。Linux 用 preempt_disable()spin_lock_irqsave()in_interrupt() 等组合表达类似约束,但没有统一的数字等级刻度。

对照 软中断与 tasklet线程化 IRQ:Linux 把大量中断下半部推到 ksoftirqdthread_irq;NT 用 DPC(Deferred Procedure Call) 在 DISPATCH_LEVEL 执行,语义接近”必须在锁内快速完成或再排队”的下半部。

1.4 HAL:为什么 Windows 需要这一层

HAL 存在的直接原因是 NT 要支持多种 CPU 架构与 OEM 硬件组合(历史上 x86、Alpha、MIPS、ARM64 等)。它提供:

Linux 在 arch/ 下为每种架构维护代码,在 drivers/ 下堆叠设备驱动——没有命名为 HAL 的单层,但功能等价物分散在 arch 与 platform 代码中。

对应用开发者,HAL 几乎不可见;对驱动开发者,通过 WDM/KMDF 接口间接触达。

1.5 系统服务入口:ntdll 与 syscall

Win32 应用大多调用 kernel32.dll,最终经 ntdll.dllNt* / Zw* 例程进入内核。这与 Linux 的 libcsyscall 指令路径类似。对照 系统调用 ABI

方面 Windows NT Linux x86_64
入口 DLL ntdll.dll libc.so
系统服务号 服务号表(因版本变化) syscall + __NR_*
稳定接口 Win32 API(用户态) POSIX + glibc 扩展
内核文档 WDK / Windows Internals man2Documentation/

跨平台中间件若直接依赖 NtReadFile 等 NT 原生 API,可移植性低于 Win32/POSIX 抽象层。


二、Object Manager:一切皆对象

2.1 设计动机

NT 把进程、线程、文件、事件、互斥体、信号量、节(section)、注册表键、定时器、Job 等统一为内核对象(Kernel Object),由 Object Manager 负责:

Windows Internals, Ch. 7 强调:这种统一性让安全子系统可以对任意对象类型套用同一套 ACL 语义,也让句柄表成为进程资源视图的中心。

Linux 没有等价的”全局对象管理器”。文件用 fd + struct file;进程用 task_struct;同步原语分散在 futex、per-CPU 队列、eventfd 等机制中——Unix 哲学是”一切皆文件”,但进程/线程并不是 fd

2.2 OBJECT_HEADER 与对象体

每个内核对象在对象体前都有 OBJECT_HEADER(布局因版本略有差异,以下为概念结构):

// 概念结构 — 详见 Windows Internals / WDK
typedef struct _OBJECT_HEADER {
    LONG PointerCount;          // 引用计数
    LONG HandleCount;           // 句柄计数
    UCHAR TypeIndex;            // 对象类型
    UCHAR Flags;
    // ...
    struct _OBJECT_TYPE *Type;
    // SecurityDescriptor 指针等
} OBJECT_HEADER;

对象类型由 OBJECT_TYPE 描述,包含类型名、回调例程(关闭、删除、安全方法等)。例如 File 类型对应文件对象,Process 类型对应 EPROCESS

Linux 对照:struct filef_count 引用计数,但没有跨越 file/process/event 的统一 header;kref/refcount_t 在各子系统内局部使用。

2.3 句柄表 vs 文件描述符

用户态看到的 HANDLE 是进程句柄表中的索引,不是内核指针:

CreateFile() → ObOpenObjectByName → 分配 HANDLE → 指向 FILE_OBJECT
ReadFile(HANDLE) → 句柄表解析 → FILE_OBJECT → I/O Manager 创建 IRP
方面 Windows HANDLE Linux fd
类型 索引(通常 4 的倍数,低 2 位标志) 小整数 0,1,2…
可表示资源 文件、进程、线程、事件、注册表键… 主要是文件式对象;pidfd 等是后补
继承 bInheritHandle、句柄复制 FD_CLOEXECfork 继承规则
跨进程共享 DuplicateHandle sendmsg SCM_RIGHTS、pidfd_getfd
内核结构 句柄表项 → 对象指针 + 访问掩码 fd 表 → struct file

Linux 用 fd 统一大量 I/O 与事件源;Windows 用 HANDLE 统一更广的内核对象集合。设计跨平台抽象时,int fdHANDLE生命周期与继承语义必须分开建模。

2.4 对象命名空间

命名对象可通过路径访问,例如:

Object Manager 维护目录对象构成的树。CreateMutex(NULL, FALSE, L"Global\\MyMutex") 在命名空间中创建或打开互斥体。

Linux 用文件系统路径命名对象:/dev/shm//run/、抽象 Unix domain socket 路径。没有单一的 \ 根命名空间;namespace 按维度(mount、pid、net)切分视图,而非 NT 式的对象目录树。

flowchart LR
  subgraph ntns [NT 对象命名空间]
    ROOT[\]
    ROOT --> BASE[\BaseNamedObjects]
    ROOT --> DEV[\Device]
    ROOT --> REG[\Registry]
    BASE --> EVT[Event/Mutex]
    DEV --> VOL[Volume]
  end
  subgraph linuxns [Linux 命名惯例]
    SLASH[/]
    SLASH --> DEV2[/dev]
    SLASH --> RUN[/run]
    SLASH --> TMP[/tmp]
    DEV2 --> SHM[shm, pts]
  end

2.5 安全描述符与打开检查

每个对象可有 SECURITY_DESCRIPTOR:所有者 SID、DACL(自主访问控制列表)、SACL(审计)。打开对象时 SeAccessCheck 根据调用线程的 access token 与请求的 GENERIC_READ 等权限做决策。

Linux 默认 DAC(owner/group/other rwx)+ 可选 MAC(SELinux/AppArmor)+ capabilities。NT 的 token 把用户 SID、组 SID、特权(privilege)打包,比单纯 uid/gid 更细,也更复杂。

Mandatory Integrity Control(MIC) 给对象和 token 标注完整性级别(Low/Medium/High/System),与 SELinux 的 type/enforce 不同,但都属于强制策略层。


三、I/O 模型:IRP 与驱动栈

3.1 IRP 是什么

I/O Request Packet(IRP) 是 I/O Manager 在驱动栈之间传递的请求包。一次 ReadFile 大致路径:

  1. I/O Manager 为线程创建 IRP,MajorFunction = IRP_MJ_READ
  2. IRP 下发到文件系统驱动(如 NTFS)
  3. 可能经过卷管理器、磁盘类驱动、端口/miniport 驱动
  4. 完成时调用 completion routine,最终线程从 ReadFile 返回或收到 IOCP 完成包
sequenceDiagram
  participant App as 应用程序
  participant IO as I/O Manager
  participant FS as 文件系统驱动
  participant Vol as 卷管理器
  participant Disk as 磁盘驱动
  participant HW as 硬件

  App->>IO: ReadFile(HANDLE, buf, len)
  IO->>IO: IoAllocateIrp / 初始化 IRP
  IO->>FS: 驱动栈顶部分发 IRP
  FS->>Vol: 转发(可能 STATUS_PENDING)
  Vol->>Disk: 转发
  Disk->>HW: 编程 DMA / 端口
  HW-->>Disk: 完成中断
  Disk-->>IO: IoCompleteRequest
  IO-->>App: 同步返回或 IOCP 完成

对照 VFS I/O 路径:Linux read()vfs_read → 具体文件系统 → page cache → block layer → 驱动;没有与 IRP 一一对应的单一内核数据结构贯穿全栈,但 request queue / bio 承担类似”请求载体”角色。

3.2 设备栈与 Filter Driver

驱动以设备对象(DEVICE_OBJECT) 栈形式叠加:

[Filter] → [Filter] → [文件系统] → [卷] → [磁盘] → [总线]

Filter Driver 可附加在栈的任意层:

Linux 有 fs/stack、block layer 的 blk-mq 插入点、FUSE 用户态文件系统,但 NT 的 Filter Manager(FltMgr) 框架提供标准化的 pre/post 回调、上下文挂载与实例管理——第三方筛选驱动生态更成熟。

Filter 可以:

3.3 IRP MajorFunction 与 Linux 对照

IRP MajorFunction 含义 Linux 近似
IRP_MJ_CREATE 打开/创建 openvfs_open
IRP_MJ_READ / WRITE 读写 vfs_read / vfs_write
IRP_MJ_DEVICE_CONTROL IOCTL ioctl
IRP_MJ_PNP 即插即用 uevent + driver probe
IRP_MJ_POWER 电源 dev_pm_ops

DeviceIoControl 对应 ioctl;Winsock 的 WSAIoctl 等网络 IOCTL 最终也走 I/O 栈或辅助库路径。

3.4 同步、异步与 STATUS_PENDING

驱动处理 IRP 时可返回:

用户态重叠 I/O 使用 OVERLAPPED 结构;完成可通过事件句柄或 IOCP 投递。

Linux 传统 read/write 以同步阻塞为主;异步路径包括 io_uringlibaioepoll + 非阻塞 fd 的组合。

3.5 Cache Manager 与内存映射 I/O

NT Cache Manager 与 Memory Manager、I/O Manager 协作,把文件数据缓存在系统工作集中,支持:

Linux page cachemmap 提供等价能力;对照 页缓存。差异在策略细节:NT 面向 GUI 与服务器混合负载长期优化;Linux 在 direct I/O、O_DIRECTfadvise 上给数据库等场景更多显式控制。


四、完成通知:IOCP、epoll、io_uring

4.1 就绪 vs 完成

高性能 I/O 多路复用的核心分叉是通知语义

模型 代表 API 通知含义 典型后续动作
就绪(Readiness) select/poll/epoll fd 可能可读/可写 用户态再调 read/write,可能 EAGAIN
完成(Completion) IOCP、io_uring CQE I/O 已结束 直接使用缓冲区,无需再次 syscall 读

epoll 内部EPOLLIN 表示 socket 接收队列有数据,不保证一次 read 读完。高并发下常见模式:epoll 唤醒 → 循环 read 直到 EAGAIN → 重新 epoll_ctl

IOCP 的 GetQueuedCompletionStatus 返回时,关联的 OVERLAPPED I/O 已完成,缓冲区已有确定字节数(或错误状态)。

4.2 IOCP 机制

I/O Completion Port(IOCP) 创建与使用流程:

// 创建完成端口;ConcurrentThreads 通常设为 CPU 数
HANDLE iocp = CreateIoCompletionPort(
    INVALID_HANDLE_VALUE, NULL, 0, concurrency);

// 将文件/socket 句柄关联到 IOCP;CompletionKey 用于区分连接
CreateIoCompletionPort(file_or_socket, iocp, completion_key, 0);

// 发起异步 I/O(需 FILE_FLAG_OVERLAPPED)
ReadFile(h, buf, len, NULL, &overlapped);

// 工作线程池等待完成包
ULONG bytes;
ULONG_PTR key;
OVERLAPPED *ovl;
GetQueuedCompletionStatus(iocp, &bytes, &key, &ovl, INFINITE);

关键设计(Windows Internals + Microsoft Docs I/O Completion Ports):

  1. 单队列多生产者:多个设备完成时统一入队到 IOCP
  2. 并发度控制ConcurrentThreads 限制同时唤醒的线程数,减轻惊群
  3. 与线程池集成BindIoCompletionCallbackSetThreadpoolWait 等 API 可复用 IOCP 语义

Linux 直到 io_uring 才在主线提供类似的完成队列抽象;传统 epoll 需要应用自己实现线程池与读写状态机。

4.3 三种模型对照表

维度 IOCP epoll io_uring
通知类型 完成 就绪 完成(可混合 OP_POLL)
系统调用频率 异步提交 + 完成 dequeue epoll_wait + 读写 syscall 批量提交 SQE、批量取 CQE
线程模型 内核感知并发度 完全用户态管理 SQPOLL 可选内核轮询
缓冲区管理 用户分配 OVERLAPPED 缓冲 用户管理 可注册固定缓冲(buffer ring)
跨平台 Windows Linux/BSD Linux 5.1+
与 sendfile 零拷贝 驱动/栈支持 splice IORING_OP_SEND

4.4 延迟与吞吐:引用数据,非本机 benchmark

本文不编造 QPS 倍数。公开资料中的共识(设计层,非特定硬件承诺):

跨平台网关(如 .NET Kestrel、Nginx、Envoy)的正确抽象是区分 ReadinessLoopCompletionLoop,而不是假设 API 名字可一一映射。

4.6 IOCP 并发度参数的工程含义

创建 IOCP 时的 NumberOfConcurrentThreads 参数告诉内核:同一完成端口上最多允许多少线程同时 runnable 处理完成包。设得过低会浪费 CPU;设得过高可能重现惊群后的锁竞争。

典型服务器进程池大小与 CPU 核数同阶,但 IO 密集 workload 可能用更多线程掩盖延迟——这与 Linux 上”epoll 线程数 vs worker 线程数”调参类似,都没有单一公式,需结合 profiling。

4.7 io_uring 作为 Linux 侧”完成阵营”参照

io_uring 深入 中,提交队列 SQE 可表达 READWRITEACCEPT 等操作,完成队列 CQE 返回结果——语义上接近”把操作提交给内核,完成后统一 dequeue”。与 IOCP 的差异包括:io_uring 可在同一 ring 混合多种操作;IOCP 仍依赖各 API(ReadFileWSARecv)发起异步 I/O,完成统一到端口队列。

设计跨平台运行时,io_uring 与 IOCP 的相似度高于 epoll 与 IOCP——但 io_uring 在 Linux 版本、权限(IORING_SETUP_*)与可移植性上仍受限。

4.8 何时 epoll 仍然足够

连接数万级、协议解析占主导、内核 5.1 以下、或依赖成熟 epoll 库(libevent、nginx)时,epoll 仍是 Linux 默认答案。Windows 侧没有 epoll 等价物——Winsock 的 select 性能差,迫使高性能路径走向 IOCP。

4.9 设计启示

场景 Windows 常见选型 Linux 常见选型
高并发 TCP 服务器 IOCP + 重叠 WSARecv epoll ET + 非阻塞;或 io_uring
数据库引擎 IO IOCP / 扩展页缓存 O_DIRECT + libaio/io_uring
跨平台运行时 抽象 IOCP/IOCP-like 抽象 epoll/kqueue/uring
静态链接嵌入式服务 不适用 有时直接阻塞 IO

中间件作者应把”事件循环类型”写进架构文档,避免 Windows 与 Linux 分支在代码审查时被当成”实现细节”而忽视语义差异。


五、进程与线程:EPROCESS 与 KTHREAD

5.1 进程与线程是独立对象

Windows 进程EPROCESS)与线程ETHREAD,调度实体为 KTHREAD)是独立内核对象:

创建进程:CreateProcess 分配新 EPROCESS 和初始线程;创建线程:CreateThread 在已有进程内新 ETHREAD

Linux task_struct线程即任务clone() 创建新 task_struct;共享 mm_struct 时为线程,独立 mm 时为进程。pidtgid 区分线程 ID 与进程组 ID。

flowchart TB
  subgraph win [Windows]
    EP[EPROCESS<br/>地址空间/句柄表/token]
    ET1[ETHREAD 1]
    ET2[ETHREAD 2]
    EP --> ET1
    EP --> ET2
  end
  subgraph lin [Linux]
    TG[task_struct 主线程<br/>tgid = pid]
    T1[task_struct 线程1]
    T2[task_struct 线程2]
    MM[mm_struct]
    TG --- MM
    T1 --- MM
    T2 --- MM
  end

5.2 字段级对照(概念)

概念 Windows Linux
调度实体 KTHREAD task_struct
地址空间 EPROCESS.VadRoot / 页表 mm_structVMA
打开的文件 句柄表中的 FILE_OBJECT files_struct → fd 表
安全上下文 Access Token cred(uid/gid/cap)
父子关系 EPROCESS parent/children 链表
创建 API CreateProcess fork + execve

Linux 没有 Windows 式”进程句柄”的一等对象;OpenProcess 在 Linux 上需通过 ptrace/proc/pid 权限或 pidfd 等组合模拟。

5.3 Job Object 与 cgroup

Job Object 将多个进程绑定到资源策略:

HANDLE job = CreateJobObject(NULL, NULL);
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info = {0};
info.BasicLimitInformation.LimitFlags =
    JOB_OBJECT_LIMIT_PROCESS_MEMORY | JOB_OBJECT_LIMIT_JOB_TIME;
SetInformationJobObject(job, JobObjectExtendedLimitInformation, &info, sizeof(info));
AssignProcessToJobObject(job, hProcess);

限制可包括:进程内存、CPU 时间、UI 访问、子进程创建、亲和性等。

对照 cgroups v2:Linux 的 pids、memory、cpu、io、cpuset 控制器粒度更细;Job Object 与 容器安全 中的进程边界都可用于沙箱,但 cgroup 是树形层级,Job 是 Win32 句柄对象。

Windows 容器(基于 Job + 命名空间隔离 + silo)与 Linux namespace + cgroup 的组合在概念上平行,实现路径不同。

5.4 线程池与 Linux 对照

Windows 提供系统级线程池 APIQueueUserWorkItem、PTP 回调),与 IOCP 深度集成。Linux 常用 pthread + 应用自建池,或由运行时(如 .NET ThreadPool、Go scheduler)管理。这不是内核能力差距,而是默认编程模型差异。


六、Registry 与 Linux 配置哲学

6.1 Registry 结构

Windows Registry 是层次化数据库,以 hive 文件持久化:

Hive 路径前缀 内容
HKLM HKEY_LOCAL_MACHINE 机器级驱动、服务、软件
HKCU HKEY_CURRENT_USER 当前用户设置
HKU HKEY_USERS 多用户配置
HKCR HKEY_CLASSES_ROOT 类型关联、COM

键值类型包括 REG_DWORDREG_SZREG_BINARY 等;权限通过 ACL 控制。

6.2 与 /etc、sysctl、/proc 对照

方面 Windows Registry Linux
配置存储 二进制 hive + API /etc 文本、.conf
内核可调参数 部分在 Registry sysctl / procfs / sysfs
原子更新 RegReplaceKey 等(有限) 文件替换 + SIGHUP reload
损坏影响 集中数据库损坏影响面大 单文件损坏相对隔离
可脚本化 PowerShell Get-ItemProperty shell、sysctl -w

Linux 哲学:文本配置、可 diff、可版本管理。Windows 哲学:类型化键值、统一 API——组策略、安装程序、服务依赖 Registry。运维 Windows 服务器时,reg query.reg 导出与备份恢复是必备技能。

6.3 服务与驱动配置

Windows 内核驱动与服务参数常存于 HKLM\SYSTEM\CurrentControlSet\...。Linux 模块参数通过 modprobe.d、kernel cmdline、sysfs module/parameters/ 暴露。


七、WSL1 与 WSL2:为何选虚拟机

7.1 WSL1:lxss 翻译层

WSL1 在 NT 上实现 lxss 子系统,将 Linux syscall 翻译为 NT 内核调用。未运行 Linux 内核

后果(Microsoft 官方架构文档):

优势:纯文件路径跨 Windows/Linux 无 VM 边界,小文件 IO 有时更快。

7.2 WSL2:Utility VM + 真内核

flowchart TB
  subgraph host [Windows 主机]
    WIN[NT 内核 / Hyper-V]
    FS9P[Plan 9 / virtio-fs 协议]
  end
  subgraph vm [轻量 Utility VM]
    LK[Linux 内核]
    DIST[发行版用户态]
  end
  WIN -->|Hyper-V 分区| vm
  LK --> DIST
  DIST <-->|\\wsl$\ 挂载| FS9P
  WIN --- FS9P

微软文档说明:WSL2 在 utility VM 内运行完整 Linux 内核,通过虚拟网卡、virtio、9P 协议访问 Windows 文件系统。代价:跨 OS 文件 IO 经虚拟化层;收益:syscall 兼容性与真内核行为

这与 FreeBSD 与 OpenBSD 对比 中”跑真内核”的论点一致:Linux 行为无法靠翻译层完美复刻。需要 Docker、eBPF、完整 PREEMPT_RT 实验时,应选 WSL2 而非 WSL1。

7.4 网络与 localhost 转发

WSL2 使用虚拟交换机;localhost 在 Windows 与 WSL2 之间默认不互通,需 localhostForwarding 或显式端口代理(Microsoft Docs WSL networking)。这与 Linux network namespace 下的 veth 配对类似——跨命名空间访问要单独配置。

7.5 文件系统性能边界

微软文档指出:跨 OS 边界访问文件(Windows 路径访问 Linux 内文件,或 \\wsl$\ 反向)性能低于同一 VM 内 ext4 本地路径。编译大型 C/C++ 工程、跑 git status 密集仓库时,应把源码放在 Linux 文件系统内——这是 WSL2 运维的常见实践,不是可选优化。

7.6 与 KVM 的平行

对照 KVM 架构:WSL2 的 utility VM 是专为开发场景优化的轻量分区,不是通用云 VM,但“主机内核 + 客户机内核”的分界与 KVM 相同。


八、安全模型速览

方面 Windows Linux
主体 Access Token(用户+组 SID+特权) uid/gid + capabilities
强制完整性 MIC 级别 SELinux/AppArmor label
沙箱 AppContainer、Job、受限 token namespace + seccomp
驱动签名 64 位生产环境强制 可选 lockdown、MOK
漏洞缓解 CFG、ACG、HVCI KASLR/KPTI 等

九、工程案例

9.1 .NET / Kestrel

ASP.NET Core 在 Windows 上默认利用 IOCP 完成 socket I/O;在 Linux 上使用 epoll(及正在推进的 io_uring 路径)。跨平台抽象 SocketAsyncEventArgs 背后是不同的内核通知语义——性能调优不能假设”异步”一词等价。

9.3 IIS 与 HTTP.sys

Windows HTTP.sys 内核驱动在 IOCP 之上提供 HTTP 解析与队列管理,IIS 可共享内核监听队列。Linux 上 Nginx/Envoy 在用户态完成同类工作,依赖 epoll/io_uring。两侧”把协议解析放哪一层”的架构选择不同,影响 CPU 占用与 TLS 终结部署。

9.4 案例边界

以上案例来自 Microsoft 与厂商公开架构描述(B 级),用来说明 I/O 路径差异,不构成性能排名。具体 QPS、延迟需在相同硬件与 workload 下实测,或引用第三方 benchmark 并标注来源。


十、内存管理:VAD 与 Linux VMA

10.1 虚拟地址描述符

Windows Memory Manager 用 VAD(Virtual Address Descriptor) 树描述进程用户态虚拟地址布局。每个 VAD 记录一段连续虚拟地址的范围、保护属性(读/写/执行)、映射类型(私有提交、映射文件、映像节)等。

VirtualAlloc 分配私有提交内存;CreateFileMapping + MapViewOfFile 建立文件后备映射——语义上对应 Linux mmap/munmapmm_struct / VMA

操作 Windows API Linux
匿名映射 VirtualAlloc mmap(MAP_ANONYMOUS)
文件映射 MapViewOfFile mmap 文件 fd
保护位 PAGE_READWRITE PROT_READ/PROT_WRITE
工作集修剪 SetProcessWorkingSetSize madvise(MADV_PAGEOUT)

10.2 工作集与 page cache 交互

进程 工作集(Working Set) 是该进程近期驻留物理页集合。Memory Manager 在内存压力下按 LRU 类策略修剪工作集;Cache Manager 的文件页与工作集统计交织——Windows Internals Ch. 5 花大量篇幅描述这一交叉。

Linux 用 active/inactive LRU 列表管理 page cache 与匿名页,回收子系统 在 direct reclaim 与 kswapd 中执行。两侧都区分文件页匿名页回收优先级,但数据结构与 sysctl 调参接口完全不同。

10.3 大页与 NUMA

Windows 支持大页(Large Pages、MEM_LARGE_PAGES,需 SeLockMemoryPrivilege);Linux 有 HugeTLB 与 THP(Huge Pages)。两侧在数据库、科学计算场景中都会启用大页以降低 TLB miss——启用方式与权限模型不同,不宜假设配置可移植。

NT 在 NUMA 机器上通过 NUMA 节点偏好 与进程亲和性影响分配;对照 NUMA 内存


十一、同步对象与 futex 对照

11.1 内核同步对象

Windows 提供命名/非命名同步对象,均为 Object Manager 管理的内核对象:

对象 用途 Linux 近似
Event 事件信号 eventfd、futex 等待
Mutex 互斥(可命名) pthread_mutex、futex PI
Semaphore 计数信号量 sem_t
SRWLock slim 读写锁 pthread_rwlock

CreateEvent / WaitForSingleObject 将线程置于等待状态,由内核调度器在信号时唤醒。Linux pthread 底层大量使用 futex 系统调用;Windows 等待路径走 KiWaitFor* 系列内核例程。

11.2 临界区 vs 轻量锁

用户态 CRITICAL_SECTION 先自旋再进入内核等待,类似 Linux 的 adaptive mutex。跨平台锁代码若假设”轻量锁永不进内核”在两侧都可能翻车——高竞争下都会落内核。

11.3 I/O 完成与事件

重叠 I/O 可将 OVERLAPPED.hEvent 设为手动重置事件,在 GetQueuedCompletionStatus 之外等待完成。Linux io_uring 用 CQ ring 取完成事件;epoll 不能原生表达”读完成”,只能表达”可读”。


十二、NTSTATUS 与 errno

Windows 内核与用户态广泛使用 NTSTATUS 码(32 位,严重性/设施/代码编码)。用户态 Win32 API 常映射为 BOOL + GetLastError()

概念 Windows Linux
成功/失败 NT_SUCCESS(status) 返回值 0 / -1
错误码 NTSTATUS / WIN32 错误 errno
可重试 STATUS_PENDING EINTREAGAIN
传播 IoCompleteRequest 带状态 负 errno 返回用户态

驱动开发必须理解 STATUS_PENDING 与完成例程配对;Linux 驱动返回 0 或负 errno,异步 bio 用 ->end_io 回调——模式相似,类型系统不同。


十三、Winsock 与 BSD socket

Winsock2 提供与 BSD socket 相似的 socket/bind/listen/accept,但:

跨平台网络库(libuv、Boost.Asio、Go net)在 Windows 分支用 IOCP,在 Linux 用 epoll/kqueue。对照 select/poll/epoll 演进


十四、调试与可观测性

工具 Windows Linux
内核调试 WinDbg / KD kgdb / crash
用户态栈 Visual Studio、WinDbg gdb、perf
内核跟踪 ETW(Event Tracing for Windows) ftraceperf
句柄泄漏 Process Explorer 句柄视图 /proc/pid/fd

ETW 是 NT 上高性能跟踪的基础设施;Linux 的 tracepoint + eBPF 在可编程性上更强。两侧都可回答”谁在阻塞 I/O”,但接口完全不同。

14.1 性能计数器

Windows Performance Counters(PerfMon) 暴露进程、内存、磁盘、网络计数器;Linux 用 /proc/stat/proc/diskstatsperf counters。跨平台监控代理(Prometheus node_exporter、Windows Exporter)必须分别映射指标名,不能假设字段一一对应。

14.2 驱动与符号

Windows 内核驱动需通过 WHQL/Attestation 签名策略(64 位生产环境);Linux 模块可 insmod 自建模块。分析崩溃转储时,Windows 需符号服务器(Microsoft 公共符号);Linux 需 debuginfo 包与 kdump。两侧”符号是否可用”决定 post-mortem 分析深度。


十五、选型检查清单

维护跨平台服务时,可用下面清单自检:

  1. I/O 循环:是否区分就绪与完成?Windows 是否用 IOCP?Linux 是否误把 epoll 当完成端口?
  2. 句柄生命周期CloseHandleclose(fd) 是否成对?子进程继承规则是否一致?
  3. 配置路径:Registry 键 vs /etc 文件是否文档化?
  4. 进程模型:Windows 是否误用线程当进程隔离?Linux 是否误用多进程代替 Job/cgroup?
  5. WSL:需要完整 Linux 行为时是否强制 WSL2?

15.1 与 POSIX 兼容层

Windows 上的 POSIX 子系统已退役;今天通过 WSL、MSYS2、Cygwin 提供 Unix 环境,但它们是附加层而非 NT 原生进程模型。Cygwin 在 Win32 上模拟 fork;WSL2 在 Linux 内核上提供真 fork。移植 Linux 守护进程时,路径选择决定 syscall 语义。

15.2 服务与 SCM

Windows 服务控制管理器(SCM) 管理长期运行进程的生命周期、恢复策略、依赖顺序——类似 systemd unit,但配置在 Registry 与 XML/JSON 服务清单中。Linux 进程生命周期 由 init 系统 + unit 文件描述;跨平台 PaaS 需抽象”安装、启动、失败重启”语义。

15.3 容器边界再述

Windows Server 容器与 Linux 容器都称”容器”,但隔离机制不同:Windows 用 silo、Job、命名空间过滤;Linux 用 namespace + cgroup。镜像格式、层文件系统、网络 dataplane 均不兼容——不能假设 Kubernetes 上”同一个 Pod 规范”在两侧行为一致。

15.4 文件系统与路径语义

NTFS 支持 ACL、压缩、重解析点(reparse point,含 junction、symlink);Linux ext4/xfs 用 inode 模式位与 xattr\\?\ 长路径前缀、MAX_PATH 历史限制与 Linux PATH_MAX 行为不同。WSL2 跨边界访问 \\wsl$\ 时,权限映射与大小写敏感性需单独测试——Microsoft 文档明确列出若干不支持或语义不同的操作。

15.5 时间与时钟

Windows 文件时间常用 FILETIME(100 ns 间隔,自 1601 年起);Linux 用 struct timespec(Unix 纪元)。日志关联与跨平台备份工具必须做时间基准转换。高精度计时:Windows QueryPerformanceCounter;Linux clock_gettime(CLOCK_MONOTONIC),对照 clocksource


十六、小结

主题 Windows NT Linux
架构 Executive / Kernel / HAL 分层 宏内核平铺子系统
资源抽象 Object Manager + HANDLE fd + task_struct + 分散机制
I/O IRP 驱动栈 + IOCP 完成 VFS + epoll 就绪 / io_uring 完成
进程模型 EPROCESS + ETHREAD task_struct + mm_struct
配置 Registry /etc + sysctl
Linux 兼容 WSL2 真内核 VM 原生

维护跨平台服务时,先把完成 vs 就绪句柄 vs fd注册表 vs 文本配置三条分叉搞清楚,再谈性能调优。

16.1 进一步阅读(本站)

主题 篇章
Linux I/O 完成模型 io_uring 深入
就绪多路复用 epoll 内部
进程与线程实体 task_struct
虚拟化边界 KVM 架构
容器隔离 容器安全

16.2 进一步阅读(书外)

Windows Internals Part 2 覆盖 I/O、存储、网络驱动细节;Windows Kernel Programming 适合需要写 WDM/KMDF 驱动的读者。Linux 侧则继续沿本系列 VFS 抽象系统调用边界 深入。

16.3 术语对照速查

Windows Linux
HANDLE fd(部分资源)
IRP bio / request(分散)
IOCP io_uring CQ(近似)
EPROCESS 进程 = 多个 task_struct 共享 mm
Registry /etc + /proc + /sys
Access Token cred
Job Object cgroup
Filter Driver kernel module stack / FUSE
ETW tracepoint + perf + eBPF
NTSTATUS errno

本表仅供快速联想,不能替代上文语义差异——尤其 完成 vs 就绪对象统一性 两处,速查表无法压缩。

最后强调 WSL 选型:若工作负载依赖 Linux 内核特性(eBPF、特定 io_uring op、自定义模块),WSL2 是唯一正解;若只需 GNU 工具链且极度依赖跨文件系统小文件 IO,才评估 WSL1 历史场景——微软已将默认安装导向 WSL2,新环境不应再按 WSL1 设计。

NT 与 Linux 的差异是系统性的:从对象模型到 I/O 完成语义,再到配置存储。跨平台工程师应把本篇当作对照地图,而不是 Windows 驱动教程——驱动开发请直接阅读 Windows Internals 与 WDK。


参考文献

书籍

官方文档

源码与对照


上一篇FreeBSD 与 OpenBSD 的不同选择

下一篇实时 OS 巡礼

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-05-23 · os

【操作系统百科】异步 I/O 模型 benchmark

epoll、io_uring、libaio、阻塞线程池——四种异步模型的真实性能对比。本文用统一 workload 量化 echo server、静态文件服务、数据库 I/O 场景下的吞吐、延迟与 CPU 开销。

2026-07-10 · os / architecture

【操作系统百科】OS 的下一个十年

综合 Windows/RTOS/Unikernel/Rust/机密计算/CXL 等前沿篇章,从 Rust 内核、机密计算、可拆分内存、io_uring 统一 I/O、eBPF 可编程内核五条趋势出发,按现在/三年/十年框架说明应用开发者应调整的假设与可执行建议。

2026-05-18 · os

【操作系统百科】io_uring 内核内部

io_uring 用共享内存 ring buffer 实现零 syscall 异步 I/O——SQ/CQ、SQPOLL、IOPOLL、注册 fd/buffer、multishot、安全模型演化。本文深入内核实现与工程实践。

2026-05-19 · os

【操作系统百科】epoll 内部

epoll 用红黑树管理 fd、就绪链表 O(1) 返回事件——打败 select/poll 的关键。本文讲 eventpoll 结构、LT/ET 语义、EPOLLEXCLUSIVE 与惊群、级联 epoll、EPOLLONESHOT、与 io_uring 的比较。


By .