AkiraZheng's Time.

openeluer安装与内核替换

Word count: 2.9kReading time: 13 min
2025/08/17

前言

主机系统:Sonoma 14.4.1(x86)

虚拟软件:VMware Fusion

openeluer系统:openEuler-24.03-LTS-SP2-x86_64-dvd.iso

内核选择:linux 5.x 内核 64G

一、Mac工具准备

  1. 虚拟软件:VMware Fusion
  2. ssh远程控制软件:Termius

二、虚拟机安装openeluer系统

首先去欧拉官网下载openeluer系统镜像文件:openEuler,下载LTS长期支持的版本

镜像文件下载完成后,在虚拟软件 VMware Fusion 中创建虚拟机,选择镜像文件并拖入到 VMware 中:

然后选个linux内核和固件后直接安装:

然后选择自定义配置进行配置:

常选择的配置如下(可以通过虚拟机->设置打开配置界面):

先分配一个较大的磁盘空间:

选择显示全部回到配置选择界面:

然后继续配置内存,将内存配置为4G(一定要换,最低4G):

配置完硬件配置后,就可以直接开启虚拟机进行安装了:

进行系统配置,经常修改的配置项:

首先语言选择中文和英文:

然后开启root:

安装目的地可以不用改:

打开网络选项:

因为我是学习虚拟化的,所以选择了虚拟机配置,否则的话可以选择服务器即可:

然后开始安装,等待一段时间后完成安装就能进入系统:

安装完成后,进入系统:

输入刚刚配置的root账号密码:

三、Termius中添加虚拟机

首先查看虚拟机地址:

然后在termius中添加虚拟机:

四、进行内核替换

内核替换主要参考openeluer的内核更新与替换

首先查看当前安装了的linux内核版本:

4.1 清理源代码树

进入解压好的源码文件夹执行命令,清理过去内核编译产生的文件,第一次编译时可不执行此命令

1
make mrproper

4.2 生成内核配置文件.config

可以先将将系统原配置文件拷贝过来,原配置文件在/boot目录下,输入config-后tab一下就出来了

1
2
mkdir ../cpConfig
cp -v /boot/config-$(uname -r) ../cpConfig

执行依赖安装

1
yum install ncurses-devel

进入配置界面,直接选择默认配置,然后选择Save,生成了一个.config文件:

然后按Exit保存刚刚的config信息

最后需要禁用证书、BTF,否则后面编译会失败:

1
vim .config

4.3 内核编译及安装

执行编译前先安装所需组件

1
2
3
yum install elfutils-libelf-devel
yum install openssl-devel
yum install bc

然后执行make开始编译,编译大概要一段时间,这个过程需要保证连接的稳定,中断了就要重新编译了

1
2
make -j$(nproc)
# make -j4

通常内核编译是一个 计算密集型任务,建议使用 -j 参数指定 CPU 核心数 来加速编译。

编译完成后,可以安装生成的 vmlinuz 文件:

1
2
make modules_install
make install

生成的新内核可以在 /boot/ 目录下查看:

4.4 更新引导

下面的命令会根据 /boot/ 目录下的内核文件自动更新启动引导文件。

1
grub2-mkconfig -o /boot/grub2/grub.cfg

然后重启系统就可以看到多个内核,其中一个就是我们新安装的内核,可自由选择一个内核启动系统。

1
reboot

重启完后可以确认新的内核是否生效:

1
uname -a

五、支持远程ssh连接

编辑 SSH 配置文件:

1
vi /etc/ssh/sshd_config

找到并修改以下参数:

1
AllowTcpForwarding yes   # 取消注释或添加此行

保存文件并重启 SSH 服务:

1
systemctl restart sshd  # 大多数 Linux 系统

六、在 mac host 上通过 qemu 启动虚机

首先要安装 qemu:

1
brew install qemu

然后查找本地的 fd 固件:

1
2
# 查找固件文件
find /opt/homebrew/Cellar/qemu -name "edk2-aarch64-code.fd" 2>/dev/null

然后在欧拉官网下载 qcow2 文件openEuler-24.03-LTS-aarch64.qcow2.xz,下载后解压:

1
xz -d openEuler-24.03-LTS-aarch64.qcow2.xz

