- 操作系统
- 虚拟化
- CLI
【免费下载链接】ish
Linux shell for iOS
本文以 iSH 官方中文文档(README_ZH.md)为主线,结合本仓库内的真实源码与配置,系统讲解如何在 iOS 上构建运行 Linux shell 的完整流程:从子模块克隆、编译依赖、Xcode 构建 iOS 应用、Meson 构建命令行测试工具,到编译期日志通道的启用,以及被作者称为"最有趣的部分"的线程化代码解释器(gadgets)的实现原理。读完本文,你将掌握 iSH 的工程构建全链路、调试手段,以及其非传统 JIT 仿真器的设计思想与可维护性代价。
项目定位:用户态 x86 仿真 + 系统调用翻译
iSH 是一个运行在 iOS 上的 Linux shell。它采用两条核心路径实现"Linux 在 iOS 上跑起来":
- 用户模式(usermode)x86 仿真:在仿真器中执行 x86 指令流,而不是在真实 CPU 上运行。
- 系统调用翻译(syscall translation):把 Linux 系统调用映射到 iOS/BSD 内核能力之上,例如通过
fs/目录下的 real、fake 两套文件系统实现把 Linux 的文件操作落到 iOS 沙盒文件上。
从本仓库的目录结构可以清晰看出这条分层的架构(README_ZH.md):
- emu/:x86 仿真核心,包含指令解码(decode.h、modrm.h)、MMU/TLB(mmu.h、tlb.h)、浮点与 MMX 支持(fpu.c、mmx.c)等。
- kernel/:Linux 内核语义的重新实现,包括系统调用表(calls.c)、进程/任务(task.c)、信号(signal.c)、内存管理(memory.c)、文件系统(fs.c)等。
- fs/:文件系统抽象层,含真实文件系统(real.c)与"假"文件系统(fake.c)两套实现。
- asbestos/ 与 linux/:两种仿真引擎(asbestos 与 unicorn)的胶合层,后文解释器部分会详细展开。
作者在文档中也明确指出:项目当前的状态请以 issue 与提交记录为准(README_ZH.md),这提醒我们在深入使用前应先关注仓库的活跃度与已知问题。
上手准备:克隆子模块与编译依赖
iSH 是一个子模块密集型项目,仓库内嵌入了 deps/ 下的大量第三方依赖(如 libapps、libarchive、linux 等)。因此克隆时必须拉取子模块,文档提供了两种等价方式(README_ZH.md):
# 方式一:克隆时直接递归拉取子模块 git clone --recurse-submodules <仓库地址> # 方式二:克隆后再初始化子模块 git submodule update --init编译依赖清单
构建项目需要以下工具链与库(README_ZH.md):
| 依赖 | 说明 | macOS 安装 | Linux 安装 |
|---|---|---|---|
| Python 3 | 构建脚本运行环境 | 预装 | 预装或包管理器安装 |
| Meson | 构建系统 | pip3 install meson | pip3 install meson |
| Ninja | 构建后端 | 见 ninja-build.org | 包管理器安装 |
| Clang + LLD | 编译器/链接器 | brew install llvm | sudo apt install clang lld或sudo pacman -S clang lld |
| sqlite3 | 数据库依赖 | macOS 通常预装 | which sqlite3自查;缺失时sudo apt install libsqlite3-dev |
| libarchive | 归档库(构建tools/fakefsify所需) | brew install libarchive或sudo port install libarchive | sudo apt install libarchive-dev |
其中libarchive比较关键:文档特别提醒,如果在build目录下找不到tools/fakefsify,多半是因为系统缺少 libarchive 依赖(README_ZH.md)。这与 tools/fakefsify.c 的职责直接相关——它负责把 Alpine minirootfs 归档解包为"假文件系统"。
从本仓库的 meson_options.txt 还可以看到构建期更多可选配置项:
option('log', type: 'string', value: '') option('nolog', type: 'string', value: '') option('log_handler', type: 'string', value: 'dprintf') option('engine', type: 'combo', choices: ['asbestos', 'unicorn'], value: 'asbestos') option('kernel', type: 'combo', choices: ['ish', 'linux'], value: 'ish')其中engine选项控制使用哪套仿真引擎(asbestos为默认自研引擎,unicorn为外部仿真器接入),kernel选项决定跑 iSH 自研内核语义(ish)还是对接真实 Linux 内核(linux,对应 linux/ 目录),这些在构建测试工具时可以按需调整。
构建 iOS 应用:Xcode 配置要点
在 Xcode 中构建 iOS 应用时,需要完成两处关键配置(README_ZH.md):
- 修改
ROOT_BUNDLE_IDENTIFIER:打开 iSH.xcconfig,将该值改为你自己的唯一标识符(对应 Apple 文档中关于 bundle identifier 唯一性的要求)。xcconfig是 iSH 集中管理构建设置的方式,仓库内还有 App.xcconfig、AppLib.xcconfig、Project.xcconfig、ProjectDebug.xcconfig、ProjectRelease.xcconfig 等分层配置,以及针对不同目标平台(iOS/Linux/静态库)的 iOS.xcconfig、Linux.xcconfig 等。 - 更新开发团队 ID:注意文档强调是在project(工程)级的 build settings 中设置,而不是target(目标)级。
完成这两步后直接点击运行(Run),工程内置的脚本会自动执行其余构建步骤(Xcode 的 build phase 中会调用 xcode-meson.sh、xcode-ninja.sh 等脚本拉起 Meson/Ninja 完成仿真器、内核等 C 代码的编译)。如果遇到问题,可以提交 issue 求助。
构建命令行测试工具:Meson 工作流与 Alpine 文件系统
对于不涉及 iOS 应用外壳、只想在桌面环境快速测试仿真器与内核逻辑的场景,文档给出了一套完整的命令行工具构建流程(README_ZH.md):
# 1. 创建构建目录并配置 meson build # 2. 进入构建目录编译 cd build && ninja自建 Alpine 文件系统
仿真 Linux 需要一个根文件系统。文档推荐从 Alpine 官网下载Alpine minirootfs tarball for i386(注意是 i386 架构,与仿真器仿真的 x86 用户态一致),然后用tools/fakefsify转换为 iSH 自有的"假文件系统"格式:
tools/fakefsify $MinirotfsTarballFilename alpine其中第一个参数是下载的 tarball 路径,第二个参数是输出目录名(例如alpine)。转换完成后,即可在 Alpine 文件系统中启动 shell:
/ish -f alpine/bin/sh这里的-f参数表示使用假文件系统(fakefs)作为根文件系统。可以对照命令行入口 xX_main_Xx.h 的参数解析逻辑:getopt(argc, argv, "+r:f:d:c:")中-r/-f用于指定根目录(-f时同时把文件系统切换为 fakefs),-d指定工作目录,-c指定控制台设备。而 main.c 中的启动流程也印证了根文件系统之上的初始化动作:create_some_device_nodes()创建设备节点,随后do_mount(&procfs, "proc", "/proc", "", 0)与do_mount(&devptsfs, "devpts", "/dev/pts", "", 0)挂载 procfs 与 devpts,最后task_run_current()开始运行用户程序(main.c)。
用 ptraceomatic 做单步寄存器对比
如果ish的仿真结果可疑,可以用 tools/ptraceomatic.c 替代它:在一个真实进程中运行同样的程序,每一步单步执行并与仿真器的寄存器状态做对比,从而定位仿真偏差(README_ZH.md)。作者表示常用它来调试。该工具要求 64 位 Linux 4.11 或更高版本。
编译期日志系统:从禁用到 strace 级系统调用跟踪
iSH 的日志系统是在编译期通过"频道(channel)"机制启用的,默认情况下所有日志通道都被禁用(README_ZH.md)。
启用方式
两种构建路径对应两种配置入口:
Xcode(iOS 应用):在 iSH.xcconfig 中把
ISH_LOG设置为以空格分隔的日志类型列表。Meson(命令行测试工具):执行
meson configure -Dlog="<空格分隔的日志频道列表>"与之对应的 Meson 配置项定义在 meson_options.txt 中:
option('log', ...)用于启用频道,option('nolog', ...)用于排除频道,option('log_handler', ...)默认值为dprintf,指定日志输出处理器。
可用日志频道
| 频道 | 作用 | 说明 |
|---|---|---|
strace | 记录几乎每个系统调用的参数和返回值 | 文档称其为最有用的类型 |
instr | 记录仿真器执行的每一条指令 | 会让所有执行变得非常慢 |
verbose | 记录不属于其他类别的调试日志 | 兜底频道 |
strace的实现可以在 kernel/calls.c 中找到实证:在系统调用分发位置,STRACE("%d call %-3d ", current->pid, syscall_num)打印进程号与调用号(kernel/calls.c),调用返回后STRACE(" = 0x%x\n", result)打印返回值(kernel/calls.c)。而STRACE宏定义于 debug.h:#define STRACE(msg, ...) TRACE_(strace, msg, ##__VA_ARGS__),最终经由TRACE_/TRACE__宏按频道展开(debug.h)。具体的系统调用列表则定义在 calls.c 的syscall_table数组里(从sys_exit、sys_fork、sys_read、sys_write等按 Linux i386 调用号索引)。
如何发现更多频道
日志机制本身支持任意命名的频道:每个源文件通过定义DEFAULT_CHANNEL宏声明自己所属频道,例如:
- kernel/memory.c:
#define DEFAULT_CHANNEL memory - fs/tty.c:
#define DEFAULT_CHANNEL debug - emu/modrm.h 与 emu/decode.h:
#define DEFAULT_CHANNEL instr - asbestos/asbestos.c:
#define DEFAULT_CHANNEL instr
未显式定义的源文件默认落在verbose频道(debug.h)。因此文档建议:用grep搜索DEFAULT_CHANNEL变量,即可确认是否有新增的日志频道需要加入ISH_LOG/-Dlog列表(README_ZH.md)。debug.h中的宏体系(DEBUG_verbose、DEBUG_instr、DEBUG_strace、DEBUG_memory等)也印证了这一点——每个频道都有一套TRACE_xxx开关宏(debug.h)。
解释器:不是 JIT 的"线程化代码"仿真器
文档作者明确表示,iSH 中最有趣的部分是解释器,并纠正了一个常见误解:它并不是真正的 JIT(README_ZH.md)。
设计原理:gadgets 与尾调用链
传统 JIT 会把指令编译成机器码,而 iSH 的解释器生成的是一组函数指针数组,每个函数被称为一个gadget;每个 gadget 的末尾都以**尾调用(tailcall)**跳到下一个函数,从而构成一条执行链。这与部分 Forth 解释器使用的"线程化代码(threaded code)"技术同源。
这样做的好处是:相比简单的 switch 分发式仿真,速度大约提升3–5 倍。原因是 switch 每执行一条指令都要经历取指→分发→执行的循环开销,而线程化代码把"下一条指令是什么"固化在函数指针里,CPU 分支预测器可以沿尾调用链顺畅流水。
在 asbestos/ 目录中可以看到该引擎的完整实现:asbestos.c 是主逻辑,asbestos.h 中甚至给出了作者对 gadget 规模的估算注释("平均每个基本块约 N 条指令 × 4,即平均每条指令约 N 个 gadget/参数")(asbestos/asbestos.h),并记录了"指向最后一个 gadget 中 ip 值的指针"这类内部机制(asbestos/asbestos.h)。gadget 的汇编实现按架构拆分在 gadgets-aarch64/ 与 gadgets-x86_64/ 两套目录中,分别覆盖位操作、控制流、数学、内存、字符串等指令类别(如 control.S、memory.S)。而指令解码侧则在 emu/decode.h 中完成,解码器与操作数尺寸通过宏胶合(glue(DECODER_NAME, OP_SIZE))展开(emu/decode.h)。
汇编化的代价与作者的警告
不幸的是,作者几乎用汇编语言编写了全部 gadgets(README_ZH.md)。这在性能上可能是好决定(作者自称"永远无法确定"),但在可读性、可维护性和作者本人的理智上是灾难性的决定:编译器、汇编器与链接器的各种怪异行为层出不穷。为了保持心智健全,代码放弃了结构与命名方面的最佳实践:
- 宏和变量使用诸如
ss、s、a这类"描述性"名称; - 汇编器宏嵌套层数超乎想象;
- 几乎没有任何注释。
从 asbestos/gadgets-generic.h 可以瞥见一斑:gadget 被 push 到名为__TEXT,__text_bullshit的段——"bullshit"(鬼扯)这个词本身就说明了作者的态度。
因此文档给出了相当直白的警告(README_ZH.md):长期接触此代码可能使你失去理智,产生关于 GAS 宏和链接器错误的噩梦,或引发其他使人虚弱的副作用。这句话虽然是半调侃,但对于打算深入修改仿真器的贡献者而言,是必须认真对待的工程现实:性能与可维护性的权衡,在这里被推到了极端。
小结:从构建到调试的完整路径
以 README_ZH.md 为纲,iSH 的开发链路可以归纳为:子模块克隆 → 安装 Meson/Ninja/Clang/sqlite3/libarchive → Xcode(iOS 应用)或 Meson + Ninja(命令行测试工具)构建 → 用 Alpine minirootfs + fakefsify 准备根文件系统 → 用ish -f或ptraceomatic运行调试 → 按需开启 strace/instr/verbose 日志频道。而整个项目最值得玩味的技术内核,则是那个"不是 JIT 的 JIT"——以汇编 gadget 尾调用链换取 3–5 倍性能的线程化代码解释器,它以牺牲可读性与可维护性为代价,换来了 iOS 上 Linux shell 的可用体验。
- 操作系统
- 虚拟化
- CLI
【免费下载链接】ish
Linux shell for iOS
相关推荐
ish:在iOS上体验Linux shell的利器
ish:在iOS上体验Linux shell的利器 项目介绍 iSH是一款运行在iOS设备上的Linux shell应用,它通过x86用户模式仿真和系统调用翻译
操作系统虚拟化CLI革命性iOS终端神器iSH:在iPhone上运行完整Linux shell
革命性iOS终端神器iSH:在iPhone上运行完整Linux shell 痛点:移动端开发者的终端困境 作为一名开发者,你是否曾经遇到过这样的场景: 在通勤路
操作系统虚拟化CLIiSH Shell:在iOS上运行Linux的革命性方案,x86仿真与系统调用翻译全解析
iSH Shell:在iOS上运行Linux的革命性方案,x86仿真与系统调用翻译全解析 你是否曾因iOS设备无法运行Linux命令行工具而困扰?是否想在iPh
操作系统虚拟化CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考