调度、执行与同步¶
调度¶
EEVDF 公平任务使用增强红黑树组织选择状态;CPU 所有权、任务交接与 统计在既有调度锁和执行事务边界内管理。各 CPU 用 ACPI PM 时钟校准 250 Hz LAPIC 抢占,失败有明确计数回退,回退不保证实际频率。BSP 也使用 本地抢占时钟;只有 100 Hz PIT 推进全局 tick、压力重试和 USER_HZ 统计, 本地抢占和 IPI 不制造额外墙钟时间。对外 CPU 时间仍以统一 USER_HZ 结算,精度 10 ms。 保持已有等待、迁移、信号和离线语义,不能从优化吞吐推导输入延迟必然改善。
内部公平运行时间优先累计各 CPU 共用的 ACPI PM 计数,保留小数余量并 延伸 24/32 位回绕,避免 PIT IRQ 延迟后旧 uptime 插值停止增长而漏计运行时间。 源不可用时回退旧 uptime,源切换不使公平时钟倒退。这不是用户时钟源; 正常调度采样须在一次硬件回绕内,不对长时间暂停后的绝对时间作保证。
发布可运行任务时,调度器设置各在线 CPU 的重新调度标志,并通过独立的 LAPIC 向量唤醒远端 CPU;同一未处理标志只发送一次 IPI。自 CPU、离线 CPU 及 SMP 启动交接完成前不发送。IPI 只确认本地 APIC,用户帧进入正常的 调度检查;内核帧继续执行,到原有安全检查点再切换,不抢占持锁内核代码。 任务的 CPU 预留、亲和性、停止状态和 EEVDF 权重保持原有规则。
setpriority(PRIO_PROCESS, tid, nice) 允许有效 UID 与目标真实/有效 UID
相同的调用者降低目标的调度优先级,因此用户态 renice 子进程可以把
同属主的父进程从 nice=-10 恢复到 0。提高优先级或修改其他属主需要
CAP_SYS_NICE;仅有 uid=0 不绕过 capability 检查。值钳制到 -20..19,
目标缺失返回 ESRCH,内核 getpriority 保持 Linux 的 20-nice 编码。
这里不新增尚不支持的 PRIO_PGRP、PRIO_USER 或 RLIMIT_NICE 接口。
权限与编码参考 Linux setpriority(2)。
停驻与 I/O¶
符合条件的 syscall 返回在原有执行事务完成;pselect 临时信号掩码跨内部 停驻保留。向量 I/O 直接调用共享读写后端,保留部分结果、偏移和 retry 状态, 避免递归进入大分发栈帧。等待路径不能把仍被 CPU 使用的任务误认为已交接。 锁开销、临界区与资源生命周期必须一起验证,不通过跳过真实同步换取速度。
无需 I/O 的私有 demand-zero、COW 和只读可执行页缓存命中,在读 gate 或 排队后的写 gate 内解决后,均通过既有用户返回检查决定是否继续当前任务; 不再因为 gate 类型或缓存命中本身强制让出 CPU。待处理信号、抢占标志、 亲和性、任务/CPU 所有权和帧检查仍可拒绝直接返回。共享 MM、实际文件页入、 回收和错误路径保留原有串行化及调度检查点。
普通文件读写、目录枚举和文件间传输沿用 2 ms 工作预算,在完整后端操作
完成后保存进度、释放所有权并通过原有内部重试路径继续。预算改用
time_work_counter_us():校准可用时读取当前 CPU 的 TSC,避免 IRQ 延迟时
旧 uptime 插值到一个 tick 后停止增长。该计数只在一次不迁移、不睡眠的
调用内相减;未校准时回退旧 uptime。它不向用户公开,不改变
clock_gettime、vDSO、USER_HZ 或任务公平时间;单次后端操作仍可能超过
预算,因此这不是整段 syscall 或输入响应的硬实时上限。
管道数据事务¶
已准备好的匿名管道标量读写可使用执行读事务。读事务保留任务、描述符、 OFD、管道和地址空间的生命周期,各管道自己的数据锁保护读写位置和完整 记录的复制,因此不同管道可并行处理,同一管道的小写入仍保持原子性。 关闭、描述符替换、端点计数、扩容及回收继续使用独占执行事务;已有写事务 排队时拒绝新的读事务,保持这些操作的进展。
该路径只接纳没有共享 MM、没有在途续调且缓冲区已驻留的用户任务。
复制前检查全部页面;输出还要求各级页表及叶项具有真实硬件写权限。
缺页、COW、共享文件首次写入通知和设备映射不在数据锁内处理,统一回到
完整路径。命名管道及带路径的描述符保留原来的 inode 通知,跟踪中的任务
也走完整分发。每次复制仍最多 32 KiB,不改变 PIPE_BUF、容量或
大请求短读写语义。
数据锁在唤醒等待队列、通知 poll 或检查调度之前释放。阻塞等待和
SIGPIPE 继续由独占路径处理;检查条件与登记睡眠之间不会插入并行
数据修改。已完成的调用更新原有进程 I/O 统计,并用同一用户返回检查处理
待处理信号、CPU 亲和性和重新调度请求。
tools/test_pipe_parallel.py 检查独立管道进展、多写者记录、生命周期
写事务排斥和不可直接复制页面的回退;原生探针
tools/tests/pipe_parallel_runtime_probe.c 检查真实进程的数据和缺页/COW
结果,--pty 可在外部负载期间测量 PTY 递送延迟。
私有页故障与执行事务¶
执行读事务只接纳没有共享 MM 的用户任务:私有匿名缺页、已映射的可写
私有 COW 页以及已在页缓存中的可执行映像只读文件页可以并行处理。COW
沿用原有复制和物理页引用规则,也适用于符合条件的私有文件映射;初次
文件页装入走缓存快速路径时,VMA 必须持有 exec_source,其来源保留
路径、提供者及 inode 引用,并禁止对该文件写入。缓存命中在缓存锁内取得
页引用,映射失败时归还;文件偏移、段尾、EOF 和执行权限沿用慢路径的规则。
读事务不调用文件系统、设备或来源生命周期回调,也不执行用户复制或调度。
共享 MM(包括 CLONE_VM 和 vfork)、共享/设备映射仍走原来的独占故障
路径。不符合上述缓存条件的初次文件页装入(包括可写文件和 tmpfs)、缓存
未命中及不符合段尾条件的页也使用独占路径;已经存在的合格私有 COW 页
仍可按上述无 I/O 分支处理。内核态用户复制故障保留可重入的写事务。
读事务因已有写者排队而被拒绝时,任务按 FIFO 获得写事务后先尝试同一批
无 I/O 的私有工作。上述任一私有工作解决后,在 IRQ 关闭且持有执行事务时
使用正常的用户返回谓词检查任务、信号、调度请求、CPU 亲和性和帧;检查
通过才直接恢复,不因读/写 gate、COW 或缓存命中本身强制重新选择任务。
检查拒绝时先释放事务再调度。其他故障保留原有调度行为,定时器仍可正常抢占。
全局页表变更序号使用原子递增和读取,映射更新在发布序号之前完成。 每个 CPU 在载入 CR3 前记录观察到的序号;载入期间出现的新变更会使下次 条件检查时重新载入。该序号仍是全局保守失效机制,不改变页表结构、 跨 CPU shootdown 协议或应用 ABI。
执行事务诊断¶
CONFIG_LOCK_STAT 默认关闭;启用后只构建诊断,启动时采样仍关闭。
配置开启时,/proc/reliefnt/lock_stats 提供 ReliefNT 自有格式,
并非 Linux /proc/lock_stat 的格式或等价实现。该文件权限为
0644 root:root,写入除了文件 DAC 检查外还要求当前任务拥有
CAP_SYS_ADMIN。在字节偏移 0 单次写入 1 开始采样、0
停止并保留计数、reset 清空当前窗口;三者均可附一个换行。其他
输入或非零写入偏移返回 EINVAL。零长度截断为无副作用的成功,
允许常见 shell 重定向;非零长度截断被拒绝。O_SYNC 和 O_DSYNC
写入成功后返回已提交的字节数;显式 fsync 仍按 proc 原有语义返回
EINVAL。writev 逐段提交,首段为完整命令时可能返回正的短写,
后续片段不会被当作同一条原子命令;需要完整命令时使用一次 write。关闭配置时该节点不存在,
proc 的原有只读挂载属性不变;开启配置时 proc 默认挂载为可写以提供
此控制节点,其余生成节点仍拒绝写入和元数据修改。显式只读挂载或
重挂载仍拒绝控制节点写入;一般 proc 节点不会因此变成可写文件。
格式头部为:
reliefnt_lock_stats 1 unit=tsc_cycles enabled=<0|1> epoch=<n> histogram=log2_cycles irq=gate_masked_window
其后每个非空 CPU/固定作用域一行,包含 cpu、scope、acquisitions、
contentions、wait_total、wait_max、hold_total、
hold_max、irq_total、irq_max、clock_errors 以及三个
wait_hist、hold_hist、irq_hist 逗号分隔的 32 桶计数。
桶 0 覆盖 0–1,桶 i(1–30)覆盖 2^i 至 2^(i+1)-1,桶 31
覆盖不少于 2^31 周期的所有值,因而高端百分位只能确定下界。
每个计数和累计值达到 UINT64_MAX 后饱和;最大值不回绕。TSC 倒退
时丢弃对应的间隔并增加 clock_errors,不会把无效值算成零延迟。
同一个区间的等待、持锁和中断窗口分别校验,因此一次倒退可产生多个
错误计数。时间采样使用带序列化屏障的原始 x86 TSC 周期,不转换为微秒;
其频率、虚拟机迁移及硬件稳定性决定了可比性。不要将未经校准的周期
当作实时时间,或跨不同频率、不同系统直接比较。
一次成功外层写事务或读事务算一次 acquisition,递归写入口不重复
计数;读尝试失败不生成持有记录。contentions 只统计成功写事务
在排队期间实际进入等待循环的次数。所有成功事务均记录从采样起点到
获得所有权的等待窗口,因此无争用事务也包含采样本身的固定开销;这与
Linux lockstat 按锁类与读写状态分类的统计并不相同。持有从获得所有权
附近的采样点开始,到最终释放或 suspend 的采样点结束。IRQ 窗口从
本次 gate 保存并屏蔽中断之后的采样点开始,到恢复中断之前的采样点
结束,包含等待和持有,但不覆盖采样点外的指令;调用者原本已经关中断
时也会记录这个重叠窗口。它既不是全系统所有 cli/sti 区间,也不
等于所有 IRQ 延迟,多个 CPU 的窗口总和不是墙钟时间。启用采样本身会
增加持有和关中断成本,测量时必须和关闭采样的对照组一起报告。
generic 是未明确分类的 gate 入口;fault 是外层页故障,
sched 包含任务选择、空闲维护重新进入和明确标记的资源清理;syscall_io 包含常见文件
打开、关闭、读写(包括定位和向量 I/O)、stat、目录、路径、属性、
同步及挂载调用,syscall_process 包含进程创建、退出、等待、
exec 和 futex,syscall_memory 包含映射、保护及内存管理,其他
syscall 归 syscall_other。这些是入口分类,并非调用内每个子操作的
独立计时;例如入口的网络、设备、回收和输入维护仍计入该次 syscall。
嵌套页故障也保留外层作用域。suspend 在释放 gate 时结束持有窗口,
不计算等待或睡眠时间;resume 在实际恢复 CPU 上开始独立的 resumed
事务,不继承睡前作用域,也不跨 CPU 相减 TSC。此选择保留现有模块可见
suspend token 布局。
计数按 CPU 和固定作用域保存,不记录内核地址。采样关闭时采样钩子不
新增 CPU 查询或读取 TSC,也不写计数;原有写 gate 为识别 CPU 的查询在
不支持 RDPID 的机器上仍可能执行 RDTSCP。reset 使用新 epoch,使先前已开始但尚未提交的采样
不能污染新窗口;关闭/重新开启也使旧持有段失效:已计入的 acquisition 可能没有对应的
持有或 IRQ 样本。控制写入在执行事务内串行化;并发快照逐字段读取,不能假定所有 CPU 是同一瞬间。每次
可读取方式打开文件捕获一个固定视图,使同一打开文件的短读、偏移和
重读一致;重新打开才观察新数据,捕获可能因内存不足失败并引入额外开销。仅写入方式
打开不分配该视图,方便在内存不足时停止采样。控制会话与 epoch
在内部代数耗尽时返回 EOVERFLOW 而不回绕,代数耗尽时仍可停止
已经开启的采样。
Linux lockstat 的分类和时间口径见 Linux Lock Statistics;该参考用于理解边界, 不能把本文件标记为 Linux 兼容输出。