首页 文章

如何在最小的Linux内核环境中启动进程?

提问于
浏览
1

是否可以在裸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 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身份登录并运行以下命令:

Virtualized guest

当我尝试运行我的hello程序时,我显示以下消息:

-sh: ./hello_world: not found

我想我的程序永远不会执行,即使我已经通过调试看到它作为elf文件加载到系统中 .

如果我输入一个不存在的随机命令,我会得到同样的错误 .

目标是让hello_world程序在用户空间中运行,在内核之上,我不希望它成为它的一部分 .

我同意你们的意见,我可能错过了一些图书馆或基本的东西,但我不知道该怎么做 .

1 回答

  • 1

    如果你打算这样做,你基本上可以运行一个进程而不是init . 要运行进程而不是系统init应用程序,您可以替换原始init或将init = / path /传递给/ 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进程,则需要处理控制台中断并正确处理它们 . 如果你不这样做,你会冒很大的系统崩溃风险 . 因此,如果您真的不知道自己在做什么,最好保留一个init进程并让该进程启动您的应用程序 .

    我过去看过你的命令输出 . 您是否使用shell脚本来启动应用程序?如果是这样,它可能表示您当前缺少虚拟环境中的某些库 .

    Edit:

    也许你可以改为创建一个静态链接的应用程序,这意味着将所有库编译到同一个文件中 . 在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在虚拟机中 .

    希望有所帮助 .

相关问题