把 XAMPP 搬到鸿蒙 PC:在鸿蒙 PC 上跑 Apache httpd 2.4.69实战项目
欢迎加入开源鸿蒙 PC 社区:https://harmonypc.csdn.net/
欢迎在 PC 社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_XAMPP
这不是把一个
httpd可执行文件复制进应用目录就结束的移植。OpenHarmony 应用沙箱对进程启动和后台服务生命周期有自己的边界;本项目最终选择把 Apache httpd 2.4.69 及其依赖交叉编译为静态对象,链接进 N-API 原生模块,再由 ArkTS 控制面板在线程中启动它。本文结合真实工程、构建脚本和真机界面,记录架构选择、交叉编译、链接注册、崩溃排查,以及 HTTP 服务验收过程。

图 1:控制面板显示 Apache 为 RUNNING,并提供本地服务地址、日志、htdocs 和诊断入口。

一、目标不是“做个像 XAMPP 的页面”
XAMPP 是 Apache Friends 提供的集成式本地开发环境。对桌面开发者来说,熟悉的体验是打开控制面板、启动 Apache,再将浏览器指向本机地址。但在 OpenHarmony PC 上,照搬传统架构并不合适:HAP 不是普通桌面安装包,应用也不能假设拥有系统级守护进程管理能力。
这个工程分阶段实现:
| 项目 | 当前实现 |
|---|---|
| Web 服务 | Apache httpd 2.4.69,静态链接运行 |
| 运行方式 | 嵌入应用进程,由 N-API 创建 pthread 承载 |
| MPM | event,使用 -X 单进程模式运行 |
| 监听地址 | 127.0.0.1:8080 |
| 文档目录 | 应用沙箱中的 filesDir/xampp/htdocs |
| ArkTS 界面 | 启停、运行状态、日志、htdocs 文件管理、诊断信息 |
| 规划中的组件 | MariaDB、PHP 等属于后续阶段,当前不能视为已实现 |
这里有个很重要的产品边界:界面上显示 MariaDB、PHP 的 Phase 2 占位卡片,不等于当前已经集成数据库或 PHP 运行时。当前已经跑通并验收的是 Apache 核心链路。

图 2:桌面浏览器访问 127.0.0.1:8080,返回由真实 Apache httpd 提供的欢迎页。
二、架构选择:不启动子进程,把 httpd 变成应用的一部分
传统 XAMPP 通常通过独立 httpd 进程运行服务。这个工程采用不同路径:

