From 1e0c20812454ae1c7e1cf83f953797ee042e831f Mon Sep 17 00:00:00 2001 From: We-unite <3205135446@qq.com> Date: Tue, 3 Sep 2024 22:19:09 +0800 Subject: Add poetry in shenzhen and godo notes --- code/ohos/lcm_compile.html | 119 ------------ code/ohos/lcm_compile.md | 141 -------------- code/ohos/ohos_compile.html | 332 ------------------------------- code/ohos/ohos_compile.md | 420 ---------------------------------------- code/projects/godo.html | 362 ++++++++++++++++++++++++++++++++++ code/projects/godo.md | 318 ++++++++++++++++++++++++++++++ code/projects/lcm_compile.html | 119 ++++++++++++ code/projects/lcm_compile.md | 141 ++++++++++++++ code/projects/ohos_compile.html | 332 +++++++++++++++++++++++++++++++ code/projects/ohos_compile.md | 420 ++++++++++++++++++++++++++++++++++++++++ common/CSS/highlight.css | 10 +- common/script4works.html | 7 +- common/script4works.js | 7 +- works/poetry.html | 21 +- 14 files changed, 1726 insertions(+), 1023 deletions(-) delete mode 100644 code/ohos/lcm_compile.html delete mode 100644 code/ohos/lcm_compile.md delete mode 100644 code/ohos/ohos_compile.html delete mode 100644 code/ohos/ohos_compile.md create mode 100644 code/projects/godo.html create mode 100644 code/projects/godo.md create mode 100644 code/projects/lcm_compile.html create mode 100644 code/projects/lcm_compile.md create mode 100644 code/projects/ohos_compile.html create mode 100644 code/projects/ohos_compile.md diff --git a/code/ohos/lcm_compile.html b/code/ohos/lcm_compile.html deleted file mode 100644 index dbc8109..0000000 --- a/code/ohos/lcm_compile.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - - - LCM 交叉编译 - - - - - - -
-
-

LCM 交叉编译

- -

LCM 简介

-

LCM(Lightweight Communications and - Marshalling)是一种轻量级的通信和编组库,是一种针对高带宽、低延迟、实时性要求高的场景下的通讯工具,用于在多个进程之间传递消息。LCM 的设计目标是提供一种简单的方法来传递结构化数据,而不需要复杂的 - API 或协议定义。LCM 的消息传递是基于发布/订阅模型的,发布者和订阅者之间通过一个中心化的消息传递系统进行通信。

-

在机器人和自动驾驶系统中,LCM 可作为 ROS 的替代品,借以完成进程间、设备间的通讯。

-

为了方便地在不同进程、不同设备间传递数据,我们在 OHOS 系统中集成了 LCM 模块。

-

参考文档:

- -

依赖项安装

-
sudo apt update
-sudo apt upgrade
-sudo apt install build-essential cmake libglib2.0-dev
-sudo apt install openjdk-8-jdk # lcm仅支持jdk8
-

x86 的 lcm

-

如果我们仅仅需要 x86 架构的 lcm,执行下列命令即可:

-
git clone https://github.com/lcm-proj/lcm
-cd lcm
-mkdir build && cd build
-cmake ..
-make
-sudo make install
-

编译完成后,注意观察build目录下是否有lcm-java文件夹,如果没有,证明没有安装 java 或者 java 版本不对。

-

lcm-python

-

若想使用lcm-python,在lcm/lcm-python下执行sudo python3 setup.py install即可,使用时import python。 -

-

lcm-spy 的使用

-

lcm-spy 是 LCM 配套的数据可视化工具。 可以通过 lcm-spy 监视 lcm 数据发送频率、数据量、数据结构,甚至实时画图。 如果安装了 JAVA,则 lcm-spy 会连同 LCM 一同被安装。 -

-

启动 lcm-spy:

-
export CLASSPATH=${your lcm-type class path}
-lcm-spy
-

交叉编译

-

既然 lcm 用的是 cmake,何不交叉编译到 OHOS 里用呢?

-

首先,我们可以设置一下 Native Development Kit(NDK)的路径:

-
# 设置成自己的NDK工具目录
-export OHOS_ROOT=~/app/native
-
$OHOS_ROOT/build-tools/cmake/bin/cmake \
-      -DOHOS_STL=c++_shared \
-      -DOHOS_ARCH=armv8-a \
-      -DOHOS_PLATFORM=OHOS \
-      -DCMAKE_TOOLCHAIN_FILE=$(find $OHOS_ROOT -name "ohos.toolchain.cmake")
-

不出意外的话,就要出意外了。果不其然,报错Could NOT fid Glib2。思考之后,我们认为,问题出现在 lcm 所依赖的 glib 库上:在 x86 - 架构上,我们直接向系统安装了libglib2.0-dev,而 lcm 编译时直接从系统里找到了这个库;但当交叉编译时,由于 OHOS 交叉编译工具未提供 - libglib2.0-dev,lcm 找遍了系统也没找到 arm 架构下的这个库,因而直接报错。

-

解决方案自然是自己手动交叉编译libglib2.0-dev

-

Glib2.0 交叉编译

-

官网下载 glib-2.79.2 源码,解压后进入源码目录。

-
mkdir glib && cd glib
-wget https://download.gnome.org/sources/glib/2.79/glib-2.79.2.tar.xz
-tar -xvf glib-2.79.2.tar.xz
-mkdir build
-

这里需要注意的是:

- -

接着,参考博客这篇博客,撰写如下meson_ohos.txt(友情提醒,使用该文件时把native也就是 - NDK 的路径修改为自己的路径、源平台与目标平台按需修改):

-
[binaries]
-c = '$OHOS_ROOT/llvm/bin/aarch64-unknown-linux-ohos-clang'
-cpp = '$OHOS_ROOT/llvm/bin/aarch64-unknown-linux-ohos-clang++'
-ar = '$OHOS_ROOT/llvm/bin/llvm-ar'
-strip = '$OHOS_ROOT/llvm/bin/llvm-strip'
-ld = '$OHOS_ROOT/llvm/bin/llvm-link'
-
-[properties]
-skip_sanity_check = true
-sys_root = '$OHOS_ROOT/sysroot'
-c_args = ['-L$OHOS_ROOT/sysroot/usr/lib/aarch64-linux-ohos']
-
-[host_machine]
-system = 'linux'
-cpu_family = 'aarch64'
-cpu = 'aarch64'
-endian = 'little'
-
-[target_machine]
-system = 'ohos'
-cpu_family = 'aarch64'
-cpu = 'armv8a'
-endian = 'little'
-

将该文件放置在glib-2.79.2源码目录下,而后在build目录下执行以下命令:

-
meson --cross-file=../glib-2.79.2/meson_ohos.txt ..
-ninja
-

编译完成,正确地安装 glib2 到交叉编译工具链中(???),之后我们回到 lcm 的源码目录,重新执行 cmake 命令:

-
$OHOS_ROOT/build-tools/cmake/bin/cmake \
-      -DOHOS_STL=c++_shared \
-      -DOHOS_ARCH=armv8-a \
-      -DOHOS_PLATFORM=OHOS \
-      -DCMAKE_TOOLCHAIN_FILE=$(find $OHOS_ROOT -name "ohos.toolchain.cmake")
-

这次,编译成功了。万岁!

- -
-
- - - \ No newline at end of file diff --git a/code/ohos/lcm_compile.md b/code/ohos/lcm_compile.md deleted file mode 100644 index 8f34297..0000000 --- a/code/ohos/lcm_compile.md +++ /dev/null @@ -1,141 +0,0 @@ - - -# LCM 简介 - -LCM(Lightweight Communications and Marshalling)是一种轻量级的通信和编组库,是一种针对高带宽、低延迟、实时性要求高的场景下的通讯工具,用于在多个进程之间传递消息。LCM 的设计目标是提供一种简单的方法来传递结构化数据,而不需要复杂的 API 或协议定义。LCM 的消息传递是基于发布/订阅模型的,发布者和订阅者之间通过一个中心化的消息传递系统进行通信。 - -在机器人和自动驾驶系统中,LCM 可作为 ROS 的替代品,借以完成进程间、设备间的通讯。 - -为了方便地在不同进程、不同设备间传递数据,我们在 OHOS 系统中集成了 LCM 模块。 - -参考文档: - -- [LCM 官方文档](https://lcm-proj.github.io/) -- [一篇博客](https://zhuanlan.zhihu.com/p/621943685) - -# 依赖项安装 - -```bash -sudo apt update -sudo apt upgrade -sudo apt install build-essential cmake libglib2.0-dev -sudo apt install openjdk-8-jdk # lcm仅支持jdk8 -``` - -# x86 的 lcm - -如果我们仅仅需要 x86 架构的 lcm,执行下列命令即可: - -```bash -git clone https://github.com/lcm-proj/lcm -cd lcm -mkdir build && cd build -cmake .. -make -sudo make install -``` - -编译完成后,注意观察build目录下是否有lcm-java文件夹,如果没有,证明没有安装 java 或者 java 版本不对。 - -## lcm-python - -若想使用lcm-python,在lcm/lcm-python下执行`sudo python3 setup.py install`即可,使用时`import python`。 - -## lcm-spy 的使用 - -lcm-spy 是 LCM 配套的数据可视化工具。 -可以通过 lcm-spy 监视 lcm 数据发送频率、数据量、数据结构,甚至实时画图。 -如果安装了 JAVA,则 lcm-spy 会连同 LCM 一同被安装。 - -启动 lcm-spy: - -```bash -export CLASSPATH=${your lcm-type class path} -lcm-spy -``` - -# 交叉编译 - -既然 lcm 用的是 cmake,何不交叉编译到 OHOS 里用呢? - -首先,我们可以设置一下 Native Development Kit(NDK)的路径: - -```bash -# 设置成自己的NDK工具目录 -export OHOS_ROOT=~/app/native -``` - -```bash -$OHOS_ROOT/build-tools/cmake/bin/cmake \ - -DOHOS_STL=c++_shared \ - -DOHOS_ARCH=armv8-a \ - -DOHOS_PLATFORM=OHOS \ - -DCMAKE_TOOLCHAIN_FILE=$(find $OHOS_ROOT -name "ohos.toolchain.cmake") -``` - -不出意外的话,就要出意外了。果不其然,报错Could NOT fid Glib2。思考之后,我们认为,问题出现在 lcm 所依赖的 glib 库上:在 x86 架构上,我们直接向系统安装了`libglib2.0-dev`,而 lcm 编译时直接从系统里找到了这个库;但当交叉编译时,由于 OHOS 交叉编译工具未提供 libglib2.0-dev,lcm 找遍了系统也没找到 arm 架构下的这个库,因而直接报错。 - -解决方案自然是自己手动交叉编译`libglib2.0-dev`。 - -## Glib2.0 交叉编译 - -从[官网](https://download.gnome.org/sources/glib/)下载 glib-2.79.2 源码,解压后进入源码目录。 - -```bash -mkdir glib && cd glib -wget https://download.gnome.org/sources/glib/2.79/glib-2.79.2.tar.xz -tar -xvf glib-2.79.2.tar.xz -mkdir build -``` - -这里需要注意的是: - -- 虽然 glib 编译需要`meson`,但它并不能直接交叉编译,OHOS 也并未提供用于交叉编译的`meson`。 -- `meson`要求保证源码目录与构建目录不能相同,以保证构建过程的干净,因此我们在源码目录外新建了一个`build`目录。 - -接着,参考博客[这篇博客](https://t.csdnimg.cn/YfSJC),撰写如下meson_ohos.txt(友情提醒,使用该文件时把native也就是 NDK 的路径修改为自己的路径、源平台与目标平台按需修改): - -```meson -[binaries] -c = '$OHOS_ROOT/llvm/bin/aarch64-unknown-linux-ohos-clang' -cpp = '$OHOS_ROOT/llvm/bin/aarch64-unknown-linux-ohos-clang++' -ar = '$OHOS_ROOT/llvm/bin/llvm-ar' -strip = '$OHOS_ROOT/llvm/bin/llvm-strip' -ld = '$OHOS_ROOT/llvm/bin/llvm-link' - -[properties] -skip_sanity_check = true -sys_root = '$OHOS_ROOT/sysroot' -c_args = ['-L$OHOS_ROOT/sysroot/usr/lib/aarch64-linux-ohos'] - -[host_machine] -system = 'linux' -cpu_family = 'aarch64' -cpu = 'aarch64' -endian = 'little' - -[target_machine] -system = 'ohos' -cpu_family = 'aarch64' -cpu = 'armv8a' -endian = 'little' -``` - -将该文件放置在glib-2.79.2源码目录下,而后在build目录下执行以下命令: - -```bash -meson --cross-file=../glib-2.79.2/meson_ohos.txt .. -ninja -``` - -编译完成,正确地安装 glib2 到交叉编译工具链中(???),之后我们回到 lcm 的源码目录,重新执行 cmake 命令: - -```bash -$OHOS_ROOT/build-tools/cmake/bin/cmake \ - -DOHOS_STL=c++_shared \ - -DOHOS_ARCH=armv8-a \ - -DOHOS_PLATFORM=OHOS \ - -DCMAKE_TOOLCHAIN_FILE=$(find $OHOS_ROOT -name "ohos.toolchain.cmake") -``` - -这次,编译成功了。万岁! diff --git a/code/ohos/ohos_compile.html b/code/ohos/ohos_compile.html deleted file mode 100644 index bf72035..0000000 --- a/code/ohos/ohos_compile.html +++ /dev/null @@ -1,332 +0,0 @@ - - - - - - - - OpenHarmony 3.2 编译 - - - - - - -
-
-
-

OpenHarmony 3.2 编译

-
- -

[toc]

-
-

本次编译环境搭建参考了以下博客:

- -
-

环境与依赖

-

硬件环境

-

编译过程使用的是 - Ubuntu,经测试,Ubuntu 22.04 LTS/Ubuntu 20.04 LTS/Ubuntu 18.04 LTS - 均可用。

-

网上的大多数博客里没有对编译的硬件限制作出说明,现根据我们的情况,给出一个大概的范围:

- -

这里我用的是 VMWare+Ubuntu22.04 虚拟机。

-

软件包依赖

-

安装依赖在博客中都有讲,但少了一部分依赖项,我因而将所有必要的依赖项整理到一个脚本里了:

-
#!/bin/bash
-set -e # 一旦出错立刻停止执行,不会执行后续指令
-
-# 更新软件源
-sudo apt update
-sudo apt upgrade
-
-# 官方博客说明的依赖项
-sudo apt install binutils git git-lfs gnupg flex \
-    bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib \
-    libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev ccache \
-    libgl1-mesa-dev libxml2-utils xsltproc unzip m4 bc gnutls-bin \
-    python3-pip ruby libtinfo-dev libtinfo5 \
-
-# 官方博客未说明,但安装过程中报缺失的依赖项
-sudo apt install openjdk-8-jdk libssl-dev libelf-dev default-jdk \
-    genext2fs u-boot-tools mtd-utils scons gcc-arm-none-eabi \
-    liblz4-tool
-
-# 别的博客说明的依赖项
-sudo apt install device-tree-compiler lib32stdc++6 lib32z1 libncurses5-dev lib32ncurses6
-
-# 检查是否存在python3,如不存在则为之安装
-if ! [ -x "$(command -v python3)" ]; then
-    sudo apt install python3
-fi
-# 是否有python,如没有则建立软连接到python3的位置
-if ! [ -x "$(command -v python)" ]; then
-    sudo ln -s $(which python3) /usr/bin/python
-fi
-

这里需要注意的是,如果安装过程中出现报错,在修改之后,必须重新执行整个脚本,因为在 apt 发现找不到某个软件包之后,后续的包与命令都不会执行。

-

配置 git

-

git 基础设置

-

在上边我们已经下载了本次所需要的gitgit-lfs,接下来我们需要对git进行基础设置。

-
# 设置用户名与密码,新用户必做
-# 这里的用户名与密码只是一个写在git提交记录中的标识,可以与gitee/github账号无关
-git config --global user.name "yourname"
-git config --global user.email "your-email-address"
-# git凭证缓存,必做
-git config --global credential.helper store
-# git默认的文本编辑器是nano,我一般喜欢改为vim,选做
-git config --global core.editor vim
-

gitee 帐户

-

由于 OpenHarmony 的源码托管在 gitee 上,所以我们需要在 gitee 上注册一个帐户。注册的方法在此不复赘述。

-

本次编译过程中,我们并不需要提交代码,因而可以仅通过 http 方式从 gitee 下载源码——如果是这样的话,就不需要进行 git 与 gitee - 关联;如果想要提交代码,那么需要将本地的 git 与 gitee 账号关联起来,具体操作如下:

-
# 在本地执行
-ssh-keygen -t rsa -C "your-email-address"
-

命令执行后,每次需要输入都直接回车,连续有三个回车,执行就会结束,ssh - 密钥对的公钥和私钥分别保存在~/.ssh/id_rsa.pub~/.ssh/id_rsa中。

-

在 gitee 用户的设置界面,将公钥id_rsa.pub的内容复制到 gitee 的 SSH 公钥中,保存即可。

-

此时,我们就可以通过 ssh 的方式与 gitee 进行交互、也可以免密提交代码了。

-

系统源码编译

-

配置 repo 工具

-

注意:前两条命令需要以 root 身份执行!!!

-
# 以下两条命令需要以root身份执行
-sudo -s
-curl -s https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > /usr/local/bin/repo
-chmod a+x /usr/local/bin/repo
-exit
-
-# 这条普通身份也可以
-pip3 install -i https://repo.huaweicloud.com/repository/pypi/simple requests
-

