如何在最小 linux 内核环境中启动进程?
How to launch a process inside a minimal linux kernel environment?
是否可以在裸 linux 内核之上 运行 通用 C 程序?
我正在使用带有 Qemu 的虚拟化 linux 最小系统,我在其中加载自定义内核和根文件系统,但显然我不能 运行 我的用户-space你好世界程序。
编辑 1:我已将问题更新得更详细。以下是我遵循的步骤:
1] 我在主机上使用以下命令编译了 hello_world 程序:
gcc hello_world.c -o hello_world
源码如下:
#include <stdio.h>
int main(int argc, char const *argv[]) {
printf("Hello world!\n");
return 0;
}
通过 运行 在我的主机上运行该程序,它运行良好。
我已经按照@nexus 的建议检查了构建程序所针对的库,如下:
$ ldd hello_world
linux-vdso.so.1 => (0x00007ffc0dfc8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efd90658000)
/lib64/ld-linux-x86-64.so.2 (0x000055a57447f000)
2] 我通过执行 buildroot 构建了根文件系统,默认来自 Ciro Santilli's linux-kernel-module-cheat。我已经编译了 linux 内核并得到了它对应的 bzImage
3] 我已经挂载了根文件系统,并将我编译好的程序复制到里面,如下:
sudo mount -t ext2 rootfs.ext2 /home/andreww/Desktop/mountfile/
sudo cp hello_world /home/andreww/Desktop/mountfile/
sudo umount /home/andreww/Desktop/mountfile/
这个感觉不对,我是因为没找到直接在Guest Machine里面编译C程序的方法才这么做的。
在来宾机器上我没有 gcc,实际上如果我尝试调用它我会得到:
# -sh: gcc: not found
4] 我已经用命令
启动了模拟系统
qemu-system-x86_64 \
-M pc \
-append 'root=/dev/vda $extra_append' \
-drive file=${images_dir}/rootfs.ext2,if=virtio,format=raw \
-kernel ${images_dir}/bzImage \
-m 256M \
-net nic,model=virtio \
-net user \
-enable-kvm \
-cpu host \
-smp threads=4
这是我从 Ciro Santilli's linux-kernel-module-cheat 中提取的。
rootfs.ext2 是系统启动的根文件系统,bzImage 是我将用来启动来宾机器的内核映像。
5] 客户机在 shell 中模拟。这是我所期望的,因为我只虚拟化裸内核而不是整个操作系统映像。
我显示了 buildroot 登录,我以 root 身份登录并 运行 以下命令:
当我尝试 运行 我的 hello 程序时,我显示了以下消息:
-sh: ./hello_world: not found
我想我的程序永远不会执行,即使我通过调试看到它作为 elf 文件加载到系统中也是如此。
如果我输入一个不存在的随机命令,我会得到同样的错误。
目标是让 hello_world 程序 运行 在 user-space 中,位于内核之上,我不希望它成为内核的一部分。
我同意你们的看法,我可能缺少一些库或一些基本的东西,但我不知道该怎么做。
您基本上可以 运行 一个进程而不是 init,如果您打算这样做的话。要 运行 进程而不是系统 init 应用程序,您可以替换原始 init 或将 init=/path/to/you/application 作为内核参数传递给您的 VM。
但是有几件事你应该记住:
所有库都需要存在于您的 VM 中与您在其上构建应用程序的系统相同的位置。这还包括它所针对的基本 c 库。您可以使用
获取所有这些库的列表
ldd hello_world
举个例子,如果你编译下面的程序
#include <stdio.h>
int main(int argc, char** argv) {
printf("hello world!\n");
return 0;
}
对于 ldd
,您可能会得到这样的结果
$ ldd test
linux-vdso.so.1 => (0x00007ffdba6ba000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8559f50000)
/lib64/ld-linux-x86-64.so.2 (0x000055fc58ece000)
如果替换init进程,需要注意console中断并妥善处理。如果你不这样做,你就会冒着非常糟糕的系统崩溃的风险。因此,如果您真的不知道自己在做什么,最好保留一个 init 进程并让该进程启动您的应用程序。
我以前看过你命令的输出。您是否使用 shell 脚本来启动您的应用程序?如果是这样,则可能表明您当前在虚拟环境中缺少某些库。
编辑:
也许您可以创建一个静态链接的应用程序,这意味着将所有库编译到同一个文件中。在 gcc 上,您可以为此使用 -static 开关。查看区别:
$ gcc -o test test.c
$ ldd test
linux-vdso.so.1 => (0x00007ffcd1f22000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f088d43a000)
/lib64/ld-linux-x86-64.so.2 (0x00005580f8a2b000)
$ gcc -static -o test test.c
$ ldd test
not a dynamic executable
如前所述,您可以使用 ldd 来查找所需的库。我通常使用的另一个选项是 objdump,因为输出更详细。
所以对于我的应用程序来说,这意味着这样的事情(我已经将输出缩短到相关部分):
$ objdump -p test
test: file format elf64-x86-64
[...]
Dynamic Section:
NEEDED libc.so.6
INIT 0x00000000004003c8
FINI 0x00000000004005c4
INIT_ARRAY 0x0000000000600e10
INIT_ARRAYSZ 0x0000000000000008
FINI_ARRAY 0x0000000000600e18
FINI_ARRAYSZ 0x0000000000000008
[...]
所以在我的例子中,就是 libc.so.6 我需要从主机上的 /lib/x86_64-linux-gnu/libc.so.6 复制到 /lib/x86_64-linux-gnu/libc.so.6虚拟机内.
希望对您有所帮助。
是否可以在裸 linux 内核之上 运行 通用 C 程序?
我正在使用带有 Qemu 的虚拟化 linux 最小系统,我在其中加载自定义内核和根文件系统,但显然我不能 运行 我的用户-space你好世界程序。
编辑 1:我已将问题更新得更详细。以下是我遵循的步骤:
1] 我在主机上使用以下命令编译了 hello_world 程序:
gcc hello_world.c -o hello_world
源码如下:
#include <stdio.h>
int main(int argc, char const *argv[]) {
printf("Hello world!\n");
return 0;
}
通过 运行 在我的主机上运行该程序,它运行良好。 我已经按照@nexus 的建议检查了构建程序所针对的库,如下:
$ ldd hello_world
linux-vdso.so.1 => (0x00007ffc0dfc8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efd90658000)
/lib64/ld-linux-x86-64.so.2 (0x000055a57447f000)
2] 我通过执行 buildroot 构建了根文件系统,默认来自 Ciro Santilli's linux-kernel-module-cheat。我已经编译了 linux 内核并得到了它对应的 bzImage
3] 我已经挂载了根文件系统,并将我编译好的程序复制到里面,如下:
sudo mount -t ext2 rootfs.ext2 /home/andreww/Desktop/mountfile/
sudo cp hello_world /home/andreww/Desktop/mountfile/
sudo umount /home/andreww/Desktop/mountfile/
这个感觉不对,我是因为没找到直接在Guest Machine里面编译C程序的方法才这么做的。 在来宾机器上我没有 gcc,实际上如果我尝试调用它我会得到:
# -sh: gcc: not found
4] 我已经用命令
启动了模拟系统qemu-system-x86_64 \
-M pc \
-append 'root=/dev/vda $extra_append' \
-drive file=${images_dir}/rootfs.ext2,if=virtio,format=raw \
-kernel ${images_dir}/bzImage \
-m 256M \
-net nic,model=virtio \
-net user \
-enable-kvm \
-cpu host \
-smp threads=4
这是我从 Ciro Santilli's linux-kernel-module-cheat 中提取的。 rootfs.ext2 是系统启动的根文件系统,bzImage 是我将用来启动来宾机器的内核映像。
5] 客户机在 shell 中模拟。这是我所期望的,因为我只虚拟化裸内核而不是整个操作系统映像。
我显示了 buildroot 登录,我以 root 身份登录并 运行 以下命令:
当我尝试 运行 我的 hello 程序时,我显示了以下消息:
-sh: ./hello_world: not found
我想我的程序永远不会执行,即使我通过调试看到它作为 elf 文件加载到系统中也是如此。
如果我输入一个不存在的随机命令,我会得到同样的错误。
目标是让 hello_world 程序 运行 在 user-space 中,位于内核之上,我不希望它成为内核的一部分。
我同意你们的看法,我可能缺少一些库或一些基本的东西,但我不知道该怎么做。
您基本上可以 运行 一个进程而不是 init,如果您打算这样做的话。要 运行 进程而不是系统 init 应用程序,您可以替换原始 init 或将 init=/path/to/you/application 作为内核参数传递给您的 VM。
但是有几件事你应该记住:
所有库都需要存在于您的 VM 中与您在其上构建应用程序的系统相同的位置。这还包括它所针对的基本 c 库。您可以使用
获取所有这些库的列表ldd hello_world
举个例子,如果你编译下面的程序
#include <stdio.h>
int main(int argc, char** argv) {
printf("hello world!\n");
return 0;
}
对于 ldd
,您可能会得到这样的结果$ ldd test
linux-vdso.so.1 => (0x00007ffdba6ba000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8559f50000)
/lib64/ld-linux-x86-64.so.2 (0x000055fc58ece000)
如果替换init进程,需要注意console中断并妥善处理。如果你不这样做,你就会冒着非常糟糕的系统崩溃的风险。因此,如果您真的不知道自己在做什么,最好保留一个 init 进程并让该进程启动您的应用程序。
我以前看过你命令的输出。您是否使用 shell 脚本来启动您的应用程序?如果是这样,则可能表明您当前在虚拟环境中缺少某些库。
编辑:
也许您可以创建一个静态链接的应用程序,这意味着将所有库编译到同一个文件中。在 gcc 上,您可以为此使用 -static 开关。查看区别:
$ gcc -o test test.c
$ ldd test
linux-vdso.so.1 => (0x00007ffcd1f22000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f088d43a000)
/lib64/ld-linux-x86-64.so.2 (0x00005580f8a2b000)
$ gcc -static -o test test.c
$ ldd test
not a dynamic executable
如前所述,您可以使用 ldd 来查找所需的库。我通常使用的另一个选项是 objdump,因为输出更详细。
所以对于我的应用程序来说,这意味着这样的事情(我已经将输出缩短到相关部分):
$ objdump -p test
test: file format elf64-x86-64
[...]
Dynamic Section:
NEEDED libc.so.6
INIT 0x00000000004003c8
FINI 0x00000000004005c4
INIT_ARRAY 0x0000000000600e10
INIT_ARRAYSZ 0x0000000000000008
FINI_ARRAY 0x0000000000600e18
FINI_ARRAYSZ 0x0000000000000008
[...]
所以在我的例子中,就是 libc.so.6 我需要从主机上的 /lib/x86_64-linux-gnu/libc.so.6 复制到 /lib/x86_64-linux-gnu/libc.so.6虚拟机内.
希望对您有所帮助。