启动虚机:

1
2
3
4
5
6
7
8
9
10
qemu-system-aarch64 \
-machine virt,accel=hvf -cpu host \
-smp 4 \
-m 4G \
-bios edk2-aarch64-code.fd \
-drive file=openEuler-24.03-LTS-aarch64.qcow2,if=none,id=hd0,format=qcow2 \
-device virtio-blk-pci,drive=hd0 \
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=net0 \
-nographic

欧拉 qcow2 的默认账号密码(进入后密码通常修改成root就行):

  • 用户名: root
  • 密码: openEuler12#$

之后就可以使用 ssh 登录虚机了:

macos 中免密登录到虚机中

成功以root登录后,执行以下命令:

1
2
# 1. 编辑SSH配置文件
vi /etc/ssh/sshd_config

在文件中找到并修改以下配置(按 i 进入编辑模式):

1
2
3
4
5
# 确保这些行没有被注释(行首没有#号)
PasswordAuthentication yes
PermitRootLogin yes
PubkeyAuthentication yes
ChallengeResponseAuthentication yes

如果找不到这些行,就手动添加。

保存退出:按 ESC,输入 :wq,回车。

1
2
3
4
5
# 2. 重启SSH服务
systemctl restart sshd

# 3. 验证SSH服务状态
systemctl status sshd

然后使用密钥认证实现免密登录,在host中查找公钥文件(如果没有的话就创建自己的公钥):

1
cat ~/.ssh/id_rsa.pub

在虚拟机内执行:

1
2
3
4
5
6
7
8
9
# 创建.ssh目录
mkdir -p /root/.ssh

# 添加你的公钥
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC..." >> /root/.ssh/authorized_keys

# 设置正确的权限
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys

注意:公钥内容就是 ~/.ssh/id_rsa.pub 文件的内容。

从Mac端重新连接:

1
2
# 或者使用密钥登录
ssh -p 2222 root@127.0.0.1

自动挂载9P

1
2
3
4
5
6
7
8
9
10
11
qemu-system-aarch64 \
-machine virt,accel=hvf -cpu host \
-smp 4 \
-m 4G \
-bios edk2-aarch64-code.fd \
-drive file=openEuler-24.03-LTS-aarch64.qcow2,if=none,id=hd0,format=qcow2 \
-device virtio-blk-pci,drive=hd0 \
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=net0 \
-virtfs local,path=../kernel,mount_tag=host-kernel,security_model=mapped \
-nographic

从Mac host通过SSH执行以下命令,在虚拟机中配置自动挂载。

首先配置 /etc/fstab:

1
ssh -p 2222 root@127.0.0.1 "echo 'host-kernel /mnt/kernel 9p trans=virtio,version=9p2000.L 0 0' >> /etc/fstab"

创建挂载点目录并验证配置:

1
ssh -p 2222 root@127.0.0.1 "mkdir -p /mnt/kernel && cat /etc/fstab | grep kernel"

手动挂载测试:

1
2
3
4
5
# 创建挂载点并挂载
ssh -p 2222 root@127.0.0.1 "mkdir -p /mnt/kernel && mount -t 9p -o trans=virtio,version=9p2000.L host-kernel /mnt/kernel"

# 验证挂载
ssh -p 2222 root@127.0.0.1 "ls /mnt/kernel"

如果能看到文件就成功了。重启虚拟机后会自动挂载。

qemu tcg 启动

1
2
3
4
5
6
7
8
9
10
11
qemu-system-aarch64 \
-machine virt,virtualization=true -cpu max \
-smp 8 \
-m 6G \
-bios edk2-aarch64-code.fd \
-drive file=openEuler-24.03-LTS-aarch64.qcow2,if=none,id=hd0,format=qcow2 \
-device virtio-blk-pci,drive=hd0 \
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=net0 \
-virtfs local,path=../kernel,mount_tag=host-kernel,security_model=mapped \
-nographic

关键点是要加上virtualization=true,然后-cpu max

lkvm 编译

1
2
git clone https://git.kernel.org/pub/scm/linux/kernel/git/will/kvmtool.git
cd kvmtool

修改 Makefile,将 LDFLAGS 设为静态编译:

