如何从C代码加载Linux内核模块?
我有一个同时具有两个外部内核模块和一个用户空间守护程序的应用程序。我想在启动时从用C编写的守护程序代码中加载模块,然后在干净退出时将其卸载。我可以用比system("modprobe
module");使用相应的方式更干净的方式加载它们rmmod
吗?
回答:
使用此简单的参数打印机模块,在QEMU
+ Buildroot VM和Ubuntu 16.04主机上进行了测试。
我们使用init_module
/ finit_module
和remove_module
Linux系统调用。
Linux内核为模块插入提供了两个系统调用:
init_module
finit_module
和:
man init_module
证明文件:
系统调用finit_module()类似于init_module(),但是从文件描述符fd中读取要加载的模块。当可以从内核模块在文件系统中的位置确定内核模块的真实性时,它很有用;在可能的情况下,可以避免使用经过密码签名的模块来确定模块的真实性的开销。param_values参数与init_module()相同。
finit
是较新的版本,仅在v3.8中添加。更多理由:https :
//lwn.net/Articles/519010/
glibc似乎没有为他们提供C包装器,因此我们仅使用创建自己的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提供了insmod
,并且由于它是为极简主义而设计的,因此我们可以尝试从那里推断出它是如何完成的。
在版本1.24.2中,入口点位于modutils/insmod.c
function上insmod_main
。
该IF_FEATURE_2_4_MODULES
是对老年人的Linux内核2.4模块可选支持,所以我们就可以忽略它。
这只是modutils.c
功能bb_init_module
。
bb_init_module
尝试两件事:
mmap
该文件通过进入内存try_to_mmap_module
。
这总是设置image_size
为.ko
文件的大小,这是一个副作用。
- 如果失败,则使用
malloc
将文件保存到内存xmalloc_open_zipped_read_close
。
如果该文件是zip,则此函数可以选择先解压缩文件,否则仅对malloc进行解压缩。
我不明白为什么要执行此压缩业务,因为我们甚至不能依靠它,因为try_to_mmap_module
似乎无法解压缩。
终于来了电话:
init_module(image, image_size, options);
image
放在内存中的可执行文件在哪里,而选项就像""
我们在调用时insmod file.elf
不带其他参数的情况一样。
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
。
如果它不存在,我认为是glibc,但是man init_module
说:
glibc不支持init_module()系统调用。glibc标头中未提供任何声明,但经过一段古怪的历史,glibc确实为此系统调用导出了一个ABI。因此,为了使用此系统调用,在代码中手动声明接口就足够了。或者,您可以使用syscall(2)调用系统调用。
明智地使用BusyBox的建议并使用syscall
glibc提供的建议和使用,并为系统调用提供C API。
以上是 如何从C代码加载Linux内核模块? 的全部内容, 来源链接: utcz.com/qa/426015.html