Skip to content

Repository files navigation

PySpOS

用 Python 写的模拟操作系统 — 真子进程、EEVDF 调度器、Unicorn ELF 引擎、curses 首次开机向导。

License: MIT Python Release CI Stars

快速开始 · 命令速查 · 架构 · 路线图 · FAQ · 许可


PySpOS 是一个教学向的模拟操作系统:在用户态把进程调度、内存映射、 文件系统和引导流程都实现一遍,让想理解操作系统原理的人能直接读源码改源码。 它不是能在裸机上跑的系统。

✨ 特性

领域 能力 位置
调度 EEVDF(对标 Linux 6.6+):权重表、vruntime、虚拟截止期、信号状态机 src/process.py
进程 真子进程 fork/exec、PID 0 swapper / 1 init / 2 shell、孤儿收养、僵尸回收 src/forkexec.py
作业 cmd & 后台、jobs / fg / bg / wait、进程组与会话 src/shell/proc_cmds.py
引导 11 步 curses OOBE 首次开机向导、真实时区(zoneinfo + TZ)、i18n src/oobe.py
终端 CJK 双宽布局、控制字符净化、stty sane 自愈、输入重试上限 src/tui.py src/ttyutil.py
ELF 默认 Unicorn 执行真 x86 指令,syscall 转发到模拟层 src/elf_loader/unicorn_runner.py
配置 SpaceConfig v2:转义、行尾注释、null、列表、Schema 校验 src/spc.py
Shell 元数据驱动的命令注册表、分组 help、Tab 补全、管道与重定向 src/commands.py
OTA A/B 槽位、Ed25519 镜像验签、云端更新与本地回滚(Recovery 可从云端选版本装到指定槽位) src/ota.py secure_boot.py
Fastboot AOSP fastboot 协议服务端(只响应请求,不带本地界面)+ Tk 图形客户端;解锁 BL 会清数据 src/fastboot.py fastboot_gui.py
毛玻璃 SpaceGlass 窗口:按 compositor 规则绘制 VortexGlass 边框,有显示时开真窗口,无显示时渲染成 PNG src/apps/spaceglass.py src/spaceglass_theme.py

ELF 同时保留自研模拟器(SpaceCPU 1 Pro)作为无依赖兜底,Windows 也能跑。

🚀 快速开始

前置:Python 3.10+。日常 CI 在 Linux / 3.12 上跑测试、lint 与文档检查。 独立的 Cross-platform tests 工作流在发布 Release 或手动触发时运行 Linux 3.10 / 3.12 / 3.14 与 macOS、Windows 的全平台矩阵。

git clone https://github.com/ViudiraTech/PySpOS.git
cd PySpOS
pip install -r requirements.txt

./start.sh          # Linux / macOS
start.bat           # Windows

首次启动会进入 OOBE 向导(语言 / 时区 / 用户名),完成后写入 etc/.oobe_done,之后每次开机直接进 shell。

unicorn 是 ELF 的默认引擎,装不上会自动降级到自研模拟器,不影响启动。

跑测试:

python3 -m pytest -q
python3 -m pytest tests/process/ tests/shell/ -q  # 按子系统验证
python3 -m pytest -m "not integration" -q        # 筛除主要进程与协议集成测试
python3 tools/benchmark_runtime.py               # 调度性能与内存基准

签名信任链按 Android Verified Boot 的边界实现:启动器固定公钥,先验签 manifest,再校验槽位文件和 security_version;LOCKED 状态拒绝未签名镜像,UNLOCKED 状态允许开发镜像但必须显示警告。ROOT 只影响运行时权限,不能改 OEM 公钥或签名。Token 不再是授权凭据。

生产/锁定模式需要在系统外部签发 policy;私钥不得放入系统镜像或设备目录:

python3 boot_keygen.py --private-key boot_signing_key.pem

把命令输出的公钥加入 secure_boot.py 的 TRUSTED_PUBLIC_KEYS 后,再执行:

python3 boot_policy.py --lock --rollback-index 1 --private-key boot_signing_key.pem
python3 build_update.py --private-key boot_signing_key.pem --security-version 1

