C 如何在最小的linux内核环境中启动进程?
是否可以在裸linux内核上运行通用C程序 我使用的是带有Qemu的虚拟化linux最小系统,在这里我加载了自定义内核和根文件系统,但显然无法运行我的用户空间hello world程序 编辑1:我已经更新了问题,使其更加详细。以下是我遵循的步骤: 1] 我已在主机上使用以下命令编译hello_world程序:C 如何在最小的linux内核环境中启动进程?,c,linux,bash,linux-kernel,qemu,C,Linux,Bash,Linux Kernel,Qemu,是否可以在裸linux内核上运行通用C程序 我使用的是带有Qemu的虚拟化linux最小系统,在这里我加载了自定义内核和根文件系统,但显然无法运行我的用户空间hello world程序 编辑1:我已经更新了问题,使其更加详细。以下是我遵循的步骤: 1] 我已在主机上使用以下命令编译hello_world程序: gcc hello_world.c -o hello_world 源代码如下所示: #include <stdio.h> int main(int argc, char c
gcc hello_world.c -o hello_world
源代码如下所示:
#include <stdio.h>
int main(int argc, char const *argv[]) {
printf("Hello world!\n");
return 0;
}
$ 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构建了根文件系统,它默认来自。我已经编译了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/
这感觉不对,我这么做是因为我还没有找到一种直接在客户机内部编译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
这是我从你那里拿走的。
ext2是系统启动时所针对的根文件系统,bzImage是我将启动来宾机的内核映像
5] 来宾计算机在shell中进行仿真。这正是我所期望的,因为我只虚拟了裸内核,而不是整个操作系统映像
显示buildroot登录,我以root身份登录并运行以下命令:
当我尝试运行hello程序时,会显示以下消息:
-sh: ./hello_world: not found
我假设我的程序从未执行过,即使我通过调试看到它作为elf文件加载到系统中
如果我键入一个不存在的随机命令,我会得到相同的错误
目标是让hello_world程序在用户空间中运行,在内核之上,我不希望它成为它的一部分
我同意你们的观点,我可能缺少一些库或一些基本的东西,但我不知道该怎么做。如果你打算这样做,你基本上可以运行一个进程而不是init。要运行进程而不是systems 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;
}
如果替换init进程,则需要处理控制台中断并正确处理它们。如果您不这样做,您将面临严重的系统崩溃风险。因此,如果您不知道自己在做什么,最好保留一个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,因为输出更详细
因此,对于我的应用程序,这意味着类似这样的内容(我已将输出缩短到相关部分):
在我的例子中,这就是libc.So.6,我需要将主机上的/lib/x86_64-linux-gnu/libc.So.6复制到虚拟机中的/lib/x86_64-linux-gnu/libc.So.6
希望有帮助。您应该检查文件上的执行标志。默认的输出文件是./a.out,使用-o,您可以更改输出文件。
rootfs.ext2
来自哪里?你是如何编写你的“hello world”程序的?如果要构建动态二进制文件,则需要确保虚拟环境包含与构建环境相同的ld.so
和其他库。如果您正在构建一个静态二进制文件,这不会是一个问题。init
是由裸内核启动的用户空间程序。你可以用你想要的任何东西来代替它。在您的例子中,您正在启动一个shellsh
。当您尝试在shell下运行程序时,它会告诉您没有在正确的目录中。@stark,这是一条有点混乱的错误消息,我认为上面的larsks是对的,它是关于库和加载程序的。谢谢大家的建议!为了更精确,我修改了这个问题!我正在使用shell脚本来启动Qemu。我认为您关于缺少库的说法是对的,可能在虚拟化系统中,我缺少了所有东西,因为它只使用最少的内核代码,而没有其他任何东西。你知道我如何添加这些最小的功能,让我的hello_程序运行吗?为了更准确,我在问题中添加了更多的信息。我扩展了我的答案。我建议您在应用程序上使用ldd或objdump或其他工具来列出所需的库。另一个(可能更容易)的选择是静态链接它。我用-static标志重新编译了我的程序,现在它在客户机上正确运行。我还检查了来宾计算机中的/lib文件夹以及所需的.so文件,以便正确运行该程序。非常感谢!
$ 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
$ 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
[...]