1
2
3
4
# 原来
LDFLAGS :=
# 改为
LDFLAGS := -static

安装依赖:

1
dnf install -y dtc libfdt-devel glibc-static glib2-devel dtc-devel

执行静态编译:

1
2
3
make ARCH=arm64 CC="gcc -static"

file lkvm

lkvm编译运行

从qemu虚机中启动lkvm就可以实现代码调试

启动 lkvm 的时候还需要在 qcow2 起的虚机中编译好启动 lkvm 需要的最小根文件系统initramfs.cpio.gz,可以直接使用附件中的脚本【注意,用脚本中的方式启的lkvm是不能用的,这里是为了在作为 host 的 qemu 中验证 stage 2 页表是否能用而已,所以倒也不需要lkvm能用,只要它能起来就行】

启动 lkvm:

1
2
3
4
5
6
7
./lkvm run --name testvm \
-k Image \
--initrd initramfs.cpio.gz \
--console serial \
--params "console=ttyS0 rdinit=/init" \
-m 512 \
--nodefaults

给挂在的仓库添加虚机的git权限

在虚机中执行:

1
2
3
4
5
6
7
8
# 如果还是卡住,先移除可能存在的配置
git config --global --unset safe.directory /mnt/kernel

# 重新添加
git config --global --add safe.directory /mnt/kernel

# 验证配置
git config --global --get-all safe.directory

附件

build-initramfs.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#!/bin/sh
set -e

# Output directory (current working directory by default)
OUTPUT_DIR="${1:-$(pwd)}"
WORK_DIR="$OUTPUT_DIR/initramfs"

echo "Building initramfs in $OUTPUT_DIR..."

# Detect architecture
ARCH=$(uname -m)
case "$ARCH" in
x86_64) LD_ARCH="ld-linux-x86-64.so.2" ;;
i386|i686) LD_ARCH="ld-linux.so.2" ;;
aarch64) LD_ARCH="ld-linux-aarch64.so.1" ;;
arm*) LD_ARCH="ld-linux-arm" ;;
*) LD_ARCH="ld-linux" ;;
esac

# Find library path dynamically
find_lib() {
local libname="$1"
# Search in common library locations
for dir in /lib /lib64 /usr/lib /usr/lib64 /lib32; do
if [ -f "$dir/$libname" ]; then
echo "$dir/$libname"
return 0
fi
done
# Use ldconfig to find library
if command -v ldconfig >/dev/null 2>&1; then
result=$(ldconfig -p 2>/dev/null | grep "$libname" | head -1 | awk '{print $NF}')
if [ -n "$result" ]; then
echo "$result"
return 0
fi
fi
return 1
}

