如何从 C 代码加载 Linux 内核模块? [英] How to load Linux kernel modules from C code?

查看:20
本文介绍了如何从 C 代码加载 Linux 内核模块?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序,它具有两个外部内核模块和一个用户空间守护程序.我想在启动时从用 C 编写的守护程序代码加载模块,并在干净退出时卸载它们.我可以用比执行 system("modprobe module"); 更简洁的方式加载它们并使用相应的 rmmod 卸载它们吗?

I have an application that has both two external kernel modules and a userspace daemon. I want to load the modules from the daemon code, written in C, at startup, and unload them on clean exit. Can I load them in a cleaner way than doing system("modprobe module"); and unload them using the corresponding rmmod?

推荐答案

init_module/remove_module 最小可运行示例

init_module / remove_module minimal runnable example

在 QEMU + Buildroot VM 和 Ubuntu 16.04 主机上测试 使用这个简单的参数打印机模块 .

Tested on a QEMU + Buildroot VM and Ubuntu 16.04 host with this simple parameter printer module .

我们使用 init_module/finit_moduleremove_module Linux 系统调用.

We use the init_module / finit_module and remove_module Linux system calls.

Linux 内核提供了两个用于模块插入的系统调用:

The Linux kernel offers two system calls for module insertion:

  • init_module
  • finit_module

和:

man init_module

文件:

finit_module() 系统调用类似于 init_module(),但从文件描述符 fd 中读取要加载的模块.当内核模块的真实性可以从它在文件系统中的位置确定时,它很有用;在可能的情况下,可以避免使用加密签名模块来确定模块真实性的开销.param_values 参数与 init_module() 相同.

The finit_module() system call is like init_module(), but reads the module to be loaded from the file descriptor fd. It is useful when the authenticity of a kernel module can be determined from its location in the filesystem; in cases where that is possible, the overhead of using cryptographically signed modules to determine the authenticity of a module can be avoided. The param_values argument is as for init_module().

finit 较新,仅在 v3.8 中添加.更多理由:https://lwn.net/Articles/519010/

finit is newer and was added only in v3.8. More rationale: https://lwn.net/Articles/519010/

glibc 似乎没有为它们提供 C 包装器,所以我们只是用 syscall 创建我们自己的.

glibc does not seem to provide a C wrapper for them, so we just create our own with syscall.

insmod.c

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

#define init_module(module_image, len, param_values) syscall(__NR_init_module, module_image, len, param_values)
#define finit_module(fd, param_values, flags) syscall(__NR_finit_module, fd, param_values, flags)

int main(int argc, char **argv) {
    const char *params;
    int fd, use_finit;
    size_t image_size;
    struct stat st;
    void *image;

    /* CLI handling. */
    if (argc < 2) {
        puts("Usage ./prog mymodule.ko [args="" [use_finit=0]");
        return EXIT_FAILURE;
    }
    if (argc < 3) {
        params = "";
    } else {
        params = argv[2];
    }
    if (argc < 4) {
        use_finit = 0;
    } else {
        use_finit = (argv[3][0] != '0');
    }

    /* Action. */
    fd = open(argv[1], O_RDONLY);
    if (use_finit) {
        puts("finit");
        if (finit_module(fd, params, 0) != 0) {
            perror("finit_module");
            return EXIT_FAILURE;
        }
        close(fd);
    } else {
        puts("init");
        fstat(fd, &st);
        image_size = st.st_size;
        image = malloc(image_size);
        read(fd, image, image_size);
        close(fd);
        if (init_module(image, image_size, params) != 0) {
            perror("init_module");
            return EXIT_FAILURE;
        }
        free(image);
    }
    return EXIT_SUCCESS;
}

GitHub 上游.

rmmod.c

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

#define delete_module(name, flags) syscall(__NR_delete_module, name, flags)

int main(int argc, char **argv) {
    if (argc != 2) {
        puts("Usage ./prog mymodule");
        return EXIT_FAILURE;
    }
    if (delete_module(argv[1], O_NONBLOCK) != 0) {
        perror("delete_module");
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

GitHub 上游.

Busybox 源代码解释

Busybox 提供了 insmod,而且由于它是为极简主义设计的,我们可以尝试从那里推断出它是如何完成的.

Busybox provides insmod, and since it is designed for minimalism, we can try to deduce how it is done from there.

在 1.24.2 版中,入口点位于 modutils/insmod.c 函数 insmod_main.

On version 1.24.2, the entry point is at modutils/insmod.c function insmod_main.

IF_FEATURE_2_4_MODULES 是对旧版 Linux 内核 2.4 模块的可选支持,所以我们现在可以忽略它.

The IF_FEATURE_2_4_MODULES is optional support for older Linux kernel 2.4 modules, so we can just ignore it for now.

这只是转发到 modutils.c 函数 bb_init_module.

That just forwards to modutils.c function bb_init_module.

bb_init_module 尝试两件事:

  • mmap通过try_to_mmap_module将文件映射到内存.

  • mmap the file to memory through try_to_mmap_module.

这总是将 image_size 设置为 .ko 文件的大小作为副作用.

This always sets image_size to the size of the .ko file as a side effect.

如果失败,malloc 使用xmalloc_open_zipped_read_close 将文件放到内存中.

if that fails, malloc the file to memory with xmalloc_open_zipped_read_close.

如果文件是 zip,此函数可选择先解压缩文件,否则只对其进行 malloc.

This function optionally unzips the file first if it is a zip, and just mallocs it otherwise.

我不明白为什么要完成这项压缩业务,因为我们甚至不能依赖它,因为 try_to_mmap_module 似乎无法解压缩.

I don't understand why this zipping business is done, since we can't even rely on it because the try_to_mmap_module does not seem to unzip things.

终于来电话了:

init_module(image, image_size, options);

其中 image 是放入内存的可执行文件,如果我们调用 insmod file.elf,则选项只是 ""没有进一步的争论.

where image is the executable that was put into memory, and options are just "" if we call insmod file.elf without further arguments.

init_module 由上面提供:

#ifdef __UCLIBC__
extern int init_module(void *module, unsigned long len, const char *options);
extern int delete_module(const char *module, unsigned int flags);
#else
# include <sys/syscall.h>
# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
#endif

ulibc 是一个嵌入式的 libc 实现,它似乎提供了 init_module.

ulibc is an embedded libc implementation, and it seems to provide init_module.

如果它不存在,我认为 glibc 是假定的,但正如 man init_module 所说:

If it is not present, I think glibc is assumed, but as man init_module says:

glibc 不支持 init_module() 系统调用.glibc 头文件中没有提供声明,但是,通过历史的怪癖,glibc 确实导出了一个 ABI这个系统调用.因此,为了使用这个系统调用,在你的代码中手动声明接口就足够了;或者,您可以调用使用 syscall(2) 的系统调用.

The init_module() system call is not supported by glibc. No declaration is provided in glibc headers, but, through a quirk of history, glibc does export an ABI for this system call. Therefore, in order to employ this system call, it is sufficient to manually declare the interface in your code; alternatively, you can invoke the system call using syscall(2).

BusyBox 明智地遵循了该建议并使用了 syscall,它由 glibc 提供,并为系统调用提供了 C API.

BusyBox wisely follows that advice and uses syscall, which glibc provides, and which offers a C API for system calls.

这篇关于如何从 C 代码加载 Linux 内核模块?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