构建时将 httpd 的 main() 符号改名为 apache_main(),然后把 httpd、MPM 和选定模块等静态对象合并进核心归档。libxapache.so 再通过链接器将归档整体链入,并导出 N-API 接口供 ArkTS 调用。
这种结构带来几个直接好处:
- 不需要把独立可执行文件放进沙箱再尝试启动。
- 服务的生命周期归应用控制,Start/Stop 和界面状态可以直接联动。
ServerRoot、配置、日志、网页都能置于应用自己的filesDir下。- 监听回环地址和非特权端口,服务面向本机开发场景,而不是默认暴露到局域网。
相应的代价也很明确:Apache 与 UI 共用应用进程,启动、停止、崩溃隔离和文件描述符管理必须由集成层谨慎处理;它不是一个脱离 UI、由系统服务管理的独立 daemon。
三、交叉编译:依赖栈和脚本流水线
工程将本地原生依赖交叉编译为 ARM64 OpenHarmony 可用的静态库。项目 README 记录的依赖版本为:
| 组件 | 版本/用途 |
|---|---|
| PCRE2 | 10.45,httpd 正则表达式依赖 |
| APR | 1.7.6,Apache 可移植运行时 |
| APR-util | 1.6.5,APR 扩展组件 |
| Expat | 2.7.3,APR-util XML 依赖 |
| Apache httpd | 2.4.69 |
完整构建流水线由 scripts/ 下的脚本拆分,避免每次改动都从头手工拼接:
00-env-prep.sh 准备构建环境及 OHOS SDK 工具链
01-toolchain-test.sh 检查交叉工具链
02-build-stack.sh 构建 PCRE2、APR、Expat、APR-util
03-build-httpd.sh 构建 Apache httpd
04-assemble-core.sh 聚合 httpd、MPM、模块和静态依赖
05-assemble-so.sh 改名 exit 引用并链接 N-API .so
06-fix-modules-registry.sh 修补静态模块注册表
交叉编译环境主要由 scripts/cross-env.sh 提供。这个项目不是只运行一次 configure && make:目标平台 sysroot、OHOS CRT 布局、LLVM runtime 和最终链接方式都必须匹配。构建脚本将环境设置集中起来,后续步骤复用同一组编译器和链接器变量。
Windows 侧负责 DevEco/Hvigor 的 HAP 构建,WSL 侧负责原生依赖和 .so。这是一个双环境流水线:
WSL:源码与静态依赖 → libxapache_core.a → libxapache.so
Windows:entry/libs/arm64-v8a/libxapache.so → Hvigor → HAP
如果原生库重新链接后,HAP 却仍包含旧库,优先核对 entry/build/default/intermediates/stripped_native_libs/ 与最终 HAP,而不要只看 entry/libs/arm64-v8a/。这个工程曾遇到过 Hvigor 中间产物未更新的问题;最终以 HAP 内实际打包的 .so 为准。
四、静态模块不是“链接进来就能用”
本次移植最有代表性的坑之一,是 Apache 模块的链接状态与运行时注册状态并不等价。
原构建配置中,一部分模块按 DSO 方式配置;但本项目又将模块静态合并进 .so,运行时并没有独立 .so 文件供 LoadModule 加载。结果是对象文件存在于链接产物中,Apache 却没有把模块登记为预链接模块,配置里的 Require 等指令因此可能被报告为未知指令。
修复点落在 modules.c 的几个表:
ap_prelinked_modules[]:预链接模块实例列表;ap_prelinked_module_symbols[]:模块名到符号的映射;ap_preloaded_modules[]:启动时需要预加载的模块。
06-fix-modules-registry.sh 根据实际静态模块集合生成这些注册项,并以 -fPIC -DPIC 编译 modules.o 后替换进核心归档。这里要坚持一个原则:注册表必须与真正存在并参与链接的模块集合一致。工程排查过程中发现 mod_mime_magic 没有生成静态库,却被注册表引用,形成未定义的 mime_magic_module,最终留下不合法的重定位并导致原生模块加载失败。修复不是盲目添加符号,而是从构建列表和注册表同时移除当前并未构建的模块。
此外,链接前还有针对 httpd exit() 调用的定点符号改名。OpenHarmony 应用线程不能把 httpd 的配置错误路径直接变成进程级退出;05-assemble-so.sh 只重命名归档内对 exit 的引用到 xapache_exit,N-API 桥接层记录退出码并通过 pthread_exit() 结束服务线程。相比在全局覆盖 libc exit(),这种按归档对象改符号的方式影响范围更小,不会扰动同一个 .so 中的其他系统/运行库代码。
这部分排查也说明了一个通用经验:原生 .so 加载失败不一定是 N-API 接口写错。检查未定义符号、动态重定位、PIC 属性以及最终打包的库版本,往往比只盯着 ArkTS import 更快找到根因。
五、N-API 桥接:从 JavaScript 参数到 Apache 线程
ArkTS 层的 XApache 类负责计算 serverRoot、首次初始化目录布局,并将命令行参数传给 libxapache.so。核心参数形态如下:
httpd -d <serverRoot> -f <serverRoot>/conf/httpd.conf -X -k start
N-API 层将参数复制到原生堆内存,创建 server_thread,在线程中调用 apache_main()。服务线程退出后更新运行状态和退出码。配置或启动错误时,httpd 原本可能调用 exit(1);桥接层通过被定点改名的 xapache_exit() 记录错误并退出当前线程,避免把预期的服务错误升级为整个应用的崩溃。
为了便于诊断,应用线程还会把 stdout/stderr 指向 logs/stderr.log。OpenHarmony 应用的标准错误输出不一定能像桌面终端那样直接查看,落盘后可在应用的日志页面中回看。
控制面板不是单纯的按钮皮肤:它通过 isRunning() 和退出码获取原生运行状态,并提供 Logs、htdocs、Admin 入口。Admin 页显示 ServerRoot、Apache/MPM、监听地址、DocumentRoot、模块以及运行状态;Logs 页查看服务日志;htdocs 页用于管理站点内容。