# Copy a binary and all its required libraries
copy_binary() {
local src="$1"
local dest_dir="$2"
local bin_name=$(basename "$src")

# Resolve symlinks to get real binary
if [ -L "$src" ]; then
real_bin=$(readlink -f "$src" 2>/dev/null || echo "$src")
else
real_bin="$src"
fi

mkdir -p "$dest_dir"

# Copy the binary
cp "$real_bin" "$dest_dir/$bin_name"

# Copy libraries needed by this binary using ldd
if command -v ldd >/dev/null 2>&1; then
ldd "$real_bin" 2>/dev/null | grep -E '=>' | awk '{print $3}' | while read -r lib; do
# Handle virtual libraries
[ -z "$lib" ] && continue
[ ! -f "$lib" ] && continue

lib_name=$(basename "$lib")

# Determine destination based on path
case "$lib" in
*/lib64/*)
mkdir -p "$dest_dir/lib64"
lib_dest="$dest_dir/lib64"
;;
*/lib32/*)
mkdir -p "$dest_dir/lib"
lib_dest="$dest_dir/lib"
;;
*)
mkdir -p "$dest_dir/lib"
lib_dest="$dest_dir/lib"
;;
esac

# Avoid duplicate copies
if [ ! -f "$lib_dest/$lib_name" ]; then
cp "$lib" "$lib_dest/$lib_name"
fi
done
fi
}

# Resolve command path
resolve_cmd() {
local cmd="$1"
# Try command -v first, fall back to which
if command -v "$cmd" >/dev/null 2>&1; then
command -v "$cmd"
elif command -v which >/dev/null 2>&1; then
which "$cmd" 2>/dev/null
fi
}

# 1. Create directory structure
mkdir -p "$WORK_DIR"/bin "$WORK_DIR"/sbin "$WORK_DIR"/etc "$WORK_DIR"/proc
mkdir -p "$WORK_DIR"/sys "$WORK_DIR"/dev "$WORK_DIR"/lib "$WORK_DIR"/lib64 "$WORK_DIR"/run

# 2. List of required binaries (name:path format, one per line)
# Format: name|path|destdir
BINARIES="
bash|/bin/bash|bin
sh|/bin/sh|bin
ls|/usr/bin/ls|bin
cat|/usr/bin/cat|bin
echo|/usr/bin/echo|bin
mkdir|/usr/bin/mkdir|bin
switch_root|/usr/sbin/switch_root|sbin
switch_root|/sbin/switch_root|sbin
mount|/usr/bin/mount|bin
umount|/usr/bin/umount|bin
sleep|/usr/bin/sleep|bin
"

# 3. Copy binaries
echo "$BINARIES" | while IFS='|' read -r name path destdir; do
[ -z "$name" ] && continue
resolved=$(resolve_cmd "$name")

if [ -n "$resolved" ] && [ -f "$resolved" ]; then
echo "Copying $name from $resolved"
copy_binary "$resolved" "$WORK_DIR/$destdir"
else
echo "Warning: $name not found, skipping"
fi
done

# 4. Copy the dynamic linker
copy_ld() {
for pattern in /lib64/ld-linux*.so.* /lib/ld-linux*.so.* /lib/*/ld-linux*.so.*; do
case "$pattern" in
*\*) continue ;;
esac
if [ -f "$pattern" ]; then
ld_name=$(basename "$pattern")
mkdir -p "$WORK_DIR/lib64"
cp "$pattern" "$WORK_DIR/lib64/$ld_name"
# Create versioned symlink
if [ -L "$pattern" ]; then
target=$(readlink "$pattern")
case "$target" in
ld-*.so.*)
ln -sf "$target" "$WORK_DIR/lib64/${target%.so.*}.so" 2>/dev/null || true
;;
esac
fi
return 0
fi
done
return 1
}
copy_ld

# 5. Create init script
cat > "$WORK_DIR/init" << 'INITEOF'
#!/bin/sh
echo "initramfs booting..."
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
echo "Welcome to LKVM!"
exec /bin/sh
INITEOF
chmod +x "$WORK_DIR/init"

# 6. Package initramfs
cd "$WORK_DIR"
if command -v cpio >/dev/null 2>&1; then
find . -print0 | cpio -o -H newc --null | gzip > "$OUTPUT_DIR/initramfs.cpio.gz"
else
echo "Error: cpio not found"
exit 1
fi

if [ -f "$OUTPUT_DIR/initramfs.cpio.gz" ]; then
size=$(ls -lh "$OUTPUT_DIR/initramfs.cpio.gz" | awk '{print $5}')
echo "Done! initramfs.cpio.gz created: $size"
else
echo "Error: Failed to create initramfs.cpio.gz"
exit 1
fi

参考

  1. Fusion 或 Vmware 安装 openEuler 20.03 最小镜像

  2. VM虚拟机中安装openeluer

  3. macOS中vmware Fusion的虚拟机创建

  4. openeluer的内核更新与替换:主要参考

  5. 探秘内核:openEuler的内核编译实战指南【华为根技术】

  6. OpenEuler内核编译及替换

CATALOG
  1. 一、Mac工具准备
  2. 二、虚拟机安装openeluer系统
  3. 三、Termius中添加虚拟机
  4. 四、进行内核替换
    1. 4.1 清理源代码树
    2. 4.2 生成内核配置文件.config
    3. 4.3 内核编译及安装
    4. 4.4 更新引导
  5. 五、支持远程ssh连接
  6. 六、在 mac host 上通过 qemu 启动虚机
    1. macos 中免密登录到虚机中
    2. 自动挂载9P
    3. qemu tcg 启动
    4. lkvm 编译
    5. 从qemu虚机中启动lkvm就可以实现代码调试
    6. 给挂在的仓库添加虚机的git权限
  7. 附件
  8. 参考