Skip to content

Commit f281677

Browse files
committed
Add HNP build support for HarmonyOS
- Add pthread_compat.h for musl libc compatibility - Fix _Atomic initialization in threads_mngr.c - Fix program_invocation_name usage in setproctitle.c for musl libc - Add build_hnp.sh script for cross-compilation - Add HNP_BUILD_GUIDE.md and CHANGES.md documentation - Add HNP example project
1 parent 60ba86f commit f281677

14 files changed

Lines changed: 685 additions & 4 deletions

File tree

BLOG_CN.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# 把 Redis 搬到鸿蒙 PC 上,我踩了这些坑
2+
3+
最近在折腾鸿蒙 PC 开发,突发奇想:能不能把 Redis 也搞上去?说干就干,结果踩了一堆坑,写下来给后来人避避雷。
4+
5+
## 为啥要干这事?
6+
7+
其实没啥高大上的理由,就是好奇。鸿蒙 PC 刚出来,生态还比较荒,想看看能不能把常用的开源软件跑起来。Redis 作为内存数据库,用的人多,如果能移植成功,应该挺有用的。
8+
9+
而且我一直觉得跨平台移植挺有意思的,每次看到代码在另一个平台上跑起来,都有种"我做到了"的感觉。
10+
11+
## 第一步:装 DevEco Studio 和 OpenHarmony SDK
12+
13+
说干就干,第一步当然是准备环境。鸿蒙开发需要 DevEco Studio 和 OpenHarmony SDK。
14+
15+
我直接去官网下了 DevEco Studio,安装过程倒是挺顺利的。装完后打开,发现需要配置 SDK。默认路径是:
16+
17+
```
18+
/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony
19+
```
20+
21+
我一开始还以为 SDK 会自动下载,结果发现得手动配置。在 DevEco Studio 的设置里找到 SDK 配置,选好路径,然后等它下载。这个过程有点慢,我趁机去泡了杯咖啡。
22+
23+
SDK 下完后,我检查了一下关键工具:
24+
25+
- `native/llvm/bin/aarch64-unknown-linux-ohos-clang` - 编译器
26+
- `native/llvm/bin/llvm-ar` - 归档工具
27+
- `native/sysroot` - 系统根目录
28+
- `toolchains/hnpcli` - HNP 打包工具
29+
30+
确认这些文件都在,我才放心继续。如果这些工具找不到,后面编译肯定要出问题。
31+
32+
顺便说一句,如果你的 SDK 路径不一样,记得改一下脚本里的路径。我一开始路径写错了,编译的时候报错说找不到编译器,我还以为是 SDK 没装好,结果是自己路径写错了,白折腾了半天。
33+
34+
## 第一次编译:想得太简单了
35+
36+
刚开始我以为改个编译器路径就完事了,结果被现实狠狠打脸。
37+
38+
第一个错误就让我傻眼了:
39+
40+
```
41+
undefined reference to `pthread_setcancelstate'
42+
```
43+
44+
我:???这啥玩意儿?
45+
46+
查了半天才知道,musl libc 根本不支持 pthread 的取消功能。我当时就懵了,这怎么搞?Redis 的代码里用到了这个函数,但目标平台不支持。
47+
48+
说实话,那一刻我有点想放弃。但转念一想,来都来了,再试试吧。
49+
50+
## 查资料查到怀疑人生
51+
52+
为了解决这个问题,我开始疯狂查资料。Stack Overflow、GitHub Issues、各种技术博客翻了个遍。
53+
54+
发现 musl libc 和 glibc 的差异比我想象的大。musl 追求简洁,很多 GNU 扩展都不支持。比如 `program_invocation_name` 这个变量,glibc 里有,musl 里就没有。
55+
56+
Redis 的代码里用这个变量来设置进程标题,我只好用条件编译绕过去:
57+
58+
```c
59+
#if defined(__GLIBC__)
60+
program_invocation_name = tmp;
61+
#endif
62+
```
63+
64+
虽然有点丑,但能跑就行。
65+
66+
## _Atomic 初始化:最坑的一个
67+
68+
这个问题真的把我搞崩溃了。
69+
70+
Redis 代码里有这么一行:
71+
72+
```c
73+
static redisAtomic run_on_thread_cb g_callback = NULL;
74+
```
75+
76+
编译报错:`initializer element is not a compile-time constant`
77+
78+
我:???NULL 不是常量吗?
79+
80+
查了 C11 标准才知道,`_Atomic` 类型不能用这种方式初始化。我试了好几种方法都不行,最后发现只能在运行时初始化:
81+
82+
```c
83+
static redisAtomic run_on_thread_cb g_callback;
84+
85+
void ThreadsManager_init(void) {
86+
atomicSet(g_callback, NULL);
87+
}
88+
```
89+
90+
就这么一个小改动,我折腾了大半天。C 语言有时候真的让人抓狂。
91+
92+
## 写兼容层:没办法的办法
93+
94+
既然 musl 不支持 pthread 取消,那我就自己写个假的。
95+
96+
创建了 `pthread_compat.h`,里面就是几个空函数:
97+
98+
```c
99+
static inline int pthread_cancel(pthread_t thread) {
100+
(void)thread;
101+
return 0; // 假装成功了
102+
}
103+
```
104+
105+
虽然这些函数啥都不干,但至少能让代码编译通过。我知道这不是最优解,但现阶段够用了。Redis 在大多数场景下也用不到线程取消,所以问题不大。
106+
107+
## 写编译脚本:终于有点样子了
108+
109+
手动编译了几次后,我意识到得写个脚本,不然每次都要敲一堆命令,太麻烦了。
110+
111+
`build_hnp.sh` 的时候,我一边写一边改,试了好几次才把参数调对。特别是那些编译标志,每个都是试出来的:
112+
113+
```bash
114+
export CFLAGS="--target=aarch64-unknown-linux-ohos --sysroot=$OHOS_SYSROOT -D__MUSL__ -DNO_PTHREAD_CANCEL"
115+
```
116+
117+
`-DNO_PTHREAD_CANCEL` 这个宏是我自己加的,用来启用兼容层。看起来简单,但找到这个方法花了不少时间。
118+
119+
## 终于编译成功了
120+
121+
当看到 `redis.hnp` 文件生成的时候,我差点从椅子上跳起来。
122+
123+
虽然只是第一步(还没在真机上测试),但至少证明这条路能走通。文件大小 4.4MB,比原版大一点,但考虑到包含了所有依赖,还算可以接受。
124+
125+
不过说实话,我现在也不知道在真机上能不能跑起来。可能还有一堆运行时问题等着我。
126+
127+
## 一些乱七八糟的想法
128+
129+
这次折腾让我明白了几件事:
130+
131+
1. **平台差异真的烦人**。glibc 和 musl libc 看起来差不多,用起来差别还挺大。每次遇到这种问题,我都想骂人,但骂完还得继续搞。
132+
133+
2. **兼容性代码虽然丑,但有用**。那些 `#ifdef` 满天飞的代码确实不好看,但能让程序在不同平台上跑,这就是它的价值。
134+
135+
3. **查资料是个技术活**。有时候一个问题要翻几十个网页才能找到答案,而且很多答案都是错的或者过时的。这种时候真的考验耐心。
136+
137+
4. **开源社区真香**。遇到问题的时候,看看别人是怎么解决的,能少走很多弯路。虽然这次没直接抄代码,但思路都是参考别人的。
138+
139+
## 接下来要干啥
140+
141+
虽然编译成功了,但我知道这还远远不够:
142+
143+
- 得在真机上跑一下,看看有没有运行时错误
144+
- 性能怎么样,会不会比 Linux 版本慢很多
145+
- TLS 和模块功能现在都禁用了,以后可能得加回来
146+
- 文档也得写,不然别人用起来不方便
147+
148+
不过现在先这样吧,至少能编译出来了。剩下的问题慢慢解决。
149+
150+
## 最后
151+
152+
这次折腾花了不少时间,但还挺有意思的。虽然过程中各种抓狂,但最后看到成果的时候,还是有点成就感的。
153+
154+
代码已经扔到 GitHub 上了,虽然还有很多不完善的地方,但至少能用了。如果有人也想在鸿蒙 PC 上跑 Redis,可以参考一下。
155+
156+
当然,如果遇到问题,欢迎提 Issue。如果觉得哪里可以改进,也欢迎提 PR。
157+
158+
---
159+
160+
项目地址:https://github.com/ohosvscode/redis
161+
162+
如果这篇文章对你有帮助,或者你也踩过类似的坑,欢迎在评论区聊聊。

CHANGES.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Redis HNP 编译修改说明
2+
3+
本文档记录了为支持 HarmonyOS Native Package (HNP) 编译而对 Redis 源代码所做的修改。
4+
5+
## 修改的文件
6+
7+
### 1. 新增文件
8+
9+
- **`src/pthread_compat.h`** - pthread 兼容层,为 musl libc 提供线程取消功能的空实现
10+
- **`build_hnp.sh`** - Redis HNP 编译脚本
11+
- **`HNP_BUILD_GUIDE.md`** - HNP 编译指南文档
12+
- **`examples/hnp_example/`** - HNP 编译示例项目
13+
14+
### 2. 修改的文件
15+
16+
#### `src/threads_mngr.c`
17+
- **问题**: `_Atomic` 类型不能直接用 `NULL` 进行静态初始化
18+
- **修改**: 将静态初始化改为运行时初始化,在 `ThreadsManager_init()` 函数中初始化原子变量
19+
20+
#### `src/setproctitle.c`
21+
- **问题**: `program_invocation_name` 是 GNU libc 扩展,在 musl libc 中不可用
22+
- **修改**: 添加 `__GLIBC__` 条件编译,只在 GNU libc 环境下使用这些变量
23+
24+
#### `src/server.h`
25+
- **修改**: 添加 `#include "pthread_compat.h"` 以包含 pthread 兼容层
26+
27+
#### `src/server.c`
28+
- **修改**: 添加 `#include "pthread_compat.h"`(通过 server.h 间接包含)
29+
30+
## 查看差异
31+
32+
使用以下命令查看与上游的差异:
33+
34+
```bash
35+
# 查看所有修改的文件
36+
git diff 8.4.0 --name-only
37+
38+
# 查看源代码修改的详细差异
39+
git diff 8.4.0 src/pthread_compat.h src/threads_mngr.c src/setproctitle.c src/server.h
40+
41+
# 查看统计信息
42+
git diff 8.4.0 --shortstat
43+
```
44+
45+
## 修改原因
46+
47+
这些修改是为了使 Redis 能够在 OpenHarmony (musl libc) 环境下编译:
48+
49+
1. **musl libc 兼容性**: musl libc 不支持某些 GNU libc 扩展功能
50+
2. **线程取消**: musl libc 不支持 pthread 取消功能,需要提供兼容实现
51+
3. **原子变量初始化**: C11 `_Atomic` 类型的初始化限制
52+
53+
## 兼容性说明
54+
55+
- ✅ 这些修改不影响在标准 Linux (glibc) 环境下的编译
56+
- ✅ 修改使用条件编译,只在需要时生效
57+
- ✅ 不影响 Redis 的核心功能
58+

HNP_BUILD_GUIDE.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Redis HNP 编译指南
2+
3+
本指南说明如何将 Redis 编译为 HarmonyOS Native Package (HNP) 格式。
4+
5+
## 文件结构
6+
7+
```
8+
redis-8.4.0/
9+
├── build_hnp.sh # Redis HNP 编译脚本(主脚本)
10+
├── redis.hnp # 编译生成的 Redis HNP 包
11+
├── examples/
12+
│ └── hnp_example/ # HNP 编译示例项目
13+
│ ├── main.c # 示例源代码
14+
│ ├── build.sh # 示例编译脚本
15+
│ ├── CMakeLists.txt # CMake 配置
16+
│ └── my_hnp_project.hnp # 示例 HNP 包
17+
├── src/
18+
│ ├── pthread_compat.h # pthread 兼容层(musl libc 支持)
19+
│ ├── threads_mngr.c # 已修改(修复 _Atomic 初始化)
20+
│ ├── setproctitle.c # 已修改(musl libc 兼容)
21+
│ └── server.h # 已修改(包含 pthread_compat.h)
22+
└── hnp_package/ # 打包临时目录
23+
├── redis-server
24+
├── redis-cli
25+
└── redis.conf
26+
```
27+
28+
## 快速开始
29+
30+
### 编译 Redis HNP
31+
32+
```bash
33+
cd /Users/naily/Documents/redis-8.4.0
34+
./build_hnp.sh
35+
```
36+
37+
编译完成后会生成 `redis.hnp` 文件。
38+
39+
### 运行示例项目
40+
41+
```bash
42+
cd examples/hnp_example
43+
./build.sh
44+
```
45+
46+
## 编译要求
47+
48+
1. **OpenHarmony SDK**: 位于 `/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony`
49+
2. **编译器**: OpenHarmony SDK 自带的 LLVM/Clang 工具链
50+
3. **打包工具**: `hnpcli` (包含在 SDK 中)
51+
52+
## 修改说明
53+
54+
为了在 OpenHarmony (musl libc) 环境下编译,对以下文件进行了修改:
55+
56+
1. **src/pthread_compat.h** (新建)
57+
- 提供 pthread 取消功能的兼容实现
58+
- musl libc 不支持线程取消,提供空实现
59+
60+
2. **src/threads_mngr.c**
61+
- 修复 `_Atomic` 类型的初始化问题
62+
- 将静态初始化改为运行时初始化
63+
64+
3. **src/setproctitle.c**
65+
- 修复 `program_invocation_name` 问题
66+
- 添加 `__GLIBC__` 条件编译
67+
68+
4. **src/server.h**
69+
- 包含 `pthread_compat.h` 头文件
70+
71+
## 编译选项
72+
73+
- `BUILD_TLS=no` - 禁用 TLS 支持
74+
- `BUILD_WITH_MODULES=no` - 禁用模块支持
75+
- `MALLOC=libc` - 使用 libc 内存分配器
76+
- `DISABLE_WERRORS=yes` - 禁用将警告视为错误
77+
78+
## 输出文件
79+
80+
- `redis.hnp` - Redis HNP 包(约 4.4MB)
81+
- `src/redis-server` - ARM64 ELF 可执行文件(约 9.3MB)
82+
- `src/redis-cli` - Redis 客户端(约 1.2MB)
83+
84+
## 注意事项
85+
86+
1. **架构兼容性**: 当前编译的是 ARM64 (aarch64) 架构
87+
2. **系统要求**: 需要在 HarmonyOS 设备上运行
88+
3. **功能限制**: TLS 和模块功能已禁用
89+
4. **测试**: 建议在 HarmonyOS 设备上测试运行
90+
91+
## 相关资源
92+
93+
- [OpenHarmony 官方文档](https://gitee.com/openharmony/docs)
94+
- [HarmonyOS 开发者文档](https://developer.harmonyos.com/)
95+

0 commit comments

Comments
 (0)