图 3:诊断页用于快速确认 ServerRoot、127.0.0.1:8080、MPM 和服务运行状态。
六、应用沙箱里的运行时布局
首次进入主页面时,ensureRuntimeLayout() 在应用私有目录准备运行时结构:
<filesDir>/xampp/
├── conf/
│ ├── httpd.conf
│ └── mime.types
├── htdocs/
│ ├── index.html
│ └── 你创建的页面
└── logs/
├── access_log
├── error_log
└── stderr.log
初始配置使用 127.0.0.1:8080,DocumentRoot 指向应用沙箱的 htdocs。<Directory> 授权块必须与这个文档目录的绝对路径一致,否则配置中对 / 的默认拒绝规则可能生效,出现 403 Forbidden。本项目曾通过 Admin/Logs 页面和浏览器请求定位到 AH01630: client denied by server configuration,随后将 DocumentRoot 和 <Directory> 改为由 serverRoot 插值生成的绝对路径。
布局升级也需要考虑已有安装:仅修改新安装使用的默认文本还不够,旧 httpd.conf 可能继续留在沙箱。因此首次布局版本标记升级到 .layout-v2,未有此标记时重写配置文件。反过来说,项目也需注意这个默认初始化策略对用户手工改过的配置意味着什么:通过版本标记强制刷新会覆盖已有 httpd.conf,后续迭代应明确区分默认模板升级与用户自定义配置的保留策略。

图 4:htdocs 管理页把站点根目录作为应用内可操作资源,支持创建 HTML 页面。
七、真机验收:从 RUNNING 到浏览器拿到页面
一次完整验收不能只看“按钮变绿”。至少要检查以下链路:
- HAP 可以安装,应用能进入主界面,
libxapache.so被 N-API 加载。 - 点击 Start 后,原生日志出现
apache_main entering,应用仍保持运行。 - 控制面板状态进入 RUNNING,诊断页的监听地址为
127.0.0.1:8080。 - 浏览器打开
http://127.0.0.1:8080/,页面来自真实 httpd,而非 WebView 中写死的假内容。 - Logs 页可检查
error_log、access_log等服务日志。 - Stop 后服务停止,UI 状态与退出码同步更新。