设备内的 ROOT、应用和旧 Token 都不能修改该信任域。UNLOCKED 模式的 OOBE 会自动生成 .pyspos_boot/device_signing_key.pem 供本地开发镜像签名;它不是 OEM 私钥,LOCKED 模式不会信任它。

UNLOCKED 开发槽位需要同步源码时,直接运行 python3 force_sync.py;它会拒绝 LOCKED 模式,不会绕过验签。若要清空旧槽位并让下次启动从 src 重建,运行 python3 reset_slot.py --slot slot_a。

⚡ Fastboot 模式

和 AOSP 一样,PySpOS 的 fastboot 没有本地界面:设备端只应答协议请求, 刷写、擦除、解锁这些特权操作只能由 host 侧的客户端发起。

# 设备端:进入 fastboot(锁定状态下需要 ROOT)
fastboot            # 或 reboot bootloader

# 开机就直接进 fastboot,跳过 OOBE 与 shell
python3 launcher.py --fastboot
#   也可以在 shell 里:reboot bootloader

# host 侧:打开图形客户端连接 127.0.0.1:5555
python3 fastboot_gui.py

reboot bootloader 与客户端的 reboot-bootloader 都会写一条启动请求 (.pyspos_boot/boot_mode,相当于 AOSP 的 BCB boot-fastboot),所以重启后 仍然停在 fastboot;普通 reboot / continue 会清掉它,下一次开机回系统。

客户端实现了 getvar / download / flash / erase / boot / continue / reboot / flashing unlock|lock,外加两条 OEM 扩展用于列出进程和发信号。

几条和真机一致的行为:

  • 解锁会清空用户数据,但保留已签名的系统镜像(槽位是不可变的安装目标)
  • 锁定状态下 flash / erase 一律被拒绝
  • 当前启动槽位没有签名镜像时拒绝锁定,否则设备会再也起不来
  • flash 只把镜像暂存到 ota/,由既有的验签安装流程落盘

Note

开机若出现「按 Enter 疯狂刷 ^M」,是上一次会话异常退出把终端留在了 icrnl 关闭状态。手动跑 stty sane 即可;3.2.0 起程序已内置该自愈 逻辑(见 FAQ)。

💻 命令速查

命令 说明
help / help <cmd> 分组命令表 / 单命令详情
ls cd cat grep 文件操作,支持 cat x.txt | grep foo
echo hi > a.txt 输出重定向,>> 追加
open gettoken 旧版答题入口,仅保留兼容性;Token 不再改变 Bootloader 信任
open getroot 获取 ROOT 权限,需父进程确认并写审计日志
bootloader_status / bl_status ROOT 查看 Bootloader 信任域、验签和防回滚状态
oobe 手动重跑首次开机向导
fastboot / reboot bootloader 进入 fastboot 模式(只跑协议,无本地界面)
launcher.py --fastboot 开机直接进 fastboot,跳过 OOBE 与 shell
python3 fastboot_gui.py fastboot 图形客户端:刷写、擦除、解锁 BL、向进程发信号
open spaceglass SpaceGlass 毛玻璃窗口:无显示设备时把窗口渲染成 PNG
run <file.elf> 运行 ELF,--stats / --map / --disasm N / --strace 可观测
run --engine native <f> 强制用自研模拟器兜底
ps kill <pid> signal <pid> SIGTERM 进程管理与信号
<cmd> & / jobs / fg / bg / wait 后台作业与进程组控制
sysmon 版本、槽位、进程、OTA 一屏总览
spc_validate spc_get spc_set spc_migrate SpaceConfig 工具链
ota_status / ota_rollback OTA 状态与本地回滚
recovery.erase 真出厂重置

📋 3.2.0 更新内容

