上次编译了 MongoDB 的 Embedded 版本,不过官方表示那个还只是一个实验中的程序,同时上次编译的还是 32-bit 的 Embedded 版本,因此这次就来编译一个真正可用的 64-bit 的 MongoDB 好了_(:3」∠)_
总的来说倒也没有想象中那么复杂,但是确实有几个可能踩到的的坑
首先我做的就是先让树莓派到 64-bit 环境中,可以参考我的这篇博客,让 Raspberry Pi 4 完全运行在 64-bit 模式下。
确认 schroot
到 64-bit 环境中之后,同样的,通过 apt
安装必要的依赖
sudo apt install -y scons libssl-dev libffi-dev libcurl4-openssl-dev wget gcc g++ vim cmake python3 python3-pip
包括稍后会用到的 Python 的依赖
cat << EOF >~/build-requirements.txt Cheetah3 # src/mongo/base/generate_error_codes.py psutil pymongo >= 3.0, != 3.6.0 # See PYTHON-1434, SERVER-34820 PyYAML >= 3.0.0 regex requests >= 2.0.0 typing >= 3.6.4 EOF sudo pip3 install -r ~/build-requirements.txt
在国内的话,可以指定使用清华大学 TUNA 镜像源
sudo pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r ~/build-requirements.txt
接着是编译 snappy ~
snappy 的源代码在其 GitHub Release 页面下载,https://github.com/google/snappy/releases,在写本文的时候,snappy 最新的 release 是在 17 年 8 月 25 号发布的 1.1.7,于是下载地址就是 https://github.com/google/snappy/archive/1.1.7.tar.gz
export SNAPPY_VER=1.1.7 wget https://github.com/google/snappy/archive/\${SNAPPY_VER}.tar.gz -O snappy-\${SNAPPY_VER}.tar.gz tar xzf snappy-\${SNAPPY_VER}.tar.gz cd snappy-\${SNAPPY_VER} mkdir build && cd build cmake -DBUILD_SHARED_LIBS=ON .. && make -j`nproc` && sudo make install
下一个则是 Mongo C Driver ♪(´ε` ) Mongo C Driver 的源代码也是在它的 GitHub Release 页面上,https://github.com/mongodb/mongo-c-driver/releases,在写这篇 post 的时候最新版本是 19 年 11 月 7 号发布的 1.15.2,下载链接则是 https://github.com/mongodb/mongo-c-driver/releases/download/1.15.2/mongo-c-driver-1.15.2.tar.gz
export MONGO_C_VER=1.15.2 wget https://github.com/mongodb/mongo-c-driver/releases/download/\${MONGO_C_VER}/mongo-c-driver-\${MONGO_C_VER}.tar.gz -O mongo-c-driver-\${MONGO_C_VER}.tar.gz tar xzf mongo-c-driver-\${MONGO_C_VER}.tar.gz cd mongo-c-driver-\${MONGO_C_VER} mkdir release && cd release cmake .. && make -j`nproc` && sudo make install
现在就是到了重头戏的地方了~编译 MongoDB~
MongoDB 的源代码则是在它的官网上下载,https://www.mongodb.com/download-center/community。在页面右侧会有一个 Download Source (tgz)
,下载到 Raspberry Pi 上即可~
截止这篇 post 写作的时候,MongoDB 在其官网上最新的版本是 4.2.1~于是
export MONGODB_VER=4.2.1
wget https://fastdl.mongodb.org/src/mongodb-src-r\${MONGODB_VER}.tar.gz -O mongodb-src-r${MONGODB_VER}.tar.gz
tar xzf mongodb-src-r\${MONGODB_VER}.tar.gz
cd mongodb-src-r\${MONGODB_VER}
在先前那篇编译 MongoDB 的 Embedded 版本中,我误以为 MongoDB 有一处 type-casting 的问题,其实因为当时是 32-bit 的环境,所以会出现问题。
但我们这里编译的命令确实需要做出一定的调整,不然编译到后面会出现这样的报错
虽然不是很清楚为什么 scons
找不到 core
这个编译依赖,但是我们编译 MongoDB Server 可以不用它
我们的编译命令需要改为如下
CCFLAGS="-mabi=lp64 -march=armv8-a+crc+simd" \ python3 buildscripts/scons.py mongod \ --disable-warnings-as-errors \ --release=RELEASE \ --dbg=off
但是!!!只是改了这里还不行,为什么呢?
因为这里的坑是 crc
,我其实一开始第一行写的是 CCFLAGS="-mabi=lp64 -march=native"
,但是编译到 wiredtiger
的 crc32-arm64
部分时,编译器突然报错说
Compiling build/opt/third_party/wiredtiger/src/checksum/arm64/crc32-arm64.o /tmp/ccHQNUYS.s: Assembler messages: /tmp/ccHQNUYS.s:40: Error: selected processor does not support `crc32cb w2,w2,w3' /tmp/ccHQNUYS.s:70: Error: selected processor does not support `crc32cb w2,w2,x4' /tmp/ccHQNUYS.s:95: Error: selected processor does not support `crc32cb w2,w2,w0' scons: *** [build/opt/third_party/wiredtiger/src/checksum/arm64/crc32-arm64.o] Error 1 scons: building terminated because of errors.
然而我记得 Raspberry Pi 4 的 CPU 肯定是支持硬件 CRC 的,于是总之先找了一段使用硬件 crc32 的代码测试,代码来自 linux kernel arch/arm64/crypto/crc32-arm64.c
#include <stdlib.h> #include <stdint.h> #define CRC32X(crc, value) __asm__("crc32x %w[c], %w[c], %x[v]":[c]"+r"(crc):[v]"r"(value)) #define CRC32W(crc, value) __asm__("crc32w %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value)) #define CRC32H(crc, value) __asm__("crc32h %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value)) #define CRC32B(crc, value) __asm__("crc32b %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value)) #define CRC32CX(crc, value) __asm__("crc32cx %w[c], %w[c], %x[v]":[c]"+r"(crc):[v]"r"(value)) #define CRC32CW(crc, value) __asm__("crc32cw %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value)) #define CRC32CH(crc, value) __asm__("crc32ch %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value)) #define CRC32CB(crc, value) __asm__("crc32cb %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value)) uint32_t crc32_arm64_le_hw(uint32_t crc, const uint8_t *p, unsigned int len) { int64_t length = len; while ((length -= sizeof(uint64_t)) >= 0) { CRC32X(crc, *((uint64_t *)p)); p += sizeof(uint64_t); } if (length & sizeof(uint32_t)) { CRC32W(crc, *((uint32_t *)p)); p += sizeof(uint32_t); } if (length & sizeof(uint16_t)) { CRC32H(crc, *((uint16_t *)p)); p += sizeof(uint16_t); } if (length & sizeof(uint8_t)) CRC32B(crc, *p); return crc; } uint32_t crc32c_arm64_le_hw(uint32_t crc, const uint8_t *p, unsigned int len) { int64_t length = len; while ((length -= sizeof(uint64_t)) >= 0) { CRC32CX(crc, *((uint64_t *)p)); p += sizeof(uint64_t); } if (length & sizeof(uint32_t)) { CRC32CW(crc, *((uint32_t *)p)); p += sizeof(uint32_t); } if (length & sizeof(uint16_t)) { CRC32CH(crc, *((uint16_t *)p)); p += sizeof(uint16_t); } if (length & sizeof(uint8_t)) CRC32CB(crc, *p); return crc; }
然后用如下命令在 Raspberry Pi 4 上编译~
gcc -mabi=lp64 -march=armv8-a+crc+simd -c crc32-arm64.c -o crc32-arm64.o
发现确实是没问题的,Raspberry Pi 4 的 CPU 支持硬件 CRC,也就是说,我们需要更改的是 wiredtiger 的相关文件~
这里因为我们是用 scons
编译的整个项目,因此我们要改的文件就是 wiredtiger
下面 scons
的配置~
wiredtiger
的源代码在 src/third_party/wiredtiger/
下,其实那下面有两个 scons
的配置文件,一个是 SConscript
,另一个是 Sconstruct
。 Sconstruct
是给 Windows 平台用的,所以我们忽略它就好
打开 Sconscript
,之后,我们需要在对应编译环境判断语句后增加 -march=armv8-a+crc+simd
这个参数~
那么阅读这个编译脚本之后,我们添加的位置如下~(对应 r4.2.1 所附带的 wiredtiger 的配置文件在 76 行
# ... elif env.TargetOSIs('linux'): if env.TargetOSIs('android'): env.Append(CPPPATH=["build_android"]) env.Append(CPPDEFINES=["_GNU_SOURCE"]) else: env.Append(CPPPATH=["build_linux"]) env.Append(CPPDEFINES=["_GNU_SOURCE"]) env.Append(CFLAGS=["-march=armv8-a+crc+simd"]) else: # ...
其实另一个方法就是不用硬件 CRC,但是那样可能会损失一定的性能,再说反正就只增加一行代码就可以用硬件 CRC 的话,并不算麻烦2333333
在修改好这个文件之后,回到 mongodb-src-r4.2.1
目录,在正式开始前还有最后一个坑,就是 MongoDB 编译的时候需要的内存还比较大,哪怕我买的已经是 4GB 的 Raspberry Pi,还是出现过 Out-of-Memory 的情况
因此可以接个读写速度相对较快的 U 盘到 Raspberry Pi 上,确保 U 盘上有足够的空间,大约需要 8G 以上的剩余空间(如果你的是 1GB 的树莓派的话,可以酌情考虑使用 12G 的 swap 文件,把下文里命令对应的 8G 改成 12G 即可),我们会将那个 U 盘拿来做 swap
给 Raspberry Pi 增加 swap 的方法也很简单~
首先,在没有接 U 盘之前执行
ls /dev/sd*
因为 Raspberry Pi 会给外接的储存依次分配 /dev/sda
,/dev/sdb
等这样的路径,记录下上面的输出~
下一步则是接上 U 盘,然后同样执行 ls /dev/sd*
,观察多出来的部分
比如我这里多出来的是 /dev/sda
,/dev/sda1
和/dev/sda2
,因为我的 U 盘上分了 2 个区,所以会有 /dev/sda1
和/dev/sda2
。
我这里 U 盘上第二个分区是 Extfs 4 的格式,所以我就用它来放 swap 文件了~
mkdir -p /tmp/sd sudo mount /dev/sda2 /tmp/sd sudo fallocate -l 8G /tmp/sd/swapfile sudo chmod 400 /tmp/sd/swapfile sudo mkswap /tmp/sd/swapfile sudo swapon /tmp/sd/swapfile
这样子就添加好了 8G 的 swap 空间~
现在一切准备好之后,就可以使用前面提到的命令开始编译了~
CCFLAGS="-mabi=lp64 -march=armv8-a+crc+simd" \ python3 buildscripts/scons.py mongod \ --disable-warnings-as-errors \ --release=RELEASE \ --dbg=off
整个过程大约 5-7 个小时,我忘了给它计时2333333
这个时候一来因为我们修改好了 wiredtiger,所以在编译 crc32-arm64.c
的时候不会再报错了
我编译完成的时候已经是晚上了,但是总算是编译完了♪(´ε` ) 而且理论上所有功能都可以正常使用~
编译好的 mongod
在 build/opt/mongo/mongod
要测试一下的话,可以使用如下命令~
cd build/opt/mongo/ rm -rf ~/mongodb mkdir -p ~/mongodb ./mongod --dbpath=${HOME}/mongodb --bind_ip=0.0.0.0
然后就能看到 MongoDB Server 正常运行了~顺便我还从 Mac 上连过去了一下,也是没问题的~
Raspberry Pi 4 那边 MongoDB Server 收到连接的 log 如下~
你的crc32-arm64.c代码中的& 应该是&符号,可能被你的博客编辑器识别错了
啊,谢谢提醒🥲 可能是之前编辑之后被博客编辑器重新转义了一次……
博主,在编译mongodb-src的过程中出现这个
scons: done reading SConscript files.
scons: Building targets ...
Compiling build/opt/third_party/icu4c-57.1/source/common/loadednormalizer2impl.o
Compiling build/opt/third_party/mozjs-60/extract/mozglue/misc/Printf.o
cc1plus: fatal error: js-confdefs.h: No such file or directory
请问这个js-confdefs.h文件是在哪的?C的知识有点忘光了。。。