图 5:日志页面汇集服务输出,配置解析错误、访问记录和启动失败都可以从这里继续排查。
这条链路里,HTTP 403 也是“服务器已启动”的有用信号,但并不代表验收完成:它说明请求到达 Apache,却仍要进一步检查目录授权和文件可读性。修正绝对路径 <Directory> 后,最终截图展示浏览器返回 XAMPP 欢迎页,才构成端到端的 HTTP 验证。
八、从这次移植中总结的排查顺序
遇到“点击 Start 没反应”或应用异常退出,不妨按边界逐层缩小问题:
1. 先确认 N-API 模块加载
检查启动日志中是否出现模块加载和 N-API exports 注册成功的信息。若 ArkTS import 阶段失败,优先比对 HAP 内 .so、动态符号和重定位,而不是先怀疑页面按钮。
2. 再检查配置解析
查看 stderr.log 和 error_log。Invalid command 'Require' 指向模块未注册;配置解析失败则可能触发 httpd 的退出路径。当前实现会把 httpd 的 exit() 转为服务线程退出,因此可以保留应用并继续查看错误。
3. 监听成功后验证 HTTP 状态码和正文
- 无法连接:检查服务线程、监听地址和端口。
- 403:检查
<Directory>的绝对路径、Require all granted及目录访问条件。 - 404:检查 DocumentRoot、文件是否实际落在沙箱
htdocs中。 - 200 但内容不对:确认响应来自本地 Apache 和当前文件,而不是浏览器缓存。
4. 始终检查最终交付物
原生库源码目录、entry/libs、Hvigor strip 中间产物和最终 HAP 是多份不同阶段的文件。构建成功日志只能证明某个任务成功,不能单独证明设备安装的包包含预期版本。用文件大小和哈希逐段核对,可避免把缓存问题误诊为运行时问题。
九、关键代码拆解:把架构落到可运行实现
前面的架构图描述的是组件关系,下面看几段真正决定运行方式的代码。示例取自工程实现并做了必要的上下文精简,便于把关键机制看清楚。
1. ArkTS 侧构造 ServerRoot 和启动参数
应用不从资源目录直接运行 Apache,而是在应用私有 filesDir 下准备可读写的运行时目录。XApache 类将沙箱根目录集中管理:
export class XApache {
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
serverRoot(): string {
return `${this.context.filesDir}/xampp`;
}
startServer(): boolean {
const root = this.serverRoot();
return xapache.startServer([
'httpd',
'-d', root,
'-f', `${root}/conf/httpd.conf`,
'-X',
'-k', 'start'
]);
}
}
这里最关键的不是参数数量,而是路径所有权:配置、文档、日志都相对于本应用的 serverRoot,不会依赖系统 /etc、系统服务目录或全局安装路径。-d 指定 ServerRoot,-f 指定配置文件,-X 让 httpd 保持单进程模式;这与传统多进程 daemon 的启动模型不同。
首次初始化的逻辑可以概括成“先保证目录存在,再按布局版本写入默认文件”:
const root = this.serverRoot();
for (const dir of [root, `${root}/conf`, `${root}/htdocs`, `${root}/logs`]) {
if (!fs.accessSync(dir)) {
fs.mkdirSync(dir, true);
}
}
const marker = `${root}/.layout-v2`;
if (!fs.accessSync(marker)) {
this.forceWrite(`${root}/conf/httpd.conf`, httpdConf(root));
this.writeIfAbsent(`${root}/conf/mime.types`, MIME_TYPES);
this.writeIfAbsent(`${root}/htdocs/index.html`, WELCOME_HTML);
this.touch(marker);
}
升级配置时使用版本标记,是为了修复已有安装中的旧配置;同时也暴露了产品化时必须处理的权衡:若用户已经编辑过 httpd.conf,自动覆盖可能丢失自定义内容。当前实现优先保证默认环境可复现,后续若扩展为用户长期使用的本地开发工具,建议将默认模板与用户配置分开,并提供显式迁移策略。
2. 关键 httpd.conf:监听、目录权限与日志
默认配置不监听所有网卡,而只绑定回环地址。文档根目录通过绝对路径注入:
ServerName 127.0.0.1:8080
Listen 127.0.0.1:8080
DocumentRoot "${root}/htdocs"
<Directory "${root}/htdocs">
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<Directory />
AllowOverride none
Require all denied
</Directory>
DirectoryIndex index.html index.htm
ErrorLog "logs/error_log"
LogLevel info
这段配置体现两个容易遗漏的细节:
<Directory>匹配的是文件系统路径,因此文档目录应当是可匹配的绝对路径;仅将DocumentRoot写成相对目录,并不能保证授权块命中。<Directory />的默认拒绝是合理的安全基线,但必须为实际站点目录配置更具体的授权规则。
用 HTTP 状态码诊断时,403 与“Apache 没启动”是两类问题。能拿到 403 响应说明请求已经进入 HTTP 服务处理链,接下来应查授权配置和目录权限,而不是继续反复重编译原生库。
3. N-API 桥接:复制参数后再创建线程
ArkTS 传入的是字符串数组。N-API 回调不能把 JS 值的临时内存地址直接留给后台线程使用,所以工程将每个参数复制到原生堆内存,再创建线程:
struct server_args {
int argc;
char **argv;
};
static void *server_thread(void *opaque)
{
struct server_args *args = (struct server_args *)opaque;
int rc = apache_main(args->argc, (const char *const *)args->argv);
g_exitCode = rc;
__atomic_store_n(&g_running, 0, __ATOMIC_SEQ_CST);
free_server_args(args);
return NULL;
}
启动入口中创建线程后立即返回 JS 调用方,避免让 UI 线程同步执行整个服务主循环:
int rc = pthread_create(&g_serverThread, NULL, server_thread, sa);
if (rc != 0) {
free_server_args(sa);
napi_throw_error(env, NULL, "pthread_create failed");
return NULL;
}
pthread_detach(g_serverThread);
__atomic_store_n(&g_running, 1, __ATOMIC_SEQ_CST);
这段代码也说明了为什么状态读取要使用原子访问:UI 线程查询 isRunning(),服务线程负责设置运行状态,二者并发读写同一状态变量。工程使用原子内建函数更新运行标志,避免简单的普通读写造成不确定行为。
4. 定点处理 exit:失败时结束服务线程,不结束应用
httpd 某些致命配置错误会走 destroy_and_exit_process(),最终调用 exit(1)。对于进程内服务器,这个动作不能原样沿用,否则配置错误可能把整个 HAP 一并结束。工程在组装静态归档时,把归档对象中对 exit 的未定义引用改名为 xapache_exit,由 N-API glue 提供实现:
void xapache_exit(int status)
{
g_exitCode = status;
g_running = 0;
pthread_exit(NULL);
}
对应的构建处理是逐个解开归档成员,只修改确实引用 exit 的对象,再重新打包:
llvm-ar-15 x "$CORE/libxapache_core.a"
for obj in *.o; do
if llvm-nm-15 --undefined-only "$obj" | grep -qE ' U exit$'; then
llvm-objcopy-15 --redefine-sym=exit=xapache_exit "$obj"
fi
done
llvm-ar-15 qc "$CORE/libxapache_core_patched.a" *.o
这里的边界非常重要:这是对 Apache 静态归档引用的定点改名,不是给整个进程或 .so 全局替换 libc 的 exit()。避免全局拦截,可以降低对 N-API、系统库和其他运行库的意外影响。服务线程因 pthread_exit() 结束后仍需确保对应资源路径妥善收尾;实际工程还将退出码和运行标志暴露给 UI 诊断。
十、构建与验证:从 WSL 到最终 HAP
1. 原生部分按依赖顺序构建
项目的脚本流水线按依赖层次拆开。下面是工程 README 中使用的典型命令(路径需按自己的 WSL 挂载位置调整):
wsl -d bajie -- bash /mnt/c/Users/24190/Desktop/harmony-pc/ohos_XAMPP/scripts/00-env-prep.sh
wsl -d bajie -- bash /mnt/c/Users/24190/Desktop/harmony-pc/ohos_XAMPP/scripts/01-toolchain-test.sh
wsl -d bajie -- bash /mnt/c/Users/24190/Desktop/harmony-pc/ohos_XAMPP/scripts/02-build-stack.sh
wsl -d bajie -- bash /mnt/c/Users/24190/Desktop/harmony-pc/ohos_XAMPP/scripts/03-build-httpd.sh
wsl -d bajie -- bash /mnt/c/Users/24190/Desktop/harmony-pc/ohos_XAMPP/scripts/04-assemble-core.sh
wsl -d bajie -- bash /mnt/c/Users/24190/Desktop/harmony-pc/ohos_XAMPP/scripts/06-fix-modules-registry.sh
wsl -d bajie -- bash /mnt/c/Users/24190/Desktop/harmony-pc/ohos_XAMPP/scripts/05-assemble-so.sh
注册表修复必须在核心归档重建之后、最终 .so 链接之前执行;否则你生成的新 modules.o 不会进入本次链接产物。脚本依赖 cross-env.sh 设置 target、sysroot、resource-dir、运行库和 linker bridge,所以直接在普通 Linux 主机上用默认 clang 替代并不等价。
2. Hvigor 负责将原生库封装进 HAP
Windows 侧的 HAP 构建大致如下:
$env:DEVECO_SDK_HOME = 'C:/Program Files/Huawei/DevEco Studio/sdk'
$env:Path = 'C:/Program Files/Huawei/DevEco Studio/jbr/bin;' + $env:Path
Set-Location 'C:/Users/24190/Desktop/harmony-pc/ohos_XAMPP/ohos_hap'
& 'C:/Program Files/Huawei/DevEco Studio/tools/node/node.exe' `
'C:/Program Files/Huawei/DevEco Studio/tools/hvigor/bin/hvigorw.js' `
assembleHap --mode module -p product=default -p buildMode=debug --no-daemon
验证时至少核对三处:
- 源库
entry/libs/arm64-v8a/libxapache.so; - Hvigor 生成的 strip 中间产物;
- 最终
entry-default-signed.hap中libs/arm64-v8a/libxapache.so。
strip 会改变文件哈希,所以不应直接要求 strip 前后哈希相同;正确做法是确认中间产物的生成时间、大小与 HAP 内库一致,再检查 strip 后的动态段和符号。曾经遇到的“应用仍像在运行旧逻辑”,根源就是 HAP 里保留了旧 .so,不是源码改动无效。
3. 设备侧验收关注结果,而不仅是构建成功
建议用一张检查表逐项验证:
[ ] 应用安装、启动,N-API 模块完成注册
[ ] 点击 Start 后出现 apache_main entering
[ ] UI 状态变为 RUNNING,应用进程仍存活
[ ] 浏览器访问 http://127.0.0.1:8080/ 返回欢迎页
[ ] htdocs 中新增 HTML 文件可通过 HTTP 访问
[ ] Logs 页面能查看 error_log / access_log
[ ] 点击 Stop 后 UI 状态同步变为 STOPPED
已完成的设备验收记录包括应用成功启动、N-API exports 注册、Apache 进入主函数、127.0.0.1:8080 监听和浏览器拿到欢迎页。后续改动仍应重新对当前 HAP 做验收,历史成功不能替代对新构建产物的验证。
十一、应用内工具页:把“排障能力”做进产品
在嵌入式服务项目里,日志不是开发阶段临时加的调试输出,而是用户判断服务状态的重要界面。当前 Logs 页面按文件读取 error_log 和 access_log 的尾部内容,并限制显示最近数据,避免把整份日志一次性读入 UI:
const candidates = [`${root}/logs/error_log`, `${root}/logs/access_log`];
for (const path of candidates) {
if (fs.accessSync(path)) {
const stat = fs.statSync(path);
const buf = new ArrayBuffer(stat.size);
const fd = fs.openSync(path, fs.OpenMode.READ_ONLY).fd;
fs.readSync(fd, buf);
fs.closeSync(fd);
const tail = new Uint8Array(buf).subarray(Math.max(0, stat.size - 8192));
const text = String.fromCharCode(...tail);
out.push(...text.split('\\n').slice(-100));
}
}
Admin 页面则提供更偏结构化的状态快照:ServerRoot、httpd 版本、MPM、监听地址、DocumentRoot、模块列表、运行状态和退出码。两类信息的用途不同:Logs 解释“刚刚发生了什么”,Admin 回答“当前配置和状态是什么”。这类诊断页面对没有 shell 权限访问应用私有目录的场景尤其有价值。
htdocs 管理页直接操作 filesDir/xampp/htdocs,当前提供文件/目录浏览和新建 HTML 的入口。最简单的端到端自测方式是新建页面后,从浏览器访问对应 URL;这样同时验证了文件写入、DocumentRoot 映射、Apache 访问控制和 HTTP 响应。
十二、故障现象与定位路径
应用启动后立刻退出
检查 N-API 模块是否完成加载,再核对 .so 中是否存在未定义模块符号、绝对重定位或 TEXTREL。本工程的典型案例是注册表引用了并未构建出来的 mime_magic_module,导致原生库加载阶段失败。结论是:先证明动态库可加载,再查服务线程,不要把两个阶段混在一起。
配置报 Invalid command 'Require'
Require 由 authz_core 模块提供。若模块对象静态链接却没有进入 Apache 的预链接注册表,httpd 仍然无法识别指令。应同时核对模块静态库是否存在、是否进入核心归档、模块符号是否已定义、ap_prelinked_modules[] 是否登记。
启动配置错误造成 SIGABRT
查看 stderr.log。httpd 某些错误处理路径会调用 exit(1),OpenHarmony appspawn 对应用线程的进程级退出有约束。检查 05-assemble-so.sh 是否对 Apache 归档内引用执行定点改名,以及 glue 中是否由 xapache_exit() 只结束服务线程;不要用全局 exit() override 粗暴替代。
服务器能响应但返回 403
请求已到达服务器,重点检查 AH01630、<Directory> 的绝对路径及 Require all granted 是否匹配当前 DocumentRoot。还要确认访问的是 127.0.0.1:8080 对应的这一个 httpd 实例,而不是另一服务。
源码更新但设备行为没变
从 HAP 中提取或检查最终打包的 .so,与中间产物逐段核对。Hvigor 的缓存目录可能导致 strip 产物未刷新;只检查源文件哈希是不够的。
十三、常见问题 FAQ
Q1:这是不是完整的 XAMPP?MariaDB 和 PHP 能用吗?
当前不是完整的 XAMPP 套件。本阶段已移植并验证 Apache httpd 2.4.69;MariaDB、PHP 仍是后续规划,控制面板上的卡片只是阶段占位,不代表相关服务已经可用。
Q2:为什么不直接把 httpd 可执行文件放进 HAP 再启动?
本项目面向的是 OpenHarmony PC 的 HAP 运行环境,不能简单套用桌面 Linux 的 fork/exec 和 daemon 生命周期模型。当前方案将 httpd 静态链接到 N-API 原生模块中,通过 pthread 在应用进程内运行,避免依赖独立可执行文件启动。
Q3:服务启动后从哪里访问?其他设备能访问吗?
默认监听 127.0.0.1:8080,在本机浏览器中访问 http://127.0.0.1:8080/。由于绑定的是 loopback 回环地址,默认不面向局域网其他设备开放;不要把它当作公网或生产 Web 服务配置。
Q4:浏览器返回 403,是不是 Apache 没启动?
不一定。收到 HTTP 403 通常说明请求已到达服务器,但访问授权没有通过。先查看 logs/error_log 中是否有 AH01630,再确认 DocumentRoot 与 <Directory> 使用相同的绝对路径,并包含适当的 Require all granted 规则。若无法建立连接,才优先检查服务线程和监听端口。
Q5:报 Invalid command 'Require' 应该检查什么?
Require 指令由 authz_core 模块提供。静态链接模块对象并不自动等于 Apache 已注册该模块;需要确认模块归档确实存在、对象进入最终核心归档,并且 modules.c 的 ap_prelinked_modules[]、ap_prelinked_module_symbols[] 等表登记了该模块。
Q6:配置错误为什么可能让整个应用崩溃?
httpd 的部分致命错误路径会调用 exit(1)。在进程内嵌入场景中,若直接按原语义执行,可能影响宿主应用。本项目对 Apache 静态归档里引用的 exit 做定点符号改名,由 xapache_exit() 记录退出码并终止服务线程。该处理不是全局覆盖 libc 的 exit()。
Q7:为什么改了 .so,设备运行行为还是旧的?
最终运行的是 HAP 里打包的原生库,而不是工作区中的源文件。请依次检查 entry/libs/arm64-v8a/libxapache.so、Hvigor 的 stripped_native_libs 中间产物和最终 HAP 内的 .so。strip 会改变文件哈希,应结合大小、时间和动态段检查确认产物链,而不能只看源库是否更新。
Q8:应用沙箱中的网页、配置和日志在哪里?
运行时根目录为应用 filesDir/xampp,主要包括 conf/httpd.conf、conf/mime.types、htdocs/ 和 logs/。具体绝对路径由当前设备分配给应用的 filesDir 决定,可通过应用 Admin 页面查看 ServerRoot;使用应用内 Logs 和 htdocs 页面访问这些沙箱内容。
Q9:如何快速验证自己创建的 HTML 页面?
在 htdocs 管理页创建或放入 HTML 文件,然后访问 http://127.0.0.1:8080/文件名.html。若首页正常但新页面 404,检查文件是否位于当前 ServerRoot 下的 htdocs,并核对 URL 路径和文件名大小写。
Q10:项目可以直接用于生产环境吗?
不建议。当前项目定位是 OpenHarmony PC 上的移植验证和本地开发服务;默认只绑定回环地址,也未完成 MariaDB/PHP 等组件集成及生产部署所需的安全、运维和生命周期设计。
十四、目前完成了什么,哪些仍未完成
本阶段已经完成的能力:
- Apache httpd 2.4.69 在 OpenHarmony PC HAP 内运行;
- N-API 方式控制服务线程启动/停止;
- 本地
127.0.0.1:8080监听与欢迎页访问; - 应用沙箱内的 conf、htdocs 和日志布局;
- 控制面板、日志、站点文件和诊断页面。
尚未完成或不应过度宣称的内容:
- MariaDB 和 PHP 仍是规划中的 Phase 2;
- 当前监听地址为回环地址,不是面向局域网的部署配置;
- 不应把该开发环境直接当作生产服务器使用;
- 最终 HAP/设备状态会随工程后续修改变化,复现时应以当前构建和设备验证结果为准。
十五、结语:移植的重点是尊重平台运行模型
这次把 XAMPP 搬到 OpenHarmony PC,最核心的工作不是重画控制面板,而是重新安排 Apache 的运行边界:把进程式服务器变成可静态链接的原生模块,把服务生命周期交给应用线程,把配置与站点内容收进沙箱,再把 Apache 的模块注册、符号重定位和错误退出路径逐一适配。
最终形成的方案并非“在鸿蒙里照搬 Linux 服务管理”,而是保留 Apache 的协议与模块能力,同时采用 HAP 能稳定承载的进程内运行方式。对类似的 C/C++ 服务端移植项目,这个经验可以概括成一句话:先确认平台允许怎样运行,再决定如何包装上游软件。
更多推荐


所有评论(0)