分类 变更
调度器 EEVDF 完整实现,含 nice 权重表与信号状态机
进程 apps 从协作式模拟升级为真子进程;PID 0/1/2 引导语义;孤儿收养与僵尸回收
作业 后台作业、进程组、$!、作业控制
引导 OOBE 11 步向导、真实时区与语言、标记文件持久化
终端 CJK 双宽渲染、控制字符净化、终端输入自愈与重试上限
配置 SpaceConfig v2 解析器与 Schema 工具链
ELF 默认切换到 Unicorn 引擎
Shell main.py 拆分为 src/shell/*;命令注册表、Tab 补全、PATH=apps/
修复 open 沙箱 builtins 白名单过窄导致 apps 全线 NameError
修复 Pipe 端点接反、TypeError 语义混淆、curses 多窗口覆盖
修复 apps 在 EOF 下无限空转(zzlsb 等)
修复 子进程 stdio 中继协议不匹配导致输出丢失、后台作业挂死
整理 清理历史归档、统一路径解析、合并构建脚本、补 MIT License
审计 gettoken 改为分数为主、拦截机器连点、题库分层抽题

🗂️ 项目结构

PySpOS/
├── launcher.py           # 槽位选择 + 终端状态恢复
├── secure_boot.py        # AVB 风格签名、信任域与防回滚验证
├── boot_policy.py        # 外部签发 LOCKED/UNLOCKED policy
├── boot_keygen.py        # 生成离线 Ed25519 密钥
├── force_sync.py         # UNLOCKED 开发槽位强制同步
├── reset_slot.py         # UNLOCKED 开发槽位安全清空
├── fastboot_gui.py       # fastboot 图形客户端(host 侧工具)
├── src/
│   ├── main.py           # 入口 facade(启动状态 + 兼容重导出)
│   ├── kernel.py         # 主循环与提示符
│   ├── process.py        # PCB + EEVDF + PID 0/1/2 引导
│   ├── forkexec.py       # 真子进程引擎 + stdio 中继 + syscall RPC
│   ├── ttyutil.py        # 终端输入防御(stty sane / 归一化 / 重试上限)
│   ├── tui.py            # curses TUI(CJK 宽度、布局、字符净化)
│   ├── oobe.py           # 首次开机向导
│   ├── syslocale.py      # 时区(zoneinfo + TZ)与 i18n
│   ├── spc.py            # SpaceConfig v2
│   ├── ota.py            # OTA + A/B 槽
│   ├── fastboot.py       # fastboot 协议服务端(设备端)
│   ├── bootmode.py       # 启动模式请求(相当于 BCB boot-fastboot)
│   ├── btcfg.py          # Bootloader 配置与校验
│   ├── commands.py       # 命令注册表 / 补全 / PATH 解析
│   ├── elf_loader/       # ELF 解析、Unicorn 引擎、自研模拟器
│   ├── shell/            # 命令实现与分发
│   ├── common/           # paths / audit / reset
│   ├── apps/             # 应用程序
│   └── spfapps/          # SPF 脚本应用
├── splibc/               # SpLibC(C 实现,供 ELF 测试程序使用)
├── tests/                # 按 boot/process/shell/formats/ui/apps/ota/common/web 分组
├── tools/
│   ├── checks/           # 静态网站、对比度与 OTA 清单检查
│   ├── manual/           # PTY / ELF 手工诊断
│   └── benchmark_runtime.py # 可重复的调度性能与内存基准
├── reports/              # 性能数据与网页检查截图
├── build_update.py       # 唯一更新包构建入口
└── requirements.txt

🏗️ 架构速览

launcher(选槽位 + stty sane)
  └─ hotreset_env(子进程热重启)
      └─ kernel.loop()
          ├─ oobe.maybe_run_oobe()   首次开机,未完成时先走向导
          └─ shell.dispatch.handle_command
              ├─ sys_cmds   help / ls / cd / open / cat
              ├─ elf_cmd    run(Unicorn → 失败降级自研引擎)
              ├─ ota_cmds   ota_*(云端开关在 pyspos.py)
              └─ proc_cmds  ps / kill / signal / jobs / fg / bg / wait
                  └─ forkexec.fork_exec()   真子进程 + stdio 中继 + RPC
                      └─ process.py  EEVDF(0 swapper / 1 init / 2 shell)

apps 通过 src/apps/api.py 以 syscall RPC 请求特权操作,避免直接触碰 父进程内存。

🗺️ 路线图

  • OTA 更新与恢复模式
  • SpaceConfig 替换 json 配置
  • SPF 2.0 解析
  • 模拟进程管理与 signal(EEVDF)
  • ELF 加载器(支持 Windows,Unicorn 引擎)
  • 真子进程 fork/exec + PID 1 init 语义 + 后台作业
  • OOBE 首次开机向导 + 真实时区语言
  • 动态链接(INTERP)与 TLS 支持
  • SpaceGlass 毛玻璃窗口(VortexGlass 边框、标题栏按钮、orb 与斜向反光)
  • 可操控 PySpOS 的图形化 app(发 signal、解锁 BL 等)

❓ FAQ

你们的 ELF 是怎么跑的?

在用户态模拟一台 x86_64 计算机:默认由 Unicorn Engine 执行真实 x86 指令,syscall 转发到内置模拟层;没装 unicorn 就降级到自研的 SpaceCPU 1 Pro。 所以 Windows 也能跑。run --engine auto|unicorn|native 可切换,默认 auto。

为什么保留两个 ELF 引擎?

Unicorn 快且语义准,自研模拟器是无依赖的教学实现(体现 OS 原理)。两者 都保留是刻意的:对照着读能看清「真实内核怎么调度」和「教科书怎么调度」 的差别。

调度器是什么算法?

EEVDF(Earliest Eligible Virtual Deadline First),对标 Linux 6.6+:按权重 推进 vruntime,在 lag >= 0 的 eligible 任务里选虚拟截止期最早者。详见 src/process.py 顶部注释。

apps 是真进程还是模拟的?

3.2.0 起是真子进程(multiprocessing fork 语义)。每个 app 独立 PID, 由 process.py 的 EEVDF 记账,孤儿被 init(1) 收养、退出后回收成僵尸再清理。

终端里按 Enter 会疯狂刷 ^M,是 bug 吗?

3.2.0 前确实存在。根因是 curses 会话或子进程 os._exit() 跳过收尾,把 tty 留在 icrnl 关闭状态,回车的 \r 不再翻译成 \n 而被原样回显; input() 读到 ^M 这样的垃圾串就进入无限重试。

3.2.0 加了 src/ttyutil.py(stty sane 恢复 + 输入归一化 + 重试上限), 并在 tui.py、forkexec.py、oobe.py 三处退出路径都接上兜底。

OOBE 是什么?

Out-Of-Box Experience。检测不到 etc/.oobe_done 时在 kernel.loop() 里 跑 11 步设置,时区通过 zoneinfo 真实设置 TZ。可随时用 oobe 命令重跑。

我想做在线 OTA 更新,有什么建议?

更新包别直接放 GitHub Pages(慢),推荐用 CDN 加速。请求头一定要写 (别问为什么)。上线前务必做签名/哈希校验,并准备好失败回滚路径。

这个项目有什么用?

让新手理解操作系统基本操作,也供作者学习 OS 原理。再次强调:这是模拟 操作系统,不是能在裸机上跑的系统。

代码开源吗?

是,全部代码现已迁移到 ViudiraTech/PySpOS,本仓库是唯一 活跃来源。

原仓库 GoutouStdio-cn/PySpOS 已被 owner 归档为只读(issue 与 PR 均已关闭),仅作历史存档。 想找 3.2.0 之前的老版本,去那边的 beta_version / pre_version 两个 tag; 3.2.0 起的历史版本看本仓库的 Releases。

代码怎么写的?

Python 为主,另有部分 C(splibc/ 里的 ELF 测试程序)。核心代码在 src/。

🌐 在线访问

🖥️ 支持系统

Windows 7 及以上、Linux 各发行版、macOS。兼容性上更推荐 Windows。

🙏 贡献者

  • ViudiraTech
  • 王俊X
  • 陈X

📄 许可

MIT License © 2022-2026 GoutouStdio

open spaceglass 使用的 VortexGlass 主题素材来自 Uinxed-Kernel(Apache-2.0), PySpOS 不内置这份素材:app 会按系统树 → 系统主题目录 → 桌面/家目录的顺序 自动搜索名为 vortexglass 且 34 个文件齐全的目录,也可以用 VORTEXGLASS_THEME_DIR 或 --theme-dir= 直接指定。把素材装进系统树时放在 assets/themes/vortexglass/,并按 Apache-2.0 第 4 条保留原项目的 LICENSE 与 NOTICE 归属声明。

About

用 Python 写的模拟操作系统:真子进程 fork/exec、EEVDF 调度器、Unicorn ELF 引擎、curses 首次开机向导

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages