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

查看:325
本文介绍了如何从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?

推荐答案

最小的可运行示例

在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内核为模块插入提供了两个系统调用:

The Linux kernel offers two system calls for module insertion:

  • init_module
  • finit_module
  • 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函数

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函数

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.

如果失败,则使用xmalloc_open_zipped_read_close将文件malloc存入内存.

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明智地遵循了该建议,并使用了glibc提供的syscall,并提供了用于系统调用的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天全站免登陆