之所以前两条命令需要以 root 身份而不能是 sudo,是因为/usr/local/bin是一个只有 root 用户才有写权限的目录,而 sudo 命令虽然是以 root - 身份执行,但重定向时候 sudo 用的也是当前用户身份,权限不足,自然报错。(如果是管道,那么 sudo - 也是只对当前命令有效,而不是对后续的整个管道有效。

-

获取源码

-

通过 repo + https/ssh 下载:

-
# 通过http下载,这里需要注意你需要的ohos的版本
-repo init -u https://gitee.com/openharmony/manifest.git -b master --no-repo-verify
-# 如果需要的是特定分支,-b后边改成对应分支名
-# 如果是tag,-b后的参数比较复杂,要在网页上提前确定好需要的tag名字,
-# 如下载的是tag为OpenHarmony-v3.2-Release的版本,命令如下:
-# repo init -u https://gitee.com/openharmony/manifest -b refs/tags/OpenHarmony-v3.2-Release --no-repo-verify
-# 除使用https外,也可以通过ssh下载
-# repo init -u git@gitee.com:openharmony/manifest.git -b master --no-repo-verify
-
-repo sync -c
-repo forall -c 'git lfs pull'
-

prebuilts 与编译

-
# 先在源码根目录下执行脚本,安装编译器及二进制工具
-bash build/prebuilts_download.sh
-# 再执行如下命令进行版本编译
-# 注意:默认编译的时候,目标cpu是32位,即使为64位cpu也无法使用64位功能
-sudo ./build.sh --product-name rk3568 --ccache
-# 如果是64位cpu,需要加上--target-cpu=arm64
-sudo ./build.sh --product-name rk3568 --ccache --target-cpu=arm64
-

编译完成

-

编译所生成的文件都归档在 out 目录下,结果镜像输出在源码根目录下的out/rk3568/packages/phone/images目录下。

-

自此源码编译成功,即可进行镜像烧录。

-

编译报错解决记录

-

以下记录编译过程中出现的一些出现过的报错、坑点和注意事项,以为后来者鉴。

-

ssh 下载源码报错

-

在使用 ssh - 下载时,第一步repo init -u git@gitee.com:openharmony/manifest.git -b master --no-repo-verify,报错: -

-
Warning: Permanently added 'gitee.com,180.76.198.77' (ECDSA) to the list of known hosts.
-

经检查,发现 gitee 邮箱设置与公钥中邮箱存在差异,修改 gitee 邮箱设置后成功。

-

设备磁盘空间不足

-

编译过程中异常终止,并显示“设备上没有空间”。

-

解决方法:

- -

如果在虚拟机里用 gparted 进行分区扩展时,弹窗说“分区被挂载为只读,无法调整大小”。此时我们需要:

- -
# 对于这个设备挂载到的每一个分区,都执行以下命令,以此类推
-sudo mount -o remount -rw /
-sudo mount -o remount -rw /var/snap/firefox/common/host-hunspell
-

而后在 gparted 中点击gparted->刷新设备,即可进行分区扩展。

-

一个奇怪的报错——GN Failed

-

这是一个很奇怪的报错,我们尚未找到真正的原因所在,只知道如何暂时地绕过去。

-

问题描述与复现

-

问题复现方法为在编译过程中,执行以下命令:

-
./build.sh --product-name rk3568 --ccache
-

该命令是指导书中写的编译命令,但执行时候会出错;与上文所述的经确认无误的编译命令不同在于没加sudo

-

如果按照指导书的命令来,不出意外编译将会报错: -

-

以下是该问题的具体描述:

-

build.log如下:

-
Set cache size limit to 100.0 GB
-[OHOS INFO] loader args:['platforms_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/platforms.build"', 'subsystem_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/subsystem_config.json"', 'example_subsystem_file=""', 'exclusion_modules_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/exclusion_modules.json"', 'source_root_dir="/home/axiomer/桌面/"', 'gn_root_out_dir="out/sdk"', 'build_platform_name=phone', 'build_xts=False', 'load_test_config=False', 'target_os=ohos', 'target_cpu=arm64', 'os_level=standard', "ignore_api_check=['xts', 'common', 'testfwk']", 'scalable_build=False', 'skip_partlist_check=False']
-[OHOS INFO] Excuting gn command: /home/axiomer/桌面/prebuilts/build-tools/linux-x86/bin/gn gen --args="product_name=\"ohos-sdk\" product_path=\"/home/axiomer/桌面/productdefine/common/products\" product_config_path=\"/home/axiomer/桌面/productdefine/common/products\" device_name=\"sdk\" device_path=\"/home/axiomer/桌面/device/board/ohos/sdk\" device_company=\"ohos\" device_config_path=\"/home/axiomer/桌面/device/board/ohos/sdk\" target_cpu=\"arm64\" is_standard_system=true ohos_build_compiler_specified=\"\" ohos_build_time=1707374188101 ohos_build_datetime=\"2024-02-08 22:36:28\" build_ohos_sdk=true build_ohos_ndk=true ohos_build_enable_ccache=true ohos_build_type=\"debug\" device_type=\"default\" build_variant=\"root\" use_thin_lto=false ndk_platform=\"linux\" sdk_for_hap_build=true skip_generate_module_list_file=true enable_lto_O0=true archive_ndk=false enable_ndk_doxygen=false use_cfi=false sdk_check_flag=false sdk_platform=\"linux\" root_perf_main=\"main\" runtime_mode=\"release\"" --args=product_name="ohos-sdk" product_path="/home/axiomer/桌面/productdefine/common/products" product_config_path="/home/axiomer/桌面/productdefine/common/products" device_name="sdk" device_path="/home/axiomer/桌面/device/board/ohos/sdk" device_company="ohos" device_config_path="/home/axiomer/桌面/device/board/ohos/sdk" target_cpu="arm64" is_standard_system=true ohos_build_compiler_specified="" ohos_build_time=1707374188101 ohos_build_datetime="2024-02-08 22:36:28" build_ohos_sdk=true build_ohos_ndk=true ohos_build_enable_ccache=true ohos_build_type="debug" device_type="default" build_variant="root" use_thin_lto=false ndk_platform="linux" sdk_for_hap_build=true skip_generate_module_list_file=true enable_lto_O0=true archive_ndk=false enable_ndk_doxygen=false use_cfi=false sdk_check_flag=false sdk_platform="linux" root_perf_main="main" runtime_mode="release" /home/axiomer/桌面/out/sdk
-ERROR at //build/config/BUILDCONFIG.gn:92:15: Could not read file.
-    read_file("${preloader_output_dir}/build_config.json", "json")
-              ^------------------------------------------
-I resolved this to "/home/axiomer/桌面/out/preloader/build_config.json".
-root_out_dir=//out/sdk
-root_build_dir=//out/sdk
-root_gen_dir=//out/sdk/gen
-current_toolchain=
-[91m[OHOS ERROR][0m Traceback (most recent call last):
-[91m[OHOS ERROR][0m   File "/home/axiomer/桌面/build/hb/containers/status.py", line 47, in wrapper
-[91m[OHOS ERROR][0m     return func(*args, **kwargs)
-[91m[OHOS ERROR][0m   File "/home/axiomer/桌面/build/hb/services/gn.py", line 197, in _execute_gn_gen_cmd
-[91m[OHOS ERROR][0m     SystemUtil.exec_command(gn_gen_cmd, self.config.log_path)
-[91m[OHOS ERROR][0m   File "/home/axiomer/桌面/build/hb/util/system_util.py", line 64, in exec_command
-[91m[OHOS ERROR][0m     LogUtil.get_failed_log(log_path)
-[91m[OHOS ERROR][0m   File "/home/axiomer/桌面/build/hb/util/log_util.py", line 191, in get_failed_log
-[91m[OHOS ERROR][0m     LogUtil.get_gn_failed_log(log_path)
-[91m[OHOS ERROR][0m   File "/home/axiomer/桌面/build/hb/util/log_util.py", line 137, in get_gn_failed_log
-[91m[OHOS ERROR][0m     raise OHOSException(
-[91m[OHOS ERROR][0m exceptions.ohos_exception.OHOSException: GN Failed! Please check error in /home/axiomer/桌面/out/sdk/error.log, and for more build information in /home/axiomer/桌面/out/sdk/build.log
-[91m[OHOS ERROR][0m
-[91m[OHOS ERROR][0m Code:        3000
-[91m[OHOS ERROR][0m
-[91m[OHOS ERROR][0m Reason:      GN Failed! Please check error in /home/axiomer/桌面/out/sdk/error.log, and for more build information in /home/axiomer/桌面/out/sdk/build.log
-[91m[OHOS ERROR][0m
-[91m[OHOS ERROR][0m Error Type:  UNKNOWN
-[91m[OHOS ERROR][0m
-[91m[OHOS ERROR][0m Description: An unknown error occurred while executing 'gn gen'.
-[91m[OHOS ERROR][0m
-[91m[OHOS ERROR][0m Solution:    There is no solution available. You can check the 'gn_error.log' in the output directory for more information
-[91m[OHOS ERROR][0m
-

报错信息中提到ERROR at //build/config/BUILDCONFIG.gn:92:15: Could not read file.,但是经过检查文件,存在名为BUILDCONFIG.gn的文件,且开放可读权限;但报错信息中显示无法读该文件。 -

-

失败尝试

-

在此,我进行了许多尝试,但都无一例外失败了;以下是我的尝试点,或许会有帮助:

- -

尝试未果 T^T

-

最终方案

-

报错信息如下所示:

-

在上述报错信息中提到Permission denied,即权限不够;于是尝试添加sudo,居然跑通了!!!!

-

在指导书的编译指令./build.sh --product-name rk3568 --ccache前添加权限设置sudo,即指令更改为:

-
sudo ./build.sh --product-name rk3568 --ccache
-

不知道为什么,编译过程中始终没有向我提出权限要求,但是不加sudo开权限就是跑不通,很奇怪 @ _ @也许是 OHOS 内敛不好意思申请权限呢……

-

- 另一个奇怪的报错——FAILED: load BTF from vmlinux: Unknown error -22

-

关于这个报错,当时的错误日志、运行日志、聊天截图均已丢失,但时日不久,记忆还算清楚,加上这个报错困扰了我好些天,因而必须记录。

-

问题简要描述

-

正常按照sudo ./build.sh --product-name rk3568 --ccache编译时,报错,主要报错信息如下:

-
die__process_unit: DW_TAG_label (0xa) @ <0x3adc> not handled!
-die__process_unit: DW_TAG_label (0xa) @ <0x3bdc> not handled!
-die__process_unit: DW_TAG_label (0xa) @ <0x3bef> not handled!
-die__process_unit: DW_TAG_label (0xa) @ <0x3ce5> not handled!
-die__process_unit: DW_TAG_label (0xa) @ <0x3cff> not handled!
-die__process_unit: DW_TAG_label (0xa) @ <0x3d19> not handled!
-Killed
-  LD      .tmp_vmlinux.kallsyms1
-  KSYMS   .tmp_vmlinux.kallsyms1.S
-  AS      .tmp_vmlinux.kallsyms1.S
-  LD      .tmp_vmlinux.kallsyms2
-  KSYMS   .tmp_vmlinux.kallsyms2.S
-  AS      .tmp_vmlinux.kallsyms2.S
-  LD      ymlinux
-  BTEIDS  vmlinux
-FAILED: load BTF from vmlinux: Unknown error -22make[2]: *** [/home/player/harmony/out/kernel/src_tmp/linux-5.19/
-Makefile:1225: vmlinux]错误 255
-make[1]: *** [arch/arm64/Makefile:208: rk3568-toybrick-x0-linux.img]错误2
-make[1]: 离开目录"/home/player/harmony/out/kernel/OBJ/linux-5.10"
-make: *** [Makefile:192:__sub-make]错误 2
-

可以看到,报错的关键点在于FAILED: load BTF from vmlinux: Unknown error -22这里。查看 Makefile 对应报错位置:

-
vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE
-    +$(call if_changed,link-vmlinux)
-
-targets := vmlinux
-

解释

- -

因而,我尝试了重装系统,将我的 Ubuntu22.04 重装为 20.04,但最后仍然会出现这个错误,足以证明不是系统差异导致的。

-

解决

-

最终,我在第二篇参考博客里找到了答案:编译较新的 - Linux 内核时,至少需要 10G+内存,而我一贯把虚拟机内存只设置为 8G。

-

令人好奇的是,为什么需要这么大内存?按理来说在虚拟内存空间里,物理内存不够的时候,不应该是由 OS 出面进行内存与磁盘的页面调度吗?我认为,可能是在编译过程中,其他进程占据一部分内存,而这里在链接一个虚拟 - linux 文件,可能文件体积过大而尚未创建完成,所有页面一直驻留在内存中,导致内存崩溃。

-

总而言之,言而总之,在VMware->虚拟机->设置->硬件->内存中,把内存扩大,就可以完美解决该问题。经我的测试,内存 13.2G(主机总内存 16G - 时候推荐的最大虚拟机内存)是能编译完成的,编译时长 6h。最终解决。

-

ohos 的 NDK

-

NDK 编译方式比较简单,在源码根目录下执行如下命令:

-
# 安装依赖
-./build/build_scripts/env_setup.sh
-
-# 执行完上述命令后记得执行source ~/.bashrc或者重启终端
-source ~/.bashrc
-
-# 安装编译SDK需要的依赖包(编译镜像的时候是不依赖这些包的)
-sudo apt-get install libxcursor-dev libxrandr-dev libxinerama-dev
-
-./build.sh --product-name ohos-sdk --ccache --build-target ohos_ndk
-

编译出来的 NDK 在out/sdk/packages/ohos-sdk/linux/native下。当然同时也有 windows 版本的 NDK,你猜在哪里?

-

将编译出来的 NDK 的 zip 解压到你想要的目录下,然后将该目录添加到环境变量中,即可使用。

-

注意,NDK 包提供的交叉编译工具是 cmake 和 ninja,编译器是 clang 和 clang++,并没有我们熟悉的 gcc/g++和 make。除此之外,NDK - 还未我们提供编译所需的全套服务,如编译工具链配置文件ohos.toolchain.cmake、头文件、库文件等。快说,谢谢 ohos~

-

完结撒花

-

本次鸿蒙开发环境的搭建过程可谓一波三折,总结几个最大的坑点,或许可以作为编译的经验罢:

- -

不说了,抓紧攒点钱开学升级电脑配置要紧……磁盘快炸了……

-

2024.2.12,甲辰年正月初三
2024.4.24改

- -
-
- - - \ No newline at end of file diff --git a/code/ohos/ohos_compile.md b/code/ohos/ohos_compile.md deleted file mode 100644 index 150ee25..0000000 --- a/code/ohos/ohos_compile.md +++ /dev/null @@ -1,420 +0,0 @@ - - -[toc] - -> 本次编译环境搭建参考了以下博客: -> -> - [HiHope_DAYU200/开发环境搭建编译指南](https://gitee.com/hihope_iot/docs/blob/master/HiHope_DAYU200/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E7%BC%96%E8%AF%91%E6%8C%87%E5%8D%97.md) -> - [基于 Ubuntu 20.04 配置 OpenHarmony 开发环境](https://juejin.cn/post/7257553293889634363) -> - [OpenHarmony 源码编译步骤(基于 3.2 beta3)](https://blog.csdn.net/jwq1220/article/details/127303546?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127303546%22%2C%22source%22%3A%222401_82617925%22%7D&fromshare=blogdetail) -> - [FAILED: load BTF from vmlinux: Unknown error -2](https://unix.stackexchange.com/questions/616392/failed-load-btf-from-vmlinux-unknown-error-2make-makefile1162-vmlinu) - -# 环境与依赖 - -## 硬件环境 - -编译过程使用的是 `Ubuntu`,经测试,`Ubuntu 22.04 LTS`/`Ubuntu 20.04 LTS`/`Ubuntu 18.04 LTS` 均可用。 - -网上的大多数博客里没有对编译的硬件限制作出说明,现根据我们的情况,给出一个大概的范围: - -- **内存必须在 10G 以上**,我使用的是 13G,编译成功(8G 时编译失败了,下文会说明) -- **磁盘 160G 或以上** - - 源码大小就 36G 左右了 - - 大量的依赖会占据数十 G 的空间 - - 我用的是 160G 磁盘,编译成功,但基本不剩什么空间了 -- 注意所需要的**ohos 的版本、cpu 版本、位数等信息** - -这里我用的是 VMWare+Ubuntu22.04 虚拟机。 - -## 软件包依赖 - -安装依赖在博客中都有讲,但**少了一部分依赖项**,我因而将所有必要的依赖项整理到一个脚本里了: - -```bash -#!/bin/bash -set -e # 一旦出错立刻停止执行,不会执行后续指令 - -# 更新软件源 -sudo apt update -sudo apt upgrade - -# 官方博客说明的依赖项 -sudo apt install binutils git git-lfs gnupg flex \ - bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib \ - libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev ccache \ - libgl1-mesa-dev libxml2-utils xsltproc unzip m4 bc gnutls-bin \ - python3-pip ruby libtinfo-dev libtinfo5 \ - -# 官方博客未说明,但安装过程中报缺失的依赖项 -sudo apt install openjdk-8-jdk libssl-dev libelf-dev default-jdk \ - genext2fs u-boot-tools mtd-utils scons gcc-arm-none-eabi \ - liblz4-tool - -# 别的博客说明的依赖项 -sudo apt install device-tree-compiler lib32stdc++6 lib32z1 libncurses5-dev lib32ncurses6 - -# 检查是否存在python3,如不存在则为之安装 -if ! [ -x "$(command -v python3)" ]; then - sudo apt install python3 -fi -# 是否有python,如没有则建立软连接到python3的位置 -if ! [ -x "$(command -v python)" ]; then - sudo ln -s $(which python3) /usr/bin/python -fi -``` - -这里需要注意的是,**如果安装过程中出现报错,在修改之后,必须重新执行整个脚本**,因为在 apt 发现找不到某个软件包之后,后续的包与命令都不会执行。 - -# 配置 git - -## git 基础设置 - -在上边我们已经下载了本次所需要的`git`与`git-lfs`,接下来我们需要对`git`进行基础设置。 - -```bash -# 设置用户名与密码,新用户必做 -# 这里的用户名与密码只是一个写在git提交记录中的标识,可以与gitee/github账号无关 -git config --global user.name "yourname" -git config --global user.email "your-email-address" -# git凭证缓存,必做 -git config --global credential.helper store -# git默认的文本编辑器是nano,我一般喜欢改为vim,选做 -git config --global core.editor vim -``` - -## gitee 帐户 - -由于 OpenHarmony 的源码托管在 gitee 上,所以我们需要在 gitee 上注册一个帐户。注册的方法在此不复赘述。 - -本次编译过程中,我们并不需要提交代码,因而可以仅通过 http 方式从 gitee 下载源码——如果是这样的话,**就不需要进行 git 与 gitee 关联**;如果想要提交代码,那么需要将本地的 git 与 gitee 账号关联起来,具体操作如下: - -```bash -# 在本地执行 -ssh-keygen -t rsa -C "your-email-address" -``` - -命令执行后,每次需要输入都直接回车,**连续有三个回车**,执行就会结束,ssh 密钥对的公钥和私钥分别保存在`~/.ssh/id_rsa.pub`和`~/.ssh/id_rsa`中。 - -在 gitee 用户的设置界面,将公钥`id_rsa.pub`的内容复制到 gitee 的 SSH 公钥中,保存即可。 - -此时,我们就可以通过 ssh 的方式与 gitee 进行交互、也可以免密提交代码了。 - -# 系统源码编译 - -## 配置 repo 工具 - -**注意:前两条命令需要以 root 身份执行!!!** - -```bash -# 以下两条命令需要以root身份执行 -sudo -s -curl -s https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > /usr/local/bin/repo -chmod a+x /usr/local/bin/repo -exit - -# 这条普通身份也可以 -pip3 install -i https://repo.huaweicloud.com/repository/pypi/simple requests -``` - -之所以前两条命令需要以 root 身份而不能是 sudo,是因为`/usr/local/bin`是一个只有 root 用户才有写权限的目录,而 sudo 命令虽然是以 root 身份执行,但**重定向时候 sudo 用的也是当前用户身份**,权限不足,自然报错。(**如果是管道,那么 sudo 也是只对当前命令有效,而不是对后续的整个管道有效。**) - -## 获取源码 - -通过 repo + https/ssh 下载: - -```bash -# 通过http下载,这里需要注意你需要的ohos的版本 -repo init -u https://gitee.com/openharmony/manifest.git -b master --no-repo-verify -# 如果需要的是特定分支,-b后边改成对应分支名 -# 如果是tag,-b后的参数比较复杂,要在网页上提前确定好需要的tag名字, -# 如下载的是tag为OpenHarmony-v3.2-Release的版本,命令如下: -# repo init -u https://gitee.com/openharmony/manifest -b refs/tags/OpenHarmony-v3.2-Release --no-repo-verify -# 除使用https外,也可以通过ssh下载 -# repo init -u git@gitee.com:openharmony/manifest.git -b master --no-repo-verify - -repo sync -c -repo forall -c 'git lfs pull' -``` - -## prebuilts 与编译 - -```bash -# 先在源码根目录下执行脚本,安装编译器及二进制工具 -bash build/prebuilts_download.sh -# 再执行如下命令进行版本编译 -# 注意:默认编译的时候,目标cpu是32位,即使为64位cpu也无法使用64位功能 -sudo ./build.sh --product-name rk3568 --ccache -# 如果是64位cpu,需要加上--target-cpu=arm64 -sudo ./build.sh --product-name rk3568 --ccache --target-cpu=arm64 -``` - -## 编译完成 - -编译所生成的文件都归档在 out 目录下,结果镜像输出在源码根目录下的`out/rk3568/packages/phone/images`目录下。 - -自此源码编译成功,即可进行镜像烧录。 - -# 编译报错解决记录 - -以下记录编译过程中出现的一些出现过的报错、坑点和注意事项,以为后来者鉴。 - -## ssh 下载源码报错 - -在使用 ssh 下载时,第一步`repo init -u git@gitee.com:openharmony/manifest.git -b master --no-repo-verify`,报错: - -``` -Warning: Permanently added 'gitee.com,180.76.198.77' (ECDSA) to the list of known hosts. -``` - -经检查,发现 gitee 邮箱设置与公钥中邮箱存在差异,修改 gitee 邮箱设置后成功。 - -## 设备磁盘空间不足 - -编译过程中异常终止,并显示“设备上没有空间”。 - -**解决方法:** - -- 在`VMWare`中扩大磁盘空间 - - **关闭虚拟机** - - 在`VMWare`上方菜单栏中依次选择`虚拟机->设置->硬件->硬盘->扩展`,然后输入新的磁盘大小 - - 点击确定后会提示,说需要在虚拟机中扩展分区,才能使用新的磁盘空间 -- 打开虚拟机,并扩展分区 - - 在命令行中输入`sudo apt install gparted`,安装`gparted`分区工具 - - 打开该工具,选择需要扩展的分区(一般为挂载到根目录`/`的那个分区),点击`Resize/Move`,然后拖动分区大小,点击`Resize/Move`,再点击`Apply`,等待分区扩展完成 - -如果在虚拟机里用 gparted 进行分区扩展时,弹窗说“分区被挂载为只读,无法调整大小”。此时我们需要: - -- 右键该分区,查看挂载到什么位置了。一般是挂载到`/`与`/var/snap/firefox/common/host-hunspell` -- 执行以下命令: - -```bash -# 对于这个设备挂载到的每一个分区,都执行以下命令,以此类推 -sudo mount -o remount -rw / -sudo mount -o remount -rw /var/snap/firefox/common/host-hunspell -``` - -而后在 gparted 中点击`gparted->刷新设备`,即可进行分区扩展。 - -## 一个奇怪的报错——`GN Failed` - -这是一个很奇怪的报错,我们尚未找到真正的原因所在,只知道如何暂时地绕过去。 - -### 问题描述与复现 - -问题复现方法为在编译过程中,执行以下命令: - -```bash -./build.sh --product-name rk3568 --ccache -``` - -该命令是指导书中写的编译命令,但执行时候会出错;**与上文所述的经确认无误的编译命令不同在于没加`sudo`**。 - -如果按照指导书的命令来,不出意外编译将会报错: -![](https://www.qin-juan-ge-zhu.top/images/code/ohos_gn_fail_1.png) -![](https://www.qin-juan-ge-zhu.top/images/code/ohos_gn_fail_2.png) - -**以下是该问题的具体描述:** - -`build.log`如下: - -```plaintext -Set cache size limit to 100.0 GB -[OHOS INFO] loader args:['platforms_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/platforms.build"', 'subsystem_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/subsystem_config.json"', 'example_subsystem_file=""', 'exclusion_modules_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/exclusion_modules.json"', 'source_root_dir="/home/axiomer/桌面/"', 'gn_root_out_dir="out/sdk"', 'build_platform_name=phone', 'build_xts=False', 'load_test_config=False', 'target_os=ohos', 'target_cpu=arm64', 'os_level=standard', "ignore_api_check=['xts', 'common', 'testfwk']", 'scalable_build=False', 'skip_partlist_check=False'] -[OHOS INFO] Excuting gn command: /home/axiomer/桌面/prebuilts/build-tools/linux-x86/bin/gn gen --args="product_name=\"ohos-sdk\" product_path=\"/home/axiomer/桌面/productdefine/common/products\" product_config_path=\"/home/axiomer/桌面/productdefine/common/products\" device_name=\"sdk\" device_path=\"/home/axiomer/桌面/device/board/ohos/sdk\" device_company=\"ohos\" device_config_path=\"/home/axiomer/桌面/device/board/ohos/sdk\" target_cpu=\"arm64\" is_standard_system=true ohos_build_compiler_specified=\"\" ohos_build_time=1707374188101 ohos_build_datetime=\"2024-02-08 22:36:28\" build_ohos_sdk=true build_ohos_ndk=true ohos_build_enable_ccache=true ohos_build_type=\"debug\" device_type=\"default\" build_variant=\"root\" use_thin_lto=false ndk_platform=\"linux\" sdk_for_hap_build=true skip_generate_module_list_file=true enable_lto_O0=true archive_ndk=false enable_ndk_doxygen=false use_cfi=false sdk_check_flag=false sdk_platform=\"linux\" root_perf_main=\"main\" runtime_mode=\"release\"" --args=product_name="ohos-sdk" product_path="/home/axiomer/桌面/productdefine/common/products" product_config_path="/home/axiomer/桌面/productdefine/common/products" device_name="sdk" device_path="/home/axiomer/桌面/device/board/ohos/sdk" device_company="ohos" device_config_path="/home/axiomer/桌面/device/board/ohos/sdk" target_cpu="arm64" is_standard_system=true ohos_build_compiler_specified="" ohos_build_time=1707374188101 ohos_build_datetime="2024-02-08 22:36:28" build_ohos_sdk=true build_ohos_ndk=true ohos_build_enable_ccache=true ohos_build_type="debug" device_type="default" build_variant="root" use_thin_lto=false ndk_platform="linux" sdk_for_hap_build=true skip_generate_module_list_file=true enable_lto_O0=true archive_ndk=false enable_ndk_doxygen=false use_cfi=false sdk_check_flag=false sdk_platform="linux" root_perf_main="main" runtime_mode="release" /home/axiomer/桌面/out/sdk -ERROR at //build/config/BUILDCONFIG.gn:92:15: Could not read file. - read_file("${preloader_output_dir}/build_config.json", "json") - ^------------------------------------------ -I resolved this to "/home/axiomer/桌面/out/preloader/build_config.json". -root_out_dir=//out/sdk -root_build_dir=//out/sdk -root_gen_dir=//out/sdk/gen -current_toolchain= -[91m[OHOS ERROR][0m Traceback (most recent call last): -[91m[OHOS ERROR][0m File "/home/axiomer/桌面/build/hb/containers/status.py", line 47, in wrapper -[91m[OHOS ERROR][0m return func(*args, **kwargs) -[91m[OHOS ERROR][0m File "/home/axiomer/桌面/build/hb/services/gn.py", line 197, in _execute_gn_gen_cmd -[91m[OHOS ERROR][0m SystemUtil.exec_command(gn_gen_cmd, self.config.log_path) -[91m[OHOS ERROR][0m File "/home/axiomer/桌面/build/hb/util/system_util.py", line 64, in exec_command -[91m[OHOS ERROR][0m LogUtil.get_failed_log(log_path) -[91m[OHOS ERROR][0m File "/home/axiomer/桌面/build/hb/util/log_util.py", line 191, in get_failed_log -[91m[OHOS ERROR][0m LogUtil.get_gn_failed_log(log_path) -[91m[OHOS ERROR][0m File "/home/axiomer/桌面/build/hb/util/log_util.py", line 137, in get_gn_failed_log -[91m[OHOS ERROR][0m raise OHOSException( -[91m[OHOS ERROR][0m exceptions.ohos_exception.OHOSException: GN Failed! Please check error in /home/axiomer/桌面/out/sdk/error.log, and for more build information in /home/axiomer/桌面/out/sdk/build.log -[91m[OHOS ERROR][0m -[91m[OHOS ERROR][0m Code: 3000 -[91m[OHOS ERROR][0m -[91m[OHOS ERROR][0m Reason: GN Failed! Please check error in /home/axiomer/桌面/out/sdk/error.log, and for more build information in /home/axiomer/桌面/out/sdk/build.log -[91m[OHOS ERROR][0m -[91m[OHOS ERROR][0m Error Type: UNKNOWN -[91m[OHOS ERROR][0m -[91m[OHOS ERROR][0m Description: An unknown error occurred while executing 'gn gen'. -[91m[OHOS ERROR][0m -[91m[OHOS ERROR][0m Solution: There is no solution available. You can check the 'gn_error.log' in the output directory for more information -[91m[OHOS ERROR][0m -``` - -报错信息中提到`ERROR at //build/config/BUILDCONFIG.gn:92:15: Could not read file.`,但是经过检查文件,**存在名为`BUILDCONFIG.gn`的文件,且开放可读权限**;但报错信息中显示无法读该文件。 - -### 失败尝试 - -在此,我进行了许多尝试,但都无一例外失败了;以下是我的尝试点,或许会有帮助: - -- 根据[一个相似提问](),对相关依赖进行安装,失败了: - ![](https://www.qin-juan-ge-zhu.top/images/code/ohos_gn_fail_dependencies.png) -- 根据报错信息,在编译命令中添加`--no-prebuilt-sdk`选项对 ohos-sdk 的构建进行跳过,依然失败 - -尝试未果 T^T - -### 最终方案 - -报错信息如下所示: - -在上述报错信息中提到`Permission denied`,即权限不够;于是尝试添加`sudo`,居然跑通了!!!! - -在指导书的编译指令`./build.sh --product-name rk3568 --ccache`前添加权限设置`sudo`,即指令更改为: - -```bash -sudo ./build.sh --product-name rk3568 --ccache -``` - -不知道为什么,编译过程中始终没有向我提出权限要求,但是不加`sudo`开权限就是跑不通,很奇怪 @ \_ @也许是 OHOS 内敛不好意思申请权限呢…… - -## 另一个奇怪的报错——`FAILED: load BTF from vmlinux: Unknown error -22` - -关于这个报错,当时的错误日志、运行日志、聊天截图均已丢失,但时日不久,记忆还算清楚,加上这个报错困扰了我好些天,因而必须记录。 - -### 问题简要描述 - -正常按照`sudo ./build.sh --product-name rk3568 --ccache`编译时,报错,主要报错信息如下: - -```plaintext -die__process_unit: DW_TAG_label (0xa) @ <0x3adc> not handled! -die__process_unit: DW_TAG_label (0xa) @ <0x3bdc> not handled! -die__process_unit: DW_TAG_label (0xa) @ <0x3bef> not handled! -die__process_unit: DW_TAG_label (0xa) @ <0x3ce5> not handled! -die__process_unit: DW_TAG_label (0xa) @ <0x3cff> not handled! -die__process_unit: DW_TAG_label (0xa) @ <0x3d19> not handled! -Killed - LD .tmp_vmlinux.kallsyms1 - KSYMS .tmp_vmlinux.kallsyms1.S - AS .tmp_vmlinux.kallsyms1.S - LD .tmp_vmlinux.kallsyms2 - KSYMS .tmp_vmlinux.kallsyms2.S - AS .tmp_vmlinux.kallsyms2.S - LD ymlinux - BTEIDS vmlinux -FAILED: load BTF from vmlinux: Unknown error -22make[2]: *** [/home/player/harmony/out/kernel/src_tmp/linux-5.19/ -Makefile:1225: vmlinux]错误 255 -make[1]: *** [arch/arm64/Makefile:208: rk3568-toybrick-x0-linux.img]错误2 -make[1]: 离开目录"/home/player/harmony/out/kernel/OBJ/linux-5.10" -make: *** [Makefile:192:__sub-make]错误 2 -``` - -可以看到,报错的关键点在于`FAILED: load BTF from vmlinux: Unknown error -22`这里。查看 Makefile 对应报错位置: - -```makefile -vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE - +$(call if_changed,link-vmlinux) - -targets := vmlinux -``` - -### 解释 - -- 这段 makefile 的实际功能是链接一个虚拟 linux - - 通过目录位置,我们可以合理猜测,这里是在进行**linux 内核编译,但失败了** -- 报错中说的`BTF`则是数据格式 - - 一般出现`load BTF from xxx`错误时,是由于系统不一致导致的(也就是不同系统 BTF 格式不一样,出现了识别错误) -- 最重要的是,**Unknown error -22 这个报错码,搜不到任何信息!** - -因而,我尝试了重装系统,将我的 Ubuntu22.04 重装为 20.04,但最后**仍然会出现这个错误**,足以证明不是系统差异导致的。 - -### 解决 - -最终,我在[第二篇参考博客](https://unix.stackexchange.com/questions/616392/failed-load-btf-from-vmlinux-unknown-error-2make-makefile1162-vmlinu)里找到了答案:编译较新的 Linux 内核时,至少需要 10G+内存,而我一贯把虚拟机内存只设置为 8G。 - -令人好奇的是,为什么需要这么大内存?按理来说在虚拟内存空间里,物理内存不够的时候,不应该是由 OS 出面进行内存与磁盘的页面调度吗?我认为,可能是在编译过程中,其他进程占据一部分内存,而这里在链接一个虚拟 linux 文件,可能文件体积过大而尚未创建完成,所有页面一直驻留在内存中,导致内存崩溃。 - -总而言之,言而总之,在`VMware->虚拟机->设置->硬件->内存`中,把内存扩大,就可以完美解决该问题。经我的测试,内存 13.2G(主机总内存 16G 时候推荐的最大虚拟机内存)是能编译完成的,编译时长 6h。最终解决。 - -# ohos 的 NDK - -NDK 编译方式比较简单,在源码根目录下执行如下命令: - -```bash -# 安装依赖 -./build/build_scripts/env_setup.sh - -# 执行完上述命令后记得执行source ~/.bashrc或者重启终端 -source ~/.bashrc - -# 安装编译SDK需要的依赖包(编译镜像的时候是不依赖这些包的) -sudo apt-get install libxcursor-dev libxrandr-dev libxinerama-dev - -./build.sh --product-name ohos-sdk --ccache --build-target ohos_ndk -``` - -编译出来的 NDK 在`out/sdk/packages/ohos-sdk/linux/native`下。当然同时也有 windows 版本的 NDK,你猜在哪里? - -将编译出来的 NDK 的 zip 解压到你想要的目录下,然后将该目录添加到环境变量中,即可使用。 - -注意,NDK 包提供的交叉编译工具是 cmake 和 ninja,编译器是 clang 和 clang++,并没有我们熟悉的 gcc/g++和 make。除此之外,NDK 还未我们提供编译所需的全套服务,如编译工具链配置文件`ohos.toolchain.cmake`、头文件、库文件等。快说,谢谢 ohos~ - -为了更方便地使用NDK,鄙人不才,写了两个脚本,分别用于cmake编译和单文件编译: - -```bash -#!/bin/bash - -########################################################################## -# File Name : compile.sh -# Encoding : utf-8 -# Author : We-unite -# Email : weunite1848@gmail.com -# Created Time : 2024-02-29 15:19:15 -########################################################################## - -set -e - -if [ $UID -eq 0 ]; then - echo "Please do not run this script as root" - exit 1 -fi - -if [ $# -ne 2 ]; then - echo "Usage: $0 " - exit 1 -fi - -if [ $2 == "v8" ]; then - arch=arm64-v8a -elif [ $2 == "v7" ]; then - arch=armeabi-v7a -else - echo "Invalid architecture: $2" - exit 1 -fi - -link=$1 # static or shared -native_path=~/app/native - -export PATH=$native_path/build-tools/cmake/bin:$PATH - -# 使用cmake编译,编译生成的文件运行在rk3568上 -cmake -B build -D OHOS_STL=c++_$link -D OHOS_ARCH=$arch -D OHOS_PLATFORM=OHOS -D CMAKE_TOOLCHAIN_FILE=$(find $native_path -name ohos.toolchain.cmake) -cmake --build build -``` - -```bash -``` - -# 完结撒花 - -本次鸿蒙开发环境的搭建过程可谓一波三折,总结几个最大的坑点,或许可以作为编译的经验罢: - -- **有报错,试试添加权限!** -- 贫贱程序猿百事哀,编译 OpenHarmony 系统需要**足够的硬件配置** - - 内存 10G+ - - 磁盘 160G+ - -不说了,抓紧攒点钱开学升级电脑配置要紧……磁盘快炸了…… \ No newline at end of file diff --git a/code/projects/godo.html b/code/projects/godo.html new file mode 100644 index 0000000..ed64840 --- /dev/null +++ b/code/projects/godo.html @@ -0,0 +1,362 @@ + + + + + + + + godo知识总结 + + + + + + +
+
+

godo知识总结

+

背景说明

+

本文档对godo编写过程中新了解到的技术、遇到的问题进行简要说明,以备所需。

+

系统调用

+

As is universually acknowledged, 操作系统、尤其是类 Unix 操作系统,以系统调用的形式对应用程序提供服务。系统调用是名称,有系统调用号与之对应(同一版本的内核在不同架构的 + cpu 上,系统调用号可能不一样)。有的时候我们需要了解一些内核行为,但却不知道从何下手。可以通过查看内核源码来学习。

+

系统调用可以在源码中查找到。由于本项目使用的是 centos 7,内核版本 3.10.0-1160、cpu 为 x86-64 架构,兹以该版本内核为例说明。

+

要查看 fork + 的系统调用号,查看arch/x86/syscalls/syscall_64.tbl。想要查看其具体的实现,则在源码根目录下执行grep -rInP "SYSCALL_DEFINE\d\(fork",其中 + SYSCALL_DEFINE+数字是 kernel + 中定义的宏,展开即完整的函数声明。通过这种查找办法,我们可以快速地定位内核中对系统调用的处理函数,查看其工作原理。查看其他的内核相关内容也可以采取类似办法,即先用 grep + 定位大致范围、看都有什么地方用到,然后找到真正起作用的地方,读相关代码。

+

使用这些系统调用有两种办法:

+
    +
  • 在 C 语言中直接调用同名函数,但大概率经过了 glibc 的封装
  • +
  • 手动封装。如下:
  • +
+
#include <stdio.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+int main(){
+    pid_t pid = syscall(SYS_fork);
+    // syscall是一个变参函数,第一个参数是系统调用号,接下来的是系统调用的各个参数
+    // syscall定义在 unistd.h
+    // SYS_fork定义在 sys/syscall.h
+    if(pid == 0) {
+        printf("Child!\n");
+    } else {
+        printf("Parent!\n");
+    }
+    return 0;
+}
+

这种封装方式与经常被用来当作 os 教材的 Linux-0.11/0.12 有所区别。Linux-0.11 环境上,unistd.h 大致如下:

+
#ifndef _UNISTD_H
+#define _UNISTD_H
+
+...
+#include <sys/stat.h>
+#include <sys/times.h>
+#include <sys/utsname.h>
+#include <utime.h>
+
+#ifdef __LIBRARY__
+
+#define __NR_setup  0   /* used only by init, to get system going */
+#define __NR_exit   1
+#define __NR_fork   2
+#define __NR_read   3
+#define __NR_write  4
+#define __NR_open   5
+#define __NR_close  6
+...
+
+#define _syscall0(type,name) \
+  type name(void) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+    : "=a" (__res) \
+    : "0" (__NR_##name)); \
+if (__res >= 0) \
+    return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall1(type,name,atype,a) \
+type name(atype a) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+    : "=a" (__res) \
+    : "0" (__NR_##name),"b" ((long)(a))); \
+if (__res >= 0) \
+    return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+...
+#endif /* __LIBRARY__ */
+...
+
+#endif
+

可以看到,Linux-0.11 上,封装的一般方法为:

+
#define __LIBRARY__ // 一定要在unistd.h之前
+#include <unistd.h>
+#include <stdio.h>
+
+syscall0(int, fork); // 宏替换后这就是个名为fork的函数的具体实现了
+int main() {
+    if(fork() == 0) {
+        printf("Child!\n");
+    } else {
+        printf("Parent!\n");
+    }
+    return 0;
+}
+

但是无论如何,一般情况下不推荐手动封装,这不是 release 版该有的做法。

+

此外,从汇编代码来看,Linux-0.11 所用的 80386 + 芯片,不提供专门的系统调用指令,因而该系统使用的是int 0x80中断指令,通过注册中断处理函数进行对应处理;而现代 x86 提供了专门的 syscall + 指令,Linux 系统直接用该指令进行系统调用。

+

系统调用中的进程与线程

+

一般地,在 Linux 系统上,我们以 pid 指代进程号,而进程可以有多个线程。很显然,真正被调度执行的单元应该是线程,换言之,是 thread 而非 process 真正地对应着内核中 + tasks 表里的一个 task,而每个 task 才具有独一无二的 id

+

常见系统调用的分析

+

看看这个:

+
extern int pthread_create (pthread_t *__restrict __newthread,
+               const pthread_attr_t *__restrict __attr,
+               void *(*__start_routine) (void *),
+               void *__restrict __arg) __THROWNL __nonnull ((1, 3));
+

pthread_create函数的第一个参数,就是一个 pthread_t 类型的指针,处理后将 task 的 id 写到指针指向的区域。

+

让我们来看一段简单的代码:

+
// test.c
+#include <stdio.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+void *test(void *args) {
+    printf("Hello, I'm %d\n", getpid());
+}
+
+int main() {
+    pthread_t pthid;
+    int pid;
+    pthread_create(&pthid, NULL, test, NULL);
+    printf("main: thread %ld\n", pthid);
+    pthread_join(pthid, NULL);
+    if ((pid = fork()) == 0) {
+        printf("Hello, I'm %d\n", getpid());
+        return 0;
+    }
+    printf("main: child process %d\n",pid);
+    if ((pid = syscall(SYS_fork)) == 0) {
+        printf("Hello, I'm %d\n", getpid());
+        return 0;
+    }
+    printf("main: child process %d\n",pid);
+    return 0;
+}
+

当我们使用strace ./test来查看上述代码时,会发现情况如下:

+
clone(child_stack=0x7f3dd28bbff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f3dd28bc9d0, tls=0x7f3dd28bc700, child_tidptr=0x7f3dd28bc9d0) = 21756
+write(1, "main: thread 139903502108416\n", 29) = 29
+clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f3dd308e9d0) = 21757
+--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21757, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+write(1, "main: child process 21757\n", 26) = 26
+fork()                                  = 21758
+--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21758, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+write(1, "main: child process 21758\n", 26) = 26
+exit_group(0)                           = ?
++++ exited with 0 +++
+

从这样的输出里,我们可以清晰地看到,无论是pthread_create还是fork(指库函数),本质上都是封装了clone系统调用,即使 + Linux 本身提供了专门的 fork 系统调用。也许这是 glibc 和 Linux 都想在添加功能的基础上保证代码兼容性?花开两朵各表一枝了属于是。

+

这一结论也可以从 glibc 的代码中得到验证:

+
// 文件 glibc-2.18/nptl/sysdeps/unix/sysv/linux/pt-fork.c
+pid_t
+__fork (void)
+{
+  return __libc_fork ();
+}
+strong_alias (__fork, fork)
+
+
+// 文件 glibc-2.18/nptl/sysdeps/unix/sysv/linux/fork.c
+pid_t
+__libc_fork (void)
+{
+  ... // 一堆不知所云的代码
+#ifdef ARCH_FORK
+  pid = ARCH_FORK ();
+#else
+# error "ARCH_FORK must be defined so that the CLONE_SETTID flag is used"
+  pid = INLINE_SYSCALL (fork, 0);
+#endif
+  ... // 又是一堆不知所云的代码
+}
+
+// 文件 glibc-2.18/nptl/sysdeps/unix/sysv/linux/x86_64/fork.c
+#define ARCH_FORK() \
+  INLINE_SYSCALL (clone, 4,                           \
+          CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 0,     \
+          NULL, &THREAD_SELF->tid)
+
+// 文件 glibc-2.18/sysdeps/unix/sysv/linux/x86_64/syscall.S
+
+/* Please consult the file sysdeps/unix/sysv/linux/x86-64/sysdep.h for
+   more information about the value -4095 used below.  */
+    .text
+ENTRY (syscall)
+    movq %rdi, %rax     /* Syscall number -> rax.  */
+    movq %rsi, %rdi     /* shift arg1 - arg5.  */
+    movq %rdx, %rsi
+    movq %rcx, %rdx
+    movq %r8, %r10
+    movq %r9, %r8
+    movq 8(%rsp),%r9    /* arg6 is on the stack.  */
+    syscall         /* Do the system call.  */
+    cmpq $-4095, %rax   /* Check %rax for error.  */
+    jae SYSCALL_ERROR_LABEL /* Jump to error handler if error.  */
+    ret         /* Return to caller.  */
+
+PSEUDO_END (syscall)
+

可以看到,fork 库函数实际上是掉入了__libc_fork,在经过各种处理之后,如果 glibc 中该平台的相关代码里定义了 ARCH_FORK + 宏,则调用之;否则会直接调用INLINE_SYSCALL(这是 glibc + 各个平台的代码里都有的宏);而如果直接调用syscall函数手动封装系统调用,则调用什么就是什么。syscall函数调用过程涉及延迟绑定等问题,就不是这里的重点了,而且我也没太搞明白,有机会单开一篇吧。 +

+

进程与线程

+

对于一个进程而言,它有很多线程,每个线程有一个号,但整个进程都有主线程的号,称为 tgid,只有一个 tgid 能真正地代表一个进程,而 pid 事实上是 task 的编号。

+

对于 netlink connector 而言,它听到的 fork 并不是 fork,而是 clone;对于 audit,也只能听到 clone 而听不到 fork。这是因为在内核中,fork 也是通过调用 + clone 的处理函数来进行的。clone 创建的是一个 task,至于具体是进程还是线程,取决于用的 flag 参数,参见 manual 手册。

+

因而,无论使用 connector 还是 audit,拿到的都是 pid,只不过 connector 可以直接拿到 tgid、据此确定是进程还是线程,而 audit 只能拿到 pid,需要从 clone + 的参数里查看是进程还是线程,且拿不到 tgid。这也就是我在项目中选择使用 connector 听进程消息的原因。

+

干巴巴说了这么多,其实就是想说,pid 也许在不同的语境下有不同含义。

+

docker 使用的技术

+

cgroup

+

Linux 下用来控制进程资源的东西。没学明白,留缺。姑且抄点书上的内容来占个位置吧。

+

cgroup 是 control group 的简写,是 Linux 内核提供的一个特性,用于限制和隔离一组进程对系统资源的使用,也即做进程的 QoS。控制的资源主要包括 cpu、内存、block + IO、网络带宽等。该特性自 2.6.24 开始进入内核主线,目前各大发行版都默认打开了该特性。

+

从实现角度,cgroup 实现了一个通用的进程分组框架,而不同类型资源的具体管理由各个 cgroup 子系统实现。截至内核 4.1,已经实现的子系统及其作用如下:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
子系统作用
devices设备权限控制
cpuset分配指定的 cpu 和内存节点
cpu控制 cpu 占用率
cpuacct统计 cpu 使用情况
memory限制内存使用上限
freezer冻结暂停 cgroup 中的进程
net_cls配合 tc(traffic controller)限制网络带宽
net_prio设置进程网络流量优先级
huge_tlb限制 huge_tlb 的使用
perf_event允许 Perf 工具基于 cgroup 分组做性能监测
+

cgroup 原生接口通过 cgroupfs 提供,类似于 procfs 和 sysfs,是一种虚拟文件系统。具体使用与分析参见《Docker 进阶与实战》。

+

namespace

+

namespace 是将内核的全局资源做封装,使每个 namespace 拥有独立的资源,进程在各自的 namespace 中对相同资源的使用不会互相干扰。比如主机名 hostname 作为全局资源,执行 + sethostname 系统调用会影响到其他进程;内核通过实现 UTS namespace,将不同进程分割在不同的 namespace 中,实现了隔离,一个 namespace 修改主机名不影响别的 + namespace。

+

目前内核实现了以下几种 namespace:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
namespace作用
IPC隔离 System V IPC 和 POSIX 消息队列
Network隔离网络资源
Mount隔离文件系统挂载点
PID隔离进程 ID
UTS隔离主机名和域名
User隔离用户 ID 与组 ID
+

对 namespace 的操作主要通过clone/setns/unshare三个系统调用来实现。详细的使用也不写了,没用过的东西就不全抄。记得读书和自己实验,补到这里。

+

文件系统

+

众所周知,docker 的文件系统是分层的,有镜像文件等一堆东西。文件系统分为若干层,在开启 docker 的时候会被联合挂载到同一个点下,作为 docker + 的根目录。这叫做联合挂载,即将多个目录和文件系统挂载到同一个目录下,其中可能有覆盖等。

+

docker 进程运行在宿主机的内核上,但是根文件系统又要用 docker 自己挂载的目录,且后来的进程也需要进入该目录。这里采用的技术是 pivot_root,该系统调用允许进程切换根目录。

+

在根目录挂载完成之后,docker 拉起一个初始 shell(正如 Linux-0.11 启动的时候也会有一个 shell 干活),这是 docker 中第一个进程,它调用 pivot_root + 切换根目录。在切换完成之后,当我们执行 docker exec 时,这是一个 docker 的新的进程,但该进程不再 pivot_root,而是打开第一个进程的 namespace,通过 setns + 系统调用,将自己的 namespace 设置为与其相同。由于 mnt 的 namespace 的存在,进程的根目录也就与第一个进程一样了。

+

书籍列表

+

毕业之前读完这些属实是有点难为人了,一个比一个硬,一次性啃完能给我门牙崩了;但是定点投放耗材市场之后,估计也不会有啥精力琢磨这些玩意了。能读一点是一点吧。

+

感觉自己现在已经染上班味了,绝症,没得治。

+
    +
  • SRE:Google 运维解密
  • +
  • Linker and Loader
  • +
  • 有空自己解析一下 ELF?
  • +
  • Docker 进阶与实战
  • +
  • containerd 原理剖析与实战
  • +
  • Linux 内核源码情景分析
  • +
  • LFS 网站,自己从软件包开始搭建 Linux
  • +
  • 构建嵌入式 Linux 系统
  • +
+

也许我应该把它们列入进阶版:

+
    +
  • gcc 技术大全
  • +
  • 黑客调试技术大全
  • +
+ +
+
+ + + \ No newline at end of file diff --git a/code/projects/godo.md b/code/projects/godo.md new file mode 100644 index 0000000..ec6e7f5 --- /dev/null +++ b/code/projects/godo.md @@ -0,0 +1,318 @@ +# 背景说明 + +本文档对[godo](https://git.qin-juan-ge-zhu.top/godo)编写过程中新了解到的技术、遇到的问题进行简要说明,以备所需。 + +# 系统调用 + +As is universually acknowledged, 操作系统、尤其是类 Unix 操作系统,以系统调用的形式对应用程序提供服务。系统调用是名称,有系统调用号与之对应(同一版本的内核在不同架构的 cpu 上,系统调用号可能不一样)。有的时候我们需要了解一些内核行为,但却不知道从何下手。可以通过查看内核源码来学习。 + +系统调用可以在源码中查找到。由于本项目使用的是 centos 7,内核版本 3.10.0-1160、cpu 为 x86-64 架构,兹以该版本内核为例说明。 + +要查看 fork 的系统调用号,查看`arch/x86/syscalls/syscall_64.tbl`。想要查看其具体的实现,则在源码根目录下**执行`grep -rInP "SYSCALL_DEFINE\d\(fork"`**,其中 SYSCALL_DEFINE+数字是 kernel 中定义的宏,展开即完整的函数声明。通过这种查找办法,我们可以快速地定位内核中对系统调用的处理函数,查看其工作原理。查看其他的内核相关内容也可以采取类似办法,即**先用 grep 定位大致范围、看都有什么地方用到,然后找到真正起作用的地方,读相关代码。** + +使用这些系统调用有两种办法: + +- 在 C 语言中直接调用同名函数,但大概率经过了 glibc 的封装 +- 手动封装。如下: + +```c +#include +#include +#include +#include +#include + +int main(){ + pid_t pid = syscall(SYS_fork); + // syscall是一个变参函数,第一个参数是系统调用号,接下来的是系统调用的各个参数 + // syscall定义在 unistd.h + // SYS_fork定义在 sys/syscall.h + if(pid == 0) { + printf("Child!\n"); + } else { + printf("Parent!\n"); + } + return 0; +} +``` + +这种封装方式与经常被用来当作 os 教材的 Linux-0.11/0.12 有所区别。Linux-0.11 环境上,unistd.h 大致如下: + +```c +#ifndef _UNISTD_H +#define _UNISTD_H + +... +#include +#include +#include +#include + +#ifdef __LIBRARY__ + +#define __NR_setup 0 /* used only by init, to get system going */ +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +... + +#define _syscall0(type,name) \ + type name(void) \ +{ \ +long __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name)); \ +if (__res >= 0) \ + return (type) __res; \ +errno = -__res; \ +return -1; \ +} + +#define _syscall1(type,name,atype,a) \ +type name(atype a) \ +{ \ +long __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" ((long)(a))); \ +if (__res >= 0) \ + return (type) __res; \ +errno = -__res; \ +return -1; \ +} + +... +#endif /* __LIBRARY__ */ +... + +#endif +``` + +可以看到,Linux-0.11 上,封装的一般方法为: + +```c +#define __LIBRARY__ // 一定要在unistd.h之前 +#include +#include + +syscall0(int, fork); // 宏替换后这就是个名为fork的函数的具体实现了 +int main() { + if(fork() == 0) { + printf("Child!\n"); + } else { + printf("Parent!\n"); + } + return 0; +} +``` + +但是无论如何,一般情况下不推荐手动封装,这不是 release 版该有的做法。 + +此外,从汇编代码来看,Linux-0.11 所用的 80386 芯片,不提供专门的系统调用指令,因而该系统使用的是`int 0x80`中断指令,通过注册中断处理函数进行对应处理;而**现代 x86 提供了专门的 syscall 指令**,Linux 系统直接用该指令进行系统调用。 + +## 系统调用中的进程与线程 + +一般地,在 Linux 系统上,我们以 pid 指代进程号,而进程可以有多个线程。很显然,真正被调度执行的单元应该是线程,换言之,**是 thread 而非 process 真正地对应着内核中 tasks 表里的一个 task,而每个 task 才具有独一无二的 id**。 + +### 常见系统调用的分析 + +看看这个: + +```c +extern int pthread_create (pthread_t *__restrict __newthread, + const pthread_attr_t *__restrict __attr, + void *(*__start_routine) (void *), + void *__restrict __arg) __THROWNL __nonnull ((1, 3)); +``` + +`pthread_create`函数的第一个参数,就是一个 pthread_t 类型的指针,处理后将 task 的 id 写到指针指向的区域。 + +让我们来看一段简单的代码: + +```c +// test.c +#include +#include +#include +#include +#include + +void *test(void *args) { + printf("Hello, I'm %d\n", getpid()); +} + +int main() { + pthread_t pthid; + int pid; + pthread_create(&pthid, NULL, test, NULL); + printf("main: thread %ld\n", pthid); + pthread_join(pthid, NULL); + if ((pid = fork()) == 0) { + printf("Hello, I'm %d\n", getpid()); + return 0; + } + printf("main: child process %d\n",pid); + if ((pid = syscall(SYS_fork)) == 0) { + printf("Hello, I'm %d\n", getpid()); + return 0; + } + printf("main: child process %d\n",pid); + return 0; +} +``` + +当我们使用`strace ./test`来查看上述代码时,会发现情况如下: + +```c +clone(child_stack=0x7f3dd28bbff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f3dd28bc9d0, tls=0x7f3dd28bc700, child_tidptr=0x7f3dd28bc9d0) = 21756 +write(1, "main: thread 139903502108416\n", 29) = 29 +clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f3dd308e9d0) = 21757 +--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21757, si_uid=1000, si_status=0, si_utime=0, si_stime=0} --- +write(1, "main: child process 21757\n", 26) = 26 +fork() = 21758 +--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21758, si_uid=1000, si_status=0, si_utime=0, si_stime=0} --- +write(1, "main: child process 21758\n", 26) = 26 +exit_group(0) = ? ++++ exited with 0 +++ +``` + +从这样的输出里,我们可以清晰地看到,**无论是`pthread_create`还是`fork`(指库函数),本质上都是封装了`clone`系统调用,即使 Linux 本身提供了专门的 fork 系统调用。**也许这是 glibc 和 Linux 都想在添加功能的基础上保证代码兼容性?花开两朵各表一枝了属于是。 + +这一结论也可以从 glibc 的代码中得到验证: + +```c +// 文件 glibc-2.18/nptl/sysdeps/unix/sysv/linux/pt-fork.c +pid_t +__fork (void) +{ + return __libc_fork (); +} +strong_alias (__fork, fork) + + +// 文件 glibc-2.18/nptl/sysdeps/unix/sysv/linux/fork.c +pid_t +__libc_fork (void) +{ + ... // 一堆不知所云的代码 +#ifdef ARCH_FORK + pid = ARCH_FORK (); +#else +# error "ARCH_FORK must be defined so that the CLONE_SETTID flag is used" + pid = INLINE_SYSCALL (fork, 0); +#endif + ... // 又是一堆不知所云的代码 +} + +// 文件 glibc-2.18/nptl/sysdeps/unix/sysv/linux/x86_64/fork.c +#define ARCH_FORK() \ + INLINE_SYSCALL (clone, 4, \ + CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 0, \ + NULL, &THREAD_SELF->tid) + +// 文件 glibc-2.18/sysdeps/unix/sysv/linux/x86_64/syscall.S + +/* Please consult the file sysdeps/unix/sysv/linux/x86-64/sysdep.h for + more information about the value -4095 used below. */ + .text +ENTRY (syscall) + movq %rdi, %rax /* Syscall number -> rax. */ + movq %rsi, %rdi /* shift arg1 - arg5. */ + movq %rdx, %rsi + movq %rcx, %rdx + movq %r8, %r10 + movq %r9, %r8 + movq 8(%rsp),%r9 /* arg6 is on the stack. */ + syscall /* Do the system call. */ + cmpq $-4095, %rax /* Check %rax for error. */ + jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */ + ret /* Return to caller. */ + +PSEUDO_END (syscall) +``` + +可以看到,fork 库函数实际上是掉入了`__libc_fork`,在经过各种处理之后,如果 glibc 中该平台的相关代码里定义了 ARCH_FORK 宏,则调用之;否则会直接调用`INLINE_SYSCALL`(这是 glibc 各个平台的代码里都有的宏);而如果直接调用`syscall`函数手动封装系统调用,则调用什么就是什么。`syscall`函数调用过程涉及延迟绑定等问题,就不是这里的重点了,而且我也没太搞明白,有机会单开一篇吧。 + +### 进程与线程 + +对于一个进程而言,它有很多线程,每个线程有一个号,但整个进程都有主线程的号,称为 tgid,只有一个 tgid 能真正地代表一个进程,而 pid 事实上是 task 的编号。 + +对于 netlink connector 而言,它听到的 fork 并不是 fork,而是 clone;对于 audit,也只能听到 clone 而听不到 fork。这是因为在内核中,fork 也是通过调用 clone 的处理函数来进行的。clone 创建的是一个 task,至于具体是进程还是线程,取决于用的 flag 参数,参见 manual 手册。 + +因而,无论使用 connector 还是 audit,拿到的都是 pid,只不过 connector 可以直接拿到 tgid、据此确定是进程还是线程,而 audit 只能拿到 pid,需要从 clone 的参数里查看是进程还是线程,且拿不到 tgid。这也就是我在项目中选择使用 connector 听进程消息的原因。 + +干巴巴说了这么多,其实就是想说,pid 也许在不同的语境下有不同含义。 + +# docker 使用的技术 + +## cgroup + +Linux 下用来控制进程资源的东西。没学明白,留缺。姑且抄点书上的内容来占个位置吧。 + +cgroup 是 control group 的简写,是 Linux 内核提供的一个特性,用于限制和隔离一组进程对系统资源的使用,也即做进程的 QoS。控制的资源主要包括 cpu、内存、block IO、网络带宽等。该特性自 2.6.24 开始进入内核主线,目前各大发行版都默认打开了该特性。 + +从实现角度,cgroup 实现了一个通用的进程分组框架,而不同类型资源的具体管理由各个 cgroup 子系统实现。截至内核 4.1,已经实现的子系统及其作用如下: + +| 子系统 | 作用 | +| ---------- | ----------------------------------------- | +| devices | 设备权限控制 | +| cpuset | 分配指定的 cpu 和内存节点 | +| cpu | 控制 cpu 占用率 | +| cpuacct | 统计 cpu 使用情况 | +| memory | 限制内存使用上限 | +| freezer | 冻结暂停 cgroup 中的进程 | +| net_cls | 配合 tc(traffic controller)限制网络带宽 | +| net_prio | 设置进程网络流量优先级 | +| huge_tlb | 限制 huge_tlb 的使用 | +| perf_event | 允许 Perf 工具基于 cgroup 分组做性能监测 | + +cgroup 原生接口通过 cgroupfs 提供,类似于 procfs 和 sysfs,是一种虚拟文件系统。具体使用与分析参见《Docker 进阶与实战》。 + +## namespace + +namespace 是将内核的全局资源做封装,使每个 namespace 拥有独立的资源,进程在各自的 namespace 中对相同资源的使用不会互相干扰。比如主机名 hostname 作为全局资源,执行 sethostname 系统调用会影响到其他进程;内核通过实现 UTS namespace,将不同进程分割在不同的 namespace 中,实现了隔离,一个 namespace 修改主机名不影响别的 namespace。 + +目前内核实现了以下几种 namespace: + +| namespace | 作用 | +| --------- | ----------------------------------- | +| IPC | 隔离 System V IPC 和 POSIX 消息队列 | +| Network | 隔离网络资源 | +| Mount | 隔离文件系统挂载点 | +| PID | 隔离进程 ID | +| UTS | 隔离主机名和域名 | +| User | 隔离用户 ID 与组 ID | + +对 namespace 的操作主要通过`clone/setns/unshare`三个系统调用来实现。详细的使用也不写了,没用过的东西就不全抄。记得读书和自己实验,补到这里。 + +### 文件系统 + +众所周知,docker 的文件系统是分层的,有镜像文件等一堆东西。文件系统分为若干层,在开启 docker 的时候会被联合挂载到同一个点下,作为 docker 的根目录。这叫做联合挂载,即将多个目录和文件系统挂载到同一个目录下,其中可能有覆盖等。 + +docker 进程运行在宿主机的内核上,但是根文件系统又要用 docker 自己挂载的目录,且后来的进程也需要进入该目录。这里采用的技术是 pivot_root,该系统调用允许进程切换根目录。 + +在根目录挂载完成之后,docker 拉起一个初始 shell(正如 Linux-0.11 启动的时候也会有一个 shell 干活),这是 docker 中第一个进程,它调用 pivot_root 切换根目录。在切换完成之后,当我们执行 docker exec 时,这是一个 docker 的新的进程,但该进程不再 pivot_root,而是打开第一个进程的 namespace,通过 setns 系统调用,将自己的 namespace 设置为与其相同。由于 mnt 的 namespace 的存在,进程的根目录也就与第一个进程一样了。 + +# 书籍列表 + +**毕业之前读完这些属实是有点难为人了,一个比一个硬,一次性啃完能给我门牙崩了;但是定点投放耗材市场之后,估计也不会有啥精力琢磨这些玩意了**。能读一点是一点吧。 + +感觉自己现在已经染上班味了,绝症,没得治。 + +- SRE:Google 运维解密 +- Linker and Loader +- 有空自己解析一下 ELF? +- Docker 进阶与实战 +- containerd 原理剖析与实战 +- Linux 内核源码情景分析 +- [LFS](https://www.linuxfromscratch.org/lfs/) 网站,自己从软件包开始搭建 Linux +- 构建嵌入式 Linux 系统 + +也许我应该把它们列入进阶版: + +- gcc 技术大全 +- 黑客调试技术大全 diff --git a/code/projects/lcm_compile.html b/code/projects/lcm_compile.html new file mode 100644 index 0000000..dbc8109 --- /dev/null +++ b/code/projects/lcm_compile.html @@ -0,0 +1,119 @@ + + + + + + + + LCM 交叉编译 + + + + + + +
+
+

LCM 交叉编译

+ +

LCM 简介

+

LCM(Lightweight Communications and + Marshalling)是一种轻量级的通信和编组库,是一种针对高带宽、低延迟、实时性要求高的场景下的通讯工具,用于在多个进程之间传递消息。LCM 的设计目标是提供一种简单的方法来传递结构化数据,而不需要复杂的 + API 或协议定义。LCM 的消息传递是基于发布/订阅模型的,发布者和订阅者之间通过一个中心化的消息传递系统进行通信。

+

在机器人和自动驾驶系统中,LCM 可作为 ROS 的替代品,借以完成进程间、设备间的通讯。

+

为了方便地在不同进程、不同设备间传递数据,我们在 OHOS 系统中集成了 LCM 模块。

+

参考文档:

+ +

依赖项安装

+
sudo apt update
+sudo apt upgrade
+sudo apt install build-essential cmake libglib2.0-dev
+sudo apt install openjdk-8-jdk # lcm仅支持jdk8
+

x86 的 lcm

+

如果我们仅仅需要 x86 架构的 lcm,执行下列命令即可:

+
git clone https://github.com/lcm-proj/lcm
+cd lcm
+mkdir build && cd build
+cmake ..
+make
+sudo make install
+

编译完成后,注意观察build目录下是否有lcm-java文件夹,如果没有,证明没有安装 java 或者 java 版本不对。

+

lcm-python

+

若想使用lcm-python,在lcm/lcm-python下执行sudo python3 setup.py install即可,使用时import python。 +

+

lcm-spy 的使用

+

lcm-spy 是 LCM 配套的数据可视化工具。 可以通过 lcm-spy 监视 lcm 数据发送频率、数据量、数据结构,甚至实时画图。 如果安装了 JAVA,则 lcm-spy 会连同 LCM 一同被安装。 +

+

启动 lcm-spy:

+
export CLASSPATH=${your lcm-type class path}
+lcm-spy
+

交叉编译

+

既然 lcm 用的是 cmake,何不交叉编译到 OHOS 里用呢?

+

首先,我们可以设置一下 Native Development Kit(NDK)的路径:

+
# 设置成自己的NDK工具目录
+export OHOS_ROOT=~/app/native
+
$OHOS_ROOT/build-tools/cmake/bin/cmake \
+      -DOHOS_STL=c++_shared \
+      -DOHOS_ARCH=armv8-a \
+      -DOHOS_PLATFORM=OHOS \
+      -DCMAKE_TOOLCHAIN_FILE=$(find $OHOS_ROOT -name "ohos.toolchain.cmake")
+

不出意外的话,就要出意外了。果不其然,报错Could NOT fid Glib2。思考之后,我们认为,问题出现在 lcm 所依赖的 glib 库上:在 x86 + 架构上,我们直接向系统安装了libglib2.0-dev,而 lcm 编译时直接从系统里找到了这个库;但当交叉编译时,由于 OHOS 交叉编译工具未提供 + libglib2.0-dev,lcm 找遍了系统也没找到 arm 架构下的这个库,因而直接报错。

+

解决方案自然是自己手动交叉编译libglib2.0-dev

+

Glib2.0 交叉编译

+

官网下载 glib-2.79.2 源码,解压后进入源码目录。

+
mkdir glib && cd glib
+wget https://download.gnome.org/sources/glib/2.79/glib-2.79.2.tar.xz
+tar -xvf glib-2.79.2.tar.xz
+mkdir build
+

这里需要注意的是:

+
    +
  • 虽然 glib 编译需要meson,但它并不能直接交叉编译,OHOS 也并未提供用于交叉编译的meson
  • +
  • meson要求保证源码目录与构建目录不能相同,以保证构建过程的干净,因此我们在源码目录外新建了一个build目录。
  • +
+

接着,参考博客这篇博客,撰写如下meson_ohos.txt(友情提醒,使用该文件时把native也就是 + NDK 的路径修改为自己的路径、源平台与目标平台按需修改):

+
[binaries]
+c = '$OHOS_ROOT/llvm/bin/aarch64-unknown-linux-ohos-clang'
+cpp = '$OHOS_ROOT/llvm/bin/aarch64-unknown-linux-ohos-clang++'
+ar = '$OHOS_ROOT/llvm/bin/llvm-ar'
+strip = '$OHOS_ROOT/llvm/bin/llvm-strip'
+ld = '$OHOS_ROOT/llvm/bin/llvm-link'
+
+[properties]
+skip_sanity_check = true
+sys_root = '$OHOS_ROOT/sysroot'
+c_args = ['-L$OHOS_ROOT/sysroot/usr/lib/aarch64-linux-ohos']
+
+[host_machine]
+system = 'linux'
+cpu_family = 'aarch64'
+cpu = 'aarch64'
+endian = 'little'
+
+[target_machine]
+system = 'ohos'
+cpu_family = 'aarch64'
+cpu = 'armv8a'
+endian = 'little'
+

将该文件放置在glib-2.79.2源码目录下,而后在build目录下执行以下命令:

+
meson --cross-file=../glib-2.79.2/meson_ohos.txt ..
+ninja
+

编译完成,正确地安装 glib2 到交叉编译工具链中(???),之后我们回到 lcm 的源码目录,重新执行 cmake 命令:

+
$OHOS_ROOT/build-tools/cmake/bin/cmake \
+      -DOHOS_STL=c++_shared \
+      -DOHOS_ARCH=armv8-a \
+      -DOHOS_PLATFORM=OHOS \
+      -DCMAKE_TOOLCHAIN_FILE=$(find $OHOS_ROOT -name "ohos.toolchain.cmake")
+

这次,编译成功了。万岁!

+ +
+
+ + + \ No newline at end of file diff --git a/code/projects/lcm_compile.md b/code/projects/lcm_compile.md new file mode 100644 index 0000000..8f34297 --- /dev/null +++ b/code/projects/lcm_compile.md @@ -0,0 +1,141 @@ + + +# LCM 简介 + +LCM(Lightweight Communications and Marshalling)是一种轻量级的通信和编组库,是一种针对高带宽、低延迟、实时性要求高的场景下的通讯工具,用于在多个进程之间传递消息。LCM 的设计目标是提供一种简单的方法来传递结构化数据,而不需要复杂的 API 或协议定义。LCM 的消息传递是基于发布/订阅模型的,发布者和订阅者之间通过一个中心化的消息传递系统进行通信。 + +在机器人和自动驾驶系统中,LCM 可作为 ROS 的替代品,借以完成进程间、设备间的通讯。 + +为了方便地在不同进程、不同设备间传递数据,我们在 OHOS 系统中集成了 LCM 模块。 + +参考文档: + +- [LCM 官方文档](https://lcm-proj.github.io/) +- [一篇博客](https://zhuanlan.zhihu.com/p/621943685) + +# 依赖项安装 + +```bash +sudo apt update +sudo apt upgrade +sudo apt install build-essential cmake libglib2.0-dev +sudo apt install openjdk-8-jdk # lcm仅支持jdk8 +``` + +# x86 的 lcm + +如果我们仅仅需要 x86 架构的 lcm,执行下列命令即可: + +```bash +git clone https://github.com/lcm-proj/lcm +cd lcm +mkdir build && cd build +cmake .. +make +sudo make install +``` + +编译完成后,注意观察build目录下是否有lcm-java文件夹,如果没有,证明没有安装 java 或者 java 版本不对。 + +## lcm-python + +若想使用lcm-python,在lcm/lcm-python下执行`sudo python3 setup.py install`即可,使用时`import python`。 + +## lcm-spy 的使用 + +lcm-spy 是 LCM 配套的数据可视化工具。 +可以通过 lcm-spy 监视 lcm 数据发送频率、数据量、数据结构,甚至实时画图。 +如果安装了 JAVA,则 lcm-spy 会连同 LCM 一同被安装。 + +启动 lcm-spy: + +```bash +export CLASSPATH=${your lcm-type class path} +lcm-spy +``` + +# 交叉编译 + +既然 lcm 用的是 cmake,何不交叉编译到 OHOS 里用呢? + +首先,我们可以设置一下 Native Development Kit(NDK)的路径: + +```bash +# 设置成自己的NDK工具目录 +export OHOS_ROOT=~/app/native +``` + +```bash +$OHOS_ROOT/build-tools/cmake/bin/cmake \ + -DOHOS_STL=c++_shared \ + -DOHOS_ARCH=armv8-a \ + -DOHOS_PLATFORM=OHOS \ + -DCMAKE_TOOLCHAIN_FILE=$(find $OHOS_ROOT -name "ohos.toolchain.cmake") +``` + +不出意外的话,就要出意外了。果不其然,报错Could NOT fid Glib2。思考之后,我们认为,问题出现在 lcm 所依赖的 glib 库上:在 x86 架构上,我们直接向系统安装了`libglib2.0-dev`,而 lcm 编译时直接从系统里找到了这个库;但当交叉编译时,由于 OHOS 交叉编译工具未提供 libglib2.0-dev,lcm 找遍了系统也没找到 arm 架构下的这个库,因而直接报错。 + +解决方案自然是自己手动交叉编译`libglib2.0-dev`。 + +## Glib2.0 交叉编译 + +从[官网](https://download.gnome.org/sources/glib/)下载 glib-2.79.2 源码,解压后进入源码目录。 + +```bash +mkdir glib && cd glib +wget https://download.gnome.org/sources/glib/2.79/glib-2.79.2.tar.xz +tar -xvf glib-2.79.2.tar.xz +mkdir build +``` + +这里需要注意的是: + +- 虽然 glib 编译需要`meson`,但它并不能直接交叉编译,OHOS 也并未提供用于交叉编译的`meson`。 +- `meson`要求保证源码目录与构建目录不能相同,以保证构建过程的干净,因此我们在源码目录外新建了一个`build`目录。 + +接着,参考博客[这篇博客](https://t.csdnimg.cn/YfSJC),撰写如下meson_ohos.txt(友情提醒,使用该文件时把native也就是 NDK 的路径修改为自己的路径、源平台与目标平台按需修改): + +```meson +[binaries] +c = '$OHOS_ROOT/llvm/bin/aarch64-unknown-linux-ohos-clang' +cpp = '$OHOS_ROOT/llvm/bin/aarch64-unknown-linux-ohos-clang++' +ar = '$OHOS_ROOT/llvm/bin/llvm-ar' +strip = '$OHOS_ROOT/llvm/bin/llvm-strip' +ld = '$OHOS_ROOT/llvm/bin/llvm-link' + +[properties] +skip_sanity_check = true +sys_root = '$OHOS_ROOT/sysroot' +c_args = ['-L$OHOS_ROOT/sysroot/usr/lib/aarch64-linux-ohos'] + +[host_machine] +system = 'linux' +cpu_family = 'aarch64' +cpu = 'aarch64' +endian = 'little' + +[target_machine] +system = 'ohos' +cpu_family = 'aarch64' +cpu = 'armv8a' +endian = 'little' +``` + +将该文件放置在glib-2.79.2源码目录下,而后在build目录下执行以下命令: + +```bash +meson --cross-file=../glib-2.79.2/meson_ohos.txt .. +ninja +``` + +编译完成,正确地安装 glib2 到交叉编译工具链中(???),之后我们回到 lcm 的源码目录,重新执行 cmake 命令: + +```bash +$OHOS_ROOT/build-tools/cmake/bin/cmake \ + -DOHOS_STL=c++_shared \ + -DOHOS_ARCH=armv8-a \ + -DOHOS_PLATFORM=OHOS \ + -DCMAKE_TOOLCHAIN_FILE=$(find $OHOS_ROOT -name "ohos.toolchain.cmake") +``` + +这次,编译成功了。万岁! diff --git a/code/projects/ohos_compile.html b/code/projects/ohos_compile.html new file mode 100644 index 0000000..bf72035 --- /dev/null +++ b/code/projects/ohos_compile.html @@ -0,0 +1,332 @@ + + + + + + + + OpenHarmony 3.2 编译 + + + + + + +
+
+
+

OpenHarmony 3.2 编译

+
+ +

[toc]

+
+

本次编译环境搭建参考了以下博客:

+ +
+

环境与依赖

+

硬件环境

+

编译过程使用的是 + Ubuntu,经测试,Ubuntu 22.04 LTS/Ubuntu 20.04 LTS/Ubuntu 18.04 LTS + 均可用。

+

网上的大多数博客里没有对编译的硬件限制作出说明,现根据我们的情况,给出一个大概的范围:

+
    +
  • 内存必须在 10G 以上,我使用的是 13G,编译成功(8G 时编译失败了,下文会说明)
  • +
  • 磁盘 160G 或以上 +
      +
    • 源码大小就 36G 左右了
    • +
    • 大量的依赖会占据数十 G 的空间
    • +
    • 我用的是 160G 磁盘,编译成功,但基本不剩什么空间了
    • +
    +
  • +
  • 注意所需要的ohos 的版本、cpu 版本、位数等信息
  • +
+

这里我用的是 VMWare+Ubuntu22.04 虚拟机。

+

软件包依赖

+

安装依赖在博客中都有讲,但少了一部分依赖项,我因而将所有必要的依赖项整理到一个脚本里了:

+
#!/bin/bash
+set -e # 一旦出错立刻停止执行,不会执行后续指令
+
+# 更新软件源
+sudo apt update
+sudo apt upgrade
+
+# 官方博客说明的依赖项
+sudo apt install binutils git git-lfs gnupg flex \
+    bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib \
+    libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev ccache \
+    libgl1-mesa-dev libxml2-utils xsltproc unzip m4 bc gnutls-bin \
+    python3-pip ruby libtinfo-dev libtinfo5 \
+
+# 官方博客未说明,但安装过程中报缺失的依赖项
+sudo apt install openjdk-8-jdk libssl-dev libelf-dev default-jdk \
+    genext2fs u-boot-tools mtd-utils scons gcc-arm-none-eabi \
+    liblz4-tool
+
+# 别的博客说明的依赖项
+sudo apt install device-tree-compiler lib32stdc++6 lib32z1 libncurses5-dev lib32ncurses6
+
+# 检查是否存在python3,如不存在则为之安装
+if ! [ -x "$(command -v python3)" ]; then
+    sudo apt install python3
+fi
+# 是否有python,如没有则建立软连接到python3的位置
+if ! [ -x "$(command -v python)" ]; then
+    sudo ln -s $(which python3) /usr/bin/python
+fi
+

这里需要注意的是,如果安装过程中出现报错,在修改之后,必须重新执行整个脚本,因为在 apt 发现找不到某个软件包之后,后续的包与命令都不会执行。

+

配置 git

+

git 基础设置

+

在上边我们已经下载了本次所需要的gitgit-lfs,接下来我们需要对git进行基础设置。

+
# 设置用户名与密码,新用户必做
+# 这里的用户名与密码只是一个写在git提交记录中的标识,可以与gitee/github账号无关
+git config --global user.name "yourname"
+git config --global user.email "your-email-address"
+# git凭证缓存,必做
+git config --global credential.helper store
+# git默认的文本编辑器是nano,我一般喜欢改为vim,选做
+git config --global core.editor vim
+

gitee 帐户

+

由于 OpenHarmony 的源码托管在 gitee 上,所以我们需要在 gitee 上注册一个帐户。注册的方法在此不复赘述。

+

本次编译过程中,我们并不需要提交代码,因而可以仅通过 http 方式从 gitee 下载源码——如果是这样的话,就不需要进行 git 与 gitee + 关联;如果想要提交代码,那么需要将本地的 git 与 gitee 账号关联起来,具体操作如下:

+
# 在本地执行
+ssh-keygen -t rsa -C "your-email-address"
+

命令执行后,每次需要输入都直接回车,连续有三个回车,执行就会结束,ssh + 密钥对的公钥和私钥分别保存在~/.ssh/id_rsa.pub~/.ssh/id_rsa中。

+

在 gitee 用户的设置界面,将公钥id_rsa.pub的内容复制到 gitee 的 SSH 公钥中,保存即可。

+

此时,我们就可以通过 ssh 的方式与 gitee 进行交互、也可以免密提交代码了。

+

系统源码编译

+

配置 repo 工具

+

注意:前两条命令需要以 root 身份执行!!!

+
# 以下两条命令需要以root身份执行
+sudo -s
+curl -s https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > /usr/local/bin/repo
+chmod a+x /usr/local/bin/repo
+exit
+
+# 这条普通身份也可以
+pip3 install -i https://repo.huaweicloud.com/repository/pypi/simple requests
+

之所以前两条命令需要以 root 身份而不能是 sudo,是因为/usr/local/bin是一个只有 root 用户才有写权限的目录,而 sudo 命令虽然是以 root + 身份执行,但重定向时候 sudo 用的也是当前用户身份,权限不足,自然报错。(如果是管道,那么 sudo + 也是只对当前命令有效,而不是对后续的整个管道有效。

+

获取源码

+

通过 repo + https/ssh 下载:

+
# 通过http下载,这里需要注意你需要的ohos的版本
+repo init -u https://gitee.com/openharmony/manifest.git -b master --no-repo-verify
+# 如果需要的是特定分支,-b后边改成对应分支名
+# 如果是tag,-b后的参数比较复杂,要在网页上提前确定好需要的tag名字,
+# 如下载的是tag为OpenHarmony-v3.2-Release的版本,命令如下:
+# repo init -u https://gitee.com/openharmony/manifest -b refs/tags/OpenHarmony-v3.2-Release --no-repo-verify
+# 除使用https外,也可以通过ssh下载
+# repo init -u git@gitee.com:openharmony/manifest.git -b master --no-repo-verify
+
+repo sync -c
+repo forall -c 'git lfs pull'
+

prebuilts 与编译

+
# 先在源码根目录下执行脚本,安装编译器及二进制工具
+bash build/prebuilts_download.sh
+# 再执行如下命令进行版本编译
+# 注意:默认编译的时候,目标cpu是32位,即使为64位cpu也无法使用64位功能
+sudo ./build.sh --product-name rk3568 --ccache
+# 如果是64位cpu,需要加上--target-cpu=arm64
+sudo ./build.sh --product-name rk3568 --ccache --target-cpu=arm64
+

编译完成

+

编译所生成的文件都归档在 out 目录下,结果镜像输出在源码根目录下的out/rk3568/packages/phone/images目录下。

+

自此源码编译成功,即可进行镜像烧录。

+

编译报错解决记录

+

以下记录编译过程中出现的一些出现过的报错、坑点和注意事项,以为后来者鉴。

+

ssh 下载源码报错

+

在使用 ssh + 下载时,第一步repo init -u git@gitee.com:openharmony/manifest.git -b master --no-repo-verify,报错: +

+
Warning: Permanently added 'gitee.com,180.76.198.77' (ECDSA) to the list of known hosts.
+

经检查,发现 gitee 邮箱设置与公钥中邮箱存在差异,修改 gitee 邮箱设置后成功。

+

设备磁盘空间不足

+

编译过程中异常终止,并显示“设备上没有空间”。

+

解决方法:

+
    +
  • VMWare中扩大磁盘空间 +
      +
    • 关闭虚拟机
    • +
    • VMWare上方菜单栏中依次选择虚拟机->设置->硬件->硬盘->扩展,然后输入新的磁盘大小
    • +
    • 点击确定后会提示,说需要在虚拟机中扩展分区,才能使用新的磁盘空间
    • +
    +
  • +
  • 打开虚拟机,并扩展分区 +
      +
    • 在命令行中输入sudo apt install gparted,安装gparted分区工具
    • +
    • 打开该工具,选择需要扩展的分区(一般为挂载到根目录/的那个分区),点击Resize/Move,然后拖动分区大小,点击Resize/Move,再点击Apply,等待分区扩展完成 +
    • +
    +
  • +
+

如果在虚拟机里用 gparted 进行分区扩展时,弹窗说“分区被挂载为只读,无法调整大小”。此时我们需要:

+
    +
  • 右键该分区,查看挂载到什么位置了。一般是挂载到//var/snap/firefox/common/host-hunspell
  • +
  • 执行以下命令:
  • +
+
# 对于这个设备挂载到的每一个分区,都执行以下命令,以此类推
+sudo mount -o remount -rw /
+sudo mount -o remount -rw /var/snap/firefox/common/host-hunspell
+

而后在 gparted 中点击gparted->刷新设备,即可进行分区扩展。

+

一个奇怪的报错——GN Failed

+

这是一个很奇怪的报错,我们尚未找到真正的原因所在,只知道如何暂时地绕过去。

+

问题描述与复现

+

问题复现方法为在编译过程中,执行以下命令:

+
./build.sh --product-name rk3568 --ccache
+

该命令是指导书中写的编译命令,但执行时候会出错;与上文所述的经确认无误的编译命令不同在于没加sudo

+

如果按照指导书的命令来,不出意外编译将会报错: +

+

以下是该问题的具体描述:

+

build.log如下:

+
Set cache size limit to 100.0 GB
+[OHOS INFO] loader args:['platforms_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/platforms.build"', 'subsystem_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/subsystem_config.json"', 'example_subsystem_file=""', 'exclusion_modules_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/exclusion_modules.json"', 'source_root_dir="/home/axiomer/桌面/"', 'gn_root_out_dir="out/sdk"', 'build_platform_name=phone', 'build_xts=False', 'load_test_config=False', 'target_os=ohos', 'target_cpu=arm64', 'os_level=standard', "ignore_api_check=['xts', 'common', 'testfwk']", 'scalable_build=False', 'skip_partlist_check=False']
+[OHOS INFO] Excuting gn command: /home/axiomer/桌面/prebuilts/build-tools/linux-x86/bin/gn gen --args="product_name=\"ohos-sdk\" product_path=\"/home/axiomer/桌面/productdefine/common/products\" product_config_path=\"/home/axiomer/桌面/productdefine/common/products\" device_name=\"sdk\" device_path=\"/home/axiomer/桌面/device/board/ohos/sdk\" device_company=\"ohos\" device_config_path=\"/home/axiomer/桌面/device/board/ohos/sdk\" target_cpu=\"arm64\" is_standard_system=true ohos_build_compiler_specified=\"\" ohos_build_time=1707374188101 ohos_build_datetime=\"2024-02-08 22:36:28\" build_ohos_sdk=true build_ohos_ndk=true ohos_build_enable_ccache=true ohos_build_type=\"debug\" device_type=\"default\" build_variant=\"root\" use_thin_lto=false ndk_platform=\"linux\" sdk_for_hap_build=true skip_generate_module_list_file=true enable_lto_O0=true archive_ndk=false enable_ndk_doxygen=false use_cfi=false sdk_check_flag=false sdk_platform=\"linux\" root_perf_main=\"main\" runtime_mode=\"release\"" --args=product_name="ohos-sdk" product_path="/home/axiomer/桌面/productdefine/common/products" product_config_path="/home/axiomer/桌面/productdefine/common/products" device_name="sdk" device_path="/home/axiomer/桌面/device/board/ohos/sdk" device_company="ohos" device_config_path="/home/axiomer/桌面/device/board/ohos/sdk" target_cpu="arm64" is_standard_system=true ohos_build_compiler_specified="" ohos_build_time=1707374188101 ohos_build_datetime="2024-02-08 22:36:28" build_ohos_sdk=true build_ohos_ndk=true ohos_build_enable_ccache=true ohos_build_type="debug" device_type="default" build_variant="root" use_thin_lto=false ndk_platform="linux" sdk_for_hap_build=true skip_generate_module_list_file=true enable_lto_O0=true archive_ndk=false enable_ndk_doxygen=false use_cfi=false sdk_check_flag=false sdk_platform="linux" root_perf_main="main" runtime_mode="release" /home/axiomer/桌面/out/sdk
+ERROR at //build/config/BUILDCONFIG.gn:92:15: Could not read file.
+    read_file("${preloader_output_dir}/build_config.json", "json")
+              ^------------------------------------------
+I resolved this to "/home/axiomer/桌面/out/preloader/build_config.json".
+root_out_dir=//out/sdk
+root_build_dir=//out/sdk
+root_gen_dir=//out/sdk/gen
+current_toolchain=
+[91m[OHOS ERROR][0m Traceback (most recent call last):
+[91m[OHOS ERROR][0m   File "/home/axiomer/桌面/build/hb/containers/status.py", line 47, in wrapper
+[91m[OHOS ERROR][0m     return func(*args, **kwargs)
+[91m[OHOS ERROR][0m   File "/home/axiomer/桌面/build/hb/services/gn.py", line 197, in _execute_gn_gen_cmd
+[91m[OHOS ERROR][0m     SystemUtil.exec_command(gn_gen_cmd, self.config.log_path)
+[91m[OHOS ERROR][0m   File "/home/axiomer/桌面/build/hb/util/system_util.py", line 64, in exec_command
+[91m[OHOS ERROR][0m     LogUtil.get_failed_log(log_path)
+[91m[OHOS ERROR][0m   File "/home/axiomer/桌面/build/hb/util/log_util.py", line 191, in get_failed_log
+[91m[OHOS ERROR][0m     LogUtil.get_gn_failed_log(log_path)
+[91m[OHOS ERROR][0m   File "/home/axiomer/桌面/build/hb/util/log_util.py", line 137, in get_gn_failed_log
+[91m[OHOS ERROR][0m     raise OHOSException(
+[91m[OHOS ERROR][0m exceptions.ohos_exception.OHOSException: GN Failed! Please check error in /home/axiomer/桌面/out/sdk/error.log, and for more build information in /home/axiomer/桌面/out/sdk/build.log
+[91m[OHOS ERROR][0m
+[91m[OHOS ERROR][0m Code:        3000
+[91m[OHOS ERROR][0m
+[91m[OHOS ERROR][0m Reason:      GN Failed! Please check error in /home/axiomer/桌面/out/sdk/error.log, and for more build information in /home/axiomer/桌面/out/sdk/build.log
+[91m[OHOS ERROR][0m
+[91m[OHOS ERROR][0m Error Type:  UNKNOWN
+[91m[OHOS ERROR][0m
+[91m[OHOS ERROR][0m Description: An unknown error occurred while executing 'gn gen'.
+[91m[OHOS ERROR][0m
+[91m[OHOS ERROR][0m Solution:    There is no solution available. You can check the 'gn_error.log' in the output directory for more information
+[91m[OHOS ERROR][0m
+

报错信息中提到ERROR at //build/config/BUILDCONFIG.gn:92:15: Could not read file.,但是经过检查文件,存在名为BUILDCONFIG.gn的文件,且开放可读权限;但报错信息中显示无法读该文件。 +

+

失败尝试

+

在此,我进行了许多尝试,但都无一例外失败了;以下是我的尝试点,或许会有帮助:

+
    +
  • 根据一个相似提问,对相关依赖进行安装,失败了:
  • +
  • 根据报错信息,在编译命令中添加--no-prebuilt-sdk选项对 ohos-sdk 的构建进行跳过,依然失败
  • +
+

尝试未果 T^T

+

最终方案

+

报错信息如下所示:

+

在上述报错信息中提到Permission denied,即权限不够;于是尝试添加sudo,居然跑通了!!!!

+

在指导书的编译指令./build.sh --product-name rk3568 --ccache前添加权限设置sudo,即指令更改为:

+
sudo ./build.sh --product-name rk3568 --ccache
+

不知道为什么,编译过程中始终没有向我提出权限要求,但是不加sudo开权限就是跑不通,很奇怪 @ _ @也许是 OHOS 内敛不好意思申请权限呢……

+

+ 另一个奇怪的报错——FAILED: load BTF from vmlinux: Unknown error -22

+

关于这个报错,当时的错误日志、运行日志、聊天截图均已丢失,但时日不久,记忆还算清楚,加上这个报错困扰了我好些天,因而必须记录。

+

问题简要描述

+

正常按照sudo ./build.sh --product-name rk3568 --ccache编译时,报错,主要报错信息如下:

+
die__process_unit: DW_TAG_label (0xa) @ <0x3adc> not handled!
+die__process_unit: DW_TAG_label (0xa) @ <0x3bdc> not handled!
+die__process_unit: DW_TAG_label (0xa) @ <0x3bef> not handled!
+die__process_unit: DW_TAG_label (0xa) @ <0x3ce5> not handled!
+die__process_unit: DW_TAG_label (0xa) @ <0x3cff> not handled!
+die__process_unit: DW_TAG_label (0xa) @ <0x3d19> not handled!
+Killed
+  LD      .tmp_vmlinux.kallsyms1
+  KSYMS   .tmp_vmlinux.kallsyms1.S
+  AS      .tmp_vmlinux.kallsyms1.S
+  LD      .tmp_vmlinux.kallsyms2
+  KSYMS   .tmp_vmlinux.kallsyms2.S
+  AS      .tmp_vmlinux.kallsyms2.S
+  LD      ymlinux
+  BTEIDS  vmlinux
+FAILED: load BTF from vmlinux: Unknown error -22make[2]: *** [/home/player/harmony/out/kernel/src_tmp/linux-5.19/
+Makefile:1225: vmlinux]错误 255
+make[1]: *** [arch/arm64/Makefile:208: rk3568-toybrick-x0-linux.img]错误2
+make[1]: 离开目录"/home/player/harmony/out/kernel/OBJ/linux-5.10"
+make: *** [Makefile:192:__sub-make]错误 2
+

可以看到,报错的关键点在于FAILED: load BTF from vmlinux: Unknown error -22这里。查看 Makefile 对应报错位置:

+
vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE
+    +$(call if_changed,link-vmlinux)
+
+targets := vmlinux
+

解释

+
    +
  • 这段 makefile 的实际功能是链接一个虚拟 linux +
      +
    • 通过目录位置,我们可以合理猜测,这里是在进行linux 内核编译,但失败了
    • +
    +
  • +
  • 报错中说的BTF则是数据格式 +
      +
    • 一般出现load BTF from xxx错误时,是由于系统不一致导致的(也就是不同系统 BTF 格式不一样,出现了识别错误)
    • +
    +
  • +
  • 最重要的是,Unknown error -22 这个报错码,搜不到任何信息!
  • +
+

因而,我尝试了重装系统,将我的 Ubuntu22.04 重装为 20.04,但最后仍然会出现这个错误,足以证明不是系统差异导致的。

+

解决

+

最终,我在第二篇参考博客里找到了答案:编译较新的 + Linux 内核时,至少需要 10G+内存,而我一贯把虚拟机内存只设置为 8G。

+

令人好奇的是,为什么需要这么大内存?按理来说在虚拟内存空间里,物理内存不够的时候,不应该是由 OS 出面进行内存与磁盘的页面调度吗?我认为,可能是在编译过程中,其他进程占据一部分内存,而这里在链接一个虚拟 + linux 文件,可能文件体积过大而尚未创建完成,所有页面一直驻留在内存中,导致内存崩溃。

+

总而言之,言而总之,在VMware->虚拟机->设置->硬件->内存中,把内存扩大,就可以完美解决该问题。经我的测试,内存 13.2G(主机总内存 16G + 时候推荐的最大虚拟机内存)是能编译完成的,编译时长 6h。最终解决。

+

ohos 的 NDK

+

NDK 编译方式比较简单,在源码根目录下执行如下命令:

+
# 安装依赖
+./build/build_scripts/env_setup.sh
+
+# 执行完上述命令后记得执行source ~/.bashrc或者重启终端
+source ~/.bashrc
+
+# 安装编译SDK需要的依赖包(编译镜像的时候是不依赖这些包的)
+sudo apt-get install libxcursor-dev libxrandr-dev libxinerama-dev
+
+./build.sh --product-name ohos-sdk --ccache --build-target ohos_ndk
+

编译出来的 NDK 在out/sdk/packages/ohos-sdk/linux/native下。当然同时也有 windows 版本的 NDK,你猜在哪里?

+

将编译出来的 NDK 的 zip 解压到你想要的目录下,然后将该目录添加到环境变量中,即可使用。

+

注意,NDK 包提供的交叉编译工具是 cmake 和 ninja,编译器是 clang 和 clang++,并没有我们熟悉的 gcc/g++和 make。除此之外,NDK + 还未我们提供编译所需的全套服务,如编译工具链配置文件ohos.toolchain.cmake、头文件、库文件等。快说,谢谢 ohos~

+

完结撒花

+

本次鸿蒙开发环境的搭建过程可谓一波三折,总结几个最大的坑点,或许可以作为编译的经验罢:

+
    +
  • 有报错,试试添加权限!
  • +
  • 贫贱程序猿百事哀,编译 OpenHarmony 系统需要足够的硬件配置 +
      +
    • 内存 10G+
    • +
    • 磁盘 160G+
    • +
    +
  • +
+

不说了,抓紧攒点钱开学升级电脑配置要紧……磁盘快炸了……

+

2024.2.12,甲辰年正月初三
2024.4.24改

+ +
+
+ + + \ No newline at end of file diff --git a/code/projects/ohos_compile.md b/code/projects/ohos_compile.md new file mode 100644 index 0000000..150ee25 --- /dev/null +++ b/code/projects/ohos_compile.md @@ -0,0 +1,420 @@ + + +[toc] + +> 本次编译环境搭建参考了以下博客: +> +> - [HiHope_DAYU200/开发环境搭建编译指南](https://gitee.com/hihope_iot/docs/blob/master/HiHope_DAYU200/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E7%BC%96%E8%AF%91%E6%8C%87%E5%8D%97.md) +> - [基于 Ubuntu 20.04 配置 OpenHarmony 开发环境](https://juejin.cn/post/7257553293889634363) +> - [OpenHarmony 源码编译步骤(基于 3.2 beta3)](https://blog.csdn.net/jwq1220/article/details/127303546?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127303546%22%2C%22source%22%3A%222401_82617925%22%7D&fromshare=blogdetail) +> - [FAILED: load BTF from vmlinux: Unknown error -2](https://unix.stackexchange.com/questions/616392/failed-load-btf-from-vmlinux-unknown-error-2make-makefile1162-vmlinu) + +# 环境与依赖 + +## 硬件环境 + +编译过程使用的是 `Ubuntu`,经测试,`Ubuntu 22.04 LTS`/`Ubuntu 20.04 LTS`/`Ubuntu 18.04 LTS` 均可用。 + +网上的大多数博客里没有对编译的硬件限制作出说明,现根据我们的情况,给出一个大概的范围: + +- **内存必须在 10G 以上**,我使用的是 13G,编译成功(8G 时编译失败了,下文会说明) +- **磁盘 160G 或以上** + - 源码大小就 36G 左右了 + - 大量的依赖会占据数十 G 的空间 + - 我用的是 160G 磁盘,编译成功,但基本不剩什么空间了 +- 注意所需要的**ohos 的版本、cpu 版本、位数等信息** + +这里我用的是 VMWare+Ubuntu22.04 虚拟机。 + +## 软件包依赖 + +安装依赖在博客中都有讲,但**少了一部分依赖项**,我因而将所有必要的依赖项整理到一个脚本里了: + +```bash +#!/bin/bash +set -e # 一旦出错立刻停止执行,不会执行后续指令 + +# 更新软件源 +sudo apt update +sudo apt upgrade + +# 官方博客说明的依赖项 +sudo apt install binutils git git-lfs gnupg flex \ + bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib \ + libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev ccache \ + libgl1-mesa-dev libxml2-utils xsltproc unzip m4 bc gnutls-bin \ + python3-pip ruby libtinfo-dev libtinfo5 \ + +# 官方博客未说明,但安装过程中报缺失的依赖项 +sudo apt install openjdk-8-jdk libssl-dev libelf-dev default-jdk \ + genext2fs u-boot-tools mtd-utils scons gcc-arm-none-eabi \ + liblz4-tool + +# 别的博客说明的依赖项 +sudo apt install device-tree-compiler lib32stdc++6 lib32z1 libncurses5-dev lib32ncurses6 + +# 检查是否存在python3,如不存在则为之安装 +if ! [ -x "$(command -v python3)" ]; then + sudo apt install python3 +fi +# 是否有python,如没有则建立软连接到python3的位置 +if ! [ -x "$(command -v python)" ]; then + sudo ln -s $(which python3) /usr/bin/python +fi +``` + +这里需要注意的是,**如果安装过程中出现报错,在修改之后,必须重新执行整个脚本**,因为在 apt 发现找不到某个软件包之后,后续的包与命令都不会执行。 + +# 配置 git + +## git 基础设置 + +在上边我们已经下载了本次所需要的`git`与`git-lfs`,接下来我们需要对`git`进行基础设置。 + +```bash +# 设置用户名与密码,新用户必做 +# 这里的用户名与密码只是一个写在git提交记录中的标识,可以与gitee/github账号无关 +git config --global user.name "yourname" +git config --global user.email "your-email-address" +# git凭证缓存,必做 +git config --global credential.helper store +# git默认的文本编辑器是nano,我一般喜欢改为vim,选做 +git config --global core.editor vim +``` + +## gitee 帐户 + +由于 OpenHarmony 的源码托管在 gitee 上,所以我们需要在 gitee 上注册一个帐户。注册的方法在此不复赘述。 + +本次编译过程中,我们并不需要提交代码,因而可以仅通过 http 方式从 gitee 下载源码——如果是这样的话,**就不需要进行 git 与 gitee 关联**;如果想要提交代码,那么需要将本地的 git 与 gitee 账号关联起来,具体操作如下: + +```bash +# 在本地执行 +ssh-keygen -t rsa -C "your-email-address" +``` + +命令执行后,每次需要输入都直接回车,**连续有三个回车**,执行就会结束,ssh 密钥对的公钥和私钥分别保存在`~/.ssh/id_rsa.pub`和`~/.ssh/id_rsa`中。 + +在 gitee 用户的设置界面,将公钥`id_rsa.pub`的内容复制到 gitee 的 SSH 公钥中,保存即可。 + +此时,我们就可以通过 ssh 的方式与 gitee 进行交互、也可以免密提交代码了。 + +# 系统源码编译 + +## 配置 repo 工具 + +**注意:前两条命令需要以 root 身份执行!!!** + +```bash +# 以下两条命令需要以root身份执行 +sudo -s +curl -s https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > /usr/local/bin/repo +chmod a+x /usr/local/bin/repo +exit + +# 这条普通身份也可以 +pip3 install -i https://repo.huaweicloud.com/repository/pypi/simple requests +``` + +之所以前两条命令需要以 root 身份而不能是 sudo,是因为`/usr/local/bin`是一个只有 root 用户才有写权限的目录,而 sudo 命令虽然是以 root 身份执行,但**重定向时候 sudo 用的也是当前用户身份**,权限不足,自然报错。(**如果是管道,那么 sudo 也是只对当前命令有效,而不是对后续的整个管道有效。**) + +## 获取源码 + +通过 repo + https/ssh 下载: + +```bash +# 通过http下载,这里需要注意你需要的ohos的版本 +repo init -u https://gitee.com/openharmony/manifest.git -b master --no-repo-verify +# 如果需要的是特定分支,-b后边改成对应分支名 +# 如果是tag,-b后的参数比较复杂,要在网页上提前确定好需要的tag名字, +# 如下载的是tag为OpenHarmony-v3.2-Release的版本,命令如下: +# repo init -u https://gitee.com/openharmony/manifest -b refs/tags/OpenHarmony-v3.2-Release --no-repo-verify +# 除使用https外,也可以通过ssh下载 +# repo init -u git@gitee.com:openharmony/manifest.git -b master --no-repo-verify + +repo sync -c +repo forall -c 'git lfs pull' +``` + +## prebuilts 与编译 + +```bash +# 先在源码根目录下执行脚本,安装编译器及二进制工具 +bash build/prebuilts_download.sh +# 再执行如下命令进行版本编译 +# 注意:默认编译的时候,目标cpu是32位,即使为64位cpu也无法使用64位功能 +sudo ./build.sh --product-name rk3568 --ccache +# 如果是64位cpu,需要加上--target-cpu=arm64 +sudo ./build.sh --product-name rk3568 --ccache --target-cpu=arm64 +``` + +## 编译完成 + +编译所生成的文件都归档在 out 目录下,结果镜像输出在源码根目录下的`out/rk3568/packages/phone/images`目录下。 + +自此源码编译成功,即可进行镜像烧录。 + +# 编译报错解决记录 + +以下记录编译过程中出现的一些出现过的报错、坑点和注意事项,以为后来者鉴。 + +## ssh 下载源码报错 + +在使用 ssh 下载时,第一步`repo init -u git@gitee.com:openharmony/manifest.git -b master --no-repo-verify`,报错: + +``` +Warning: Permanently added 'gitee.com,180.76.198.77' (ECDSA) to the list of known hosts. +``` + +经检查,发现 gitee 邮箱设置与公钥中邮箱存在差异,修改 gitee 邮箱设置后成功。 + +## 设备磁盘空间不足 + +编译过程中异常终止,并显示“设备上没有空间”。 + +**解决方法:** + +- 在`VMWare`中扩大磁盘空间 + - **关闭虚拟机** + - 在`VMWare`上方菜单栏中依次选择`虚拟机->设置->硬件->硬盘->扩展`,然后输入新的磁盘大小 + - 点击确定后会提示,说需要在虚拟机中扩展分区,才能使用新的磁盘空间 +- 打开虚拟机,并扩展分区 + - 在命令行中输入`sudo apt install gparted`,安装`gparted`分区工具 + - 打开该工具,选择需要扩展的分区(一般为挂载到根目录`/`的那个分区),点击`Resize/Move`,然后拖动分区大小,点击`Resize/Move`,再点击`Apply`,等待分区扩展完成 + +如果在虚拟机里用 gparted 进行分区扩展时,弹窗说“分区被挂载为只读,无法调整大小”。此时我们需要: + +- 右键该分区,查看挂载到什么位置了。一般是挂载到`/`与`/var/snap/firefox/common/host-hunspell` +- 执行以下命令: + +```bash +# 对于这个设备挂载到的每一个分区,都执行以下命令,以此类推 +sudo mount -o remount -rw / +sudo mount -o remount -rw /var/snap/firefox/common/host-hunspell +``` + +而后在 gparted 中点击`gparted->刷新设备`,即可进行分区扩展。 + +## 一个奇怪的报错——`GN Failed` + +这是一个很奇怪的报错,我们尚未找到真正的原因所在,只知道如何暂时地绕过去。 + +### 问题描述与复现 + +问题复现方法为在编译过程中,执行以下命令: + +```bash +./build.sh --product-name rk3568 --ccache +``` + +该命令是指导书中写的编译命令,但执行时候会出错;**与上文所述的经确认无误的编译命令不同在于没加`sudo`**。 + +如果按照指导书的命令来,不出意外编译将会报错: +![](https://www.qin-juan-ge-zhu.top/images/code/ohos_gn_fail_1.png) +![](https://www.qin-juan-ge-zhu.top/images/code/ohos_gn_fail_2.png) + +**以下是该问题的具体描述:** + +`build.log`如下: + +```plaintext +Set cache size limit to 100.0 GB +[OHOS INFO] loader args:['platforms_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/platforms.build"', 'subsystem_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/subsystem_config.json"', 'example_subsystem_file=""', 'exclusion_modules_config_file="/home/axiomer/桌面/out/preloader/ohos-sdk/exclusion_modules.json"', 'source_root_dir="/home/axiomer/桌面/"', 'gn_root_out_dir="out/sdk"', 'build_platform_name=phone', 'build_xts=False', 'load_test_config=False', 'target_os=ohos', 'target_cpu=arm64', 'os_level=standard', "ignore_api_check=['xts', 'common', 'testfwk']", 'scalable_build=False', 'skip_partlist_check=False'] +[OHOS INFO] Excuting gn command: /home/axiomer/桌面/prebuilts/build-tools/linux-x86/bin/gn gen --args="product_name=\"ohos-sdk\" product_path=\"/home/axiomer/桌面/productdefine/common/products\" product_config_path=\"/home/axiomer/桌面/productdefine/common/products\" device_name=\"sdk\" device_path=\"/home/axiomer/桌面/device/board/ohos/sdk\" device_company=\"ohos\" device_config_path=\"/home/axiomer/桌面/device/board/ohos/sdk\" target_cpu=\"arm64\" is_standard_system=true ohos_build_compiler_specified=\"\" ohos_build_time=1707374188101 ohos_build_datetime=\"2024-02-08 22:36:28\" build_ohos_sdk=true build_ohos_ndk=true ohos_build_enable_ccache=true ohos_build_type=\"debug\" device_type=\"default\" build_variant=\"root\" use_thin_lto=false ndk_platform=\"linux\" sdk_for_hap_build=true skip_generate_module_list_file=true enable_lto_O0=true archive_ndk=false enable_ndk_doxygen=false use_cfi=false sdk_check_flag=false sdk_platform=\"linux\" root_perf_main=\"main\" runtime_mode=\"release\"" --args=product_name="ohos-sdk" product_path="/home/axiomer/桌面/productdefine/common/products" product_config_path="/home/axiomer/桌面/productdefine/common/products" device_name="sdk" device_path="/home/axiomer/桌面/device/board/ohos/sdk" device_company="ohos" device_config_path="/home/axiomer/桌面/device/board/ohos/sdk" target_cpu="arm64" is_standard_system=true ohos_build_compiler_specified="" ohos_build_time=1707374188101 ohos_build_datetime="2024-02-08 22:36:28" build_ohos_sdk=true build_ohos_ndk=true ohos_build_enable_ccache=true ohos_build_type="debug" device_type="default" build_variant="root" use_thin_lto=false ndk_platform="linux" sdk_for_hap_build=true skip_generate_module_list_file=true enable_lto_O0=true archive_ndk=false enable_ndk_doxygen=false use_cfi=false sdk_check_flag=false sdk_platform="linux" root_perf_main="main" runtime_mode="release" /home/axiomer/桌面/out/sdk +ERROR at //build/config/BUILDCONFIG.gn:92:15: Could not read file. + read_file("${preloader_output_dir}/build_config.json", "json") + ^------------------------------------------ +I resolved this to "/home/axiomer/桌面/out/preloader/build_config.json". +root_out_dir=//out/sdk +root_build_dir=//out/sdk +root_gen_dir=//out/sdk/gen +current_toolchain= +[91m[OHOS ERROR][0m Traceback (most recent call last): +[91m[OHOS ERROR][0m File "/home/axiomer/桌面/build/hb/containers/status.py", line 47, in wrapper +[91m[OHOS ERROR][0m return func(*args, **kwargs) +[91m[OHOS ERROR][0m File "/home/axiomer/桌面/build/hb/services/gn.py", line 197, in _execute_gn_gen_cmd +[91m[OHOS ERROR][0m SystemUtil.exec_command(gn_gen_cmd, self.config.log_path) +[91m[OHOS ERROR][0m File "/home/axiomer/桌面/build/hb/util/system_util.py", line 64, in exec_command +[91m[OHOS ERROR][0m LogUtil.get_failed_log(log_path) +[91m[OHOS ERROR][0m File "/home/axiomer/桌面/build/hb/util/log_util.py", line 191, in get_failed_log +[91m[OHOS ERROR][0m LogUtil.get_gn_failed_log(log_path) +[91m[OHOS ERROR][0m File "/home/axiomer/桌面/build/hb/util/log_util.py", line 137, in get_gn_failed_log +[91m[OHOS ERROR][0m raise OHOSException( +[91m[OHOS ERROR][0m exceptions.ohos_exception.OHOSException: GN Failed! Please check error in /home/axiomer/桌面/out/sdk/error.log, and for more build information in /home/axiomer/桌面/out/sdk/build.log +[91m[OHOS ERROR][0m +[91m[OHOS ERROR][0m Code: 3000 +[91m[OHOS ERROR][0m +[91m[OHOS ERROR][0m Reason: GN Failed! Please check error in /home/axiomer/桌面/out/sdk/error.log, and for more build information in /home/axiomer/桌面/out/sdk/build.log +[91m[OHOS ERROR][0m +[91m[OHOS ERROR][0m Error Type: UNKNOWN +[91m[OHOS ERROR][0m +[91m[OHOS ERROR][0m Description: An unknown error occurred while executing 'gn gen'. +[91m[OHOS ERROR][0m +[91m[OHOS ERROR][0m Solution: There is no solution available. You can check the 'gn_error.log' in the output directory for more information +[91m[OHOS ERROR][0m +``` + +报错信息中提到`ERROR at //build/config/BUILDCONFIG.gn:92:15: Could not read file.`,但是经过检查文件,**存在名为`BUILDCONFIG.gn`的文件,且开放可读权限**;但报错信息中显示无法读该文件。 + +### 失败尝试 + +在此,我进行了许多尝试,但都无一例外失败了;以下是我的尝试点,或许会有帮助: + +- 根据[一个相似提问](),对相关依赖进行安装,失败了: + ![](https://www.qin-juan-ge-zhu.top/images/code/ohos_gn_fail_dependencies.png) +- 根据报错信息,在编译命令中添加`--no-prebuilt-sdk`选项对 ohos-sdk 的构建进行跳过,依然失败 + +尝试未果 T^T + +### 最终方案 + +报错信息如下所示: + +在上述报错信息中提到`Permission denied`,即权限不够;于是尝试添加`sudo`,居然跑通了!!!! + +在指导书的编译指令`./build.sh --product-name rk3568 --ccache`前添加权限设置`sudo`,即指令更改为: + +```bash +sudo ./build.sh --product-name rk3568 --ccache +``` + +不知道为什么,编译过程中始终没有向我提出权限要求,但是不加`sudo`开权限就是跑不通,很奇怪 @ \_ @也许是 OHOS 内敛不好意思申请权限呢…… + +## 另一个奇怪的报错——`FAILED: load BTF from vmlinux: Unknown error -22` + +关于这个报错,当时的错误日志、运行日志、聊天截图均已丢失,但时日不久,记忆还算清楚,加上这个报错困扰了我好些天,因而必须记录。 + +### 问题简要描述 + +正常按照`sudo ./build.sh --product-name rk3568 --ccache`编译时,报错,主要报错信息如下: + +```plaintext +die__process_unit: DW_TAG_label (0xa) @ <0x3adc> not handled! +die__process_unit: DW_TAG_label (0xa) @ <0x3bdc> not handled! +die__process_unit: DW_TAG_label (0xa) @ <0x3bef> not handled! +die__process_unit: DW_TAG_label (0xa) @ <0x3ce5> not handled! +die__process_unit: DW_TAG_label (0xa) @ <0x3cff> not handled! +die__process_unit: DW_TAG_label (0xa) @ <0x3d19> not handled! +Killed + LD .tmp_vmlinux.kallsyms1 + KSYMS .tmp_vmlinux.kallsyms1.S + AS .tmp_vmlinux.kallsyms1.S + LD .tmp_vmlinux.kallsyms2 + KSYMS .tmp_vmlinux.kallsyms2.S + AS .tmp_vmlinux.kallsyms2.S + LD ymlinux + BTEIDS vmlinux +FAILED: load BTF from vmlinux: Unknown error -22make[2]: *** [/home/player/harmony/out/kernel/src_tmp/linux-5.19/ +Makefile:1225: vmlinux]错误 255 +make[1]: *** [arch/arm64/Makefile:208: rk3568-toybrick-x0-linux.img]错误2 +make[1]: 离开目录"/home/player/harmony/out/kernel/OBJ/linux-5.10" +make: *** [Makefile:192:__sub-make]错误 2 +``` + +可以看到,报错的关键点在于`FAILED: load BTF from vmlinux: Unknown error -22`这里。查看 Makefile 对应报错位置: + +```makefile +vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE + +$(call if_changed,link-vmlinux) + +targets := vmlinux +``` + +### 解释 + +- 这段 makefile 的实际功能是链接一个虚拟 linux + - 通过目录位置,我们可以合理猜测,这里是在进行**linux 内核编译,但失败了** +- 报错中说的`BTF`则是数据格式 + - 一般出现`load BTF from xxx`错误时,是由于系统不一致导致的(也就是不同系统 BTF 格式不一样,出现了识别错误) +- 最重要的是,**Unknown error -22 这个报错码,搜不到任何信息!** + +因而,我尝试了重装系统,将我的 Ubuntu22.04 重装为 20.04,但最后**仍然会出现这个错误**,足以证明不是系统差异导致的。 + +### 解决 + +最终,我在[第二篇参考博客](https://unix.stackexchange.com/questions/616392/failed-load-btf-from-vmlinux-unknown-error-2make-makefile1162-vmlinu)里找到了答案:编译较新的 Linux 内核时,至少需要 10G+内存,而我一贯把虚拟机内存只设置为 8G。 + +令人好奇的是,为什么需要这么大内存?按理来说在虚拟内存空间里,物理内存不够的时候,不应该是由 OS 出面进行内存与磁盘的页面调度吗?我认为,可能是在编译过程中,其他进程占据一部分内存,而这里在链接一个虚拟 linux 文件,可能文件体积过大而尚未创建完成,所有页面一直驻留在内存中,导致内存崩溃。 + +总而言之,言而总之,在`VMware->虚拟机->设置->硬件->内存`中,把内存扩大,就可以完美解决该问题。经我的测试,内存 13.2G(主机总内存 16G 时候推荐的最大虚拟机内存)是能编译完成的,编译时长 6h。最终解决。 + +# ohos 的 NDK + +NDK 编译方式比较简单,在源码根目录下执行如下命令: + +```bash +# 安装依赖 +./build/build_scripts/env_setup.sh + +# 执行完上述命令后记得执行source ~/.bashrc或者重启终端 +source ~/.bashrc + +# 安装编译SDK需要的依赖包(编译镜像的时候是不依赖这些包的) +sudo apt-get install libxcursor-dev libxrandr-dev libxinerama-dev + +./build.sh --product-name ohos-sdk --ccache --build-target ohos_ndk +``` + +编译出来的 NDK 在`out/sdk/packages/ohos-sdk/linux/native`下。当然同时也有 windows 版本的 NDK,你猜在哪里? + +将编译出来的 NDK 的 zip 解压到你想要的目录下,然后将该目录添加到环境变量中,即可使用。 + +注意,NDK 包提供的交叉编译工具是 cmake 和 ninja,编译器是 clang 和 clang++,并没有我们熟悉的 gcc/g++和 make。除此之外,NDK 还未我们提供编译所需的全套服务,如编译工具链配置文件`ohos.toolchain.cmake`、头文件、库文件等。快说,谢谢 ohos~ + +为了更方便地使用NDK,鄙人不才,写了两个脚本,分别用于cmake编译和单文件编译: + +```bash +#!/bin/bash + +########################################################################## +# File Name : compile.sh +# Encoding : utf-8 +# Author : We-unite +# Email : weunite1848@gmail.com +# Created Time : 2024-02-29 15:19:15 +########################################################################## + +set -e + +if [ $UID -eq 0 ]; then + echo "Please do not run this script as root" + exit 1 +fi + +if [ $# -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +if [ $2 == "v8" ]; then + arch=arm64-v8a +elif [ $2 == "v7" ]; then + arch=armeabi-v7a +else + echo "Invalid architecture: $2" + exit 1 +fi + +link=$1 # static or shared +native_path=~/app/native + +export PATH=$native_path/build-tools/cmake/bin:$PATH + +# 使用cmake编译,编译生成的文件运行在rk3568上 +cmake -B build -D OHOS_STL=c++_$link -D OHOS_ARCH=$arch -D OHOS_PLATFORM=OHOS -D CMAKE_TOOLCHAIN_FILE=$(find $native_path -name ohos.toolchain.cmake) +cmake --build build +``` + +```bash +``` + +# 完结撒花 + +本次鸿蒙开发环境的搭建过程可谓一波三折,总结几个最大的坑点,或许可以作为编译的经验罢: + +- **有报错,试试添加权限!** +- 贫贱程序猿百事哀,编译 OpenHarmony 系统需要**足够的硬件配置** + - 内存 10G+ + - 磁盘 160G+ + +不说了,抓紧攒点钱开学升级电脑配置要紧……磁盘快炸了…… \ No newline at end of file diff --git a/common/CSS/highlight.css b/common/CSS/highlight.css index 092a52b..14b71ed 100644 --- a/common/CSS/highlight.css +++ b/common/CSS/highlight.css @@ -73,7 +73,11 @@ pre .btn-tip { border-radius: 4px; } -/* 代码复制时全选但不能被看到 */ -code ::selection { +/* 代码复制时全选但不能被看到 + * 该功能是不道德的,用户想要怎么复制是人家的权利 + * 即使是自己用起来也经常只需要复制一段 + * 所以还是让用户自由复制吧 + */ +/* code ::selection { background-color: rgba(0, 0, 0, 0); -} \ No newline at end of file +} */ \ No newline at end of file diff --git a/common/script4works.html b/common/script4works.html index 0080751..b865bf1 100644 --- a/common/script4works.html +++ b/common/script4works.html @@ -156,10 +156,11 @@ Zeal文档下载 - -OHOS 文档 + -项目文档 -功能网页 diff --git a/common/script4works.js b/common/script4works.js index 68090f1..0ae78a2 100644 --- a/common/script4works.js +++ b/common/script4works.js @@ -157,10 +157,11 @@ document.writeln(" Zeal文档下载"); document.writeln(" "); document.writeln(""); -document.writeln(" -OHOS 文档"); +document.writeln(" -项目文档"); document.writeln("
"); -document.writeln(" OpenHarmony编译"); -document.writeln(" LCM交叉编译/a>"); +document.writeln(" OpenHarmony编译"); +document.writeln(" LCM交叉编译"); +document.writeln(" godo知识总结"); document.writeln("
"); document.writeln(""); document.writeln(" -功能网页"); diff --git a/works/poetry.html b/works/poetry.html index 27fff26..e4dcbfa 100644 --- a/works/poetry.html +++ b/works/poetry.html @@ -61,7 +61,9 @@ 机上作
顺天三日
+ href="#京师三日">京师三日
+ 深圳两首

@@ -277,13 +279,28 @@

23.2.11

-

顺天三日

+

京师三日

夜半登车辔,晨风谒主席。
一哭石鼓字,三叹党人碑。
皇史竟谁睹,长城岂世违?
不闻君主业,新月笑为炊。

23.12.20

+ +

深圳两首

+

至粤已期,犹记初来心绪,草诗聊记,未求音律。

+

谁知一季竟重登,火炙云停更不同。
+ 万木坦途长静伫,千帆江上寂无声。
+ 砖石有汗层楼起,力者无言史简成。
+ 换地英雄千万数,珠江惟见日当空。
+

+ +

暑气蒸腾下,谁人奔走忙。
+ 日仄未及饭,雨汗落衣裳。
+ 广厦虽千万,安求七尺床。
+ 入夜星灯火,心安岂吾乡?
+

+

24.7.14

-- cgit v1.2.3-70-g09d2