你已经详细介绍了 fchown
和 fchownat
两个系统调用的功能、参数、使用方式及示例代码。接下来,你提到“函数列表:finit_module”,这表示你可能希望继续介绍 Linux 系统调用中的 finit_module
函数。
下面我将为你继续补充关于 finit_module
的内容,保持与前面一致的格式和风格:
68. finit_module - 从文件描述符加载并初始化内核模块 Link to heading
1. 函数介绍 Link to heading
finit_module
是一个 Linux 系统调用,用于从文件描述符加载并初始化一个内核模块。它是 init_module
的增强版本,通过使用文件描述符而非路径名来加载模块,避免了某些竞态条件(如符号链接攻击或路径解析问题)。
你可以把它想象成通过“钥匙孔”而不是“门牌号”进入房子,这样即使地址被篡改,你仍然可以通过正确的钥匙孔进入。
2. 函数原型 Link to heading
#define _GNU_SOURCE
#include <linux/module.h>
#include <sys/syscall.h>
#include <unistd.h>
long finit_module(int fd, const char *param_values, int flags);
注意:该函数不是标准 C 库的一部分,需要通过
syscall()
调用或者包含 glibc 2.34+ 版本后才能直接调用。
3. 功能 Link to heading
- 加载指定的内核模块(通过文件描述符指向
.ko
文件) - 使用提供的参数初始化模块
- 支持额外的标志控制模块加载行为
4. 参数 Link to heading
int fd
: 指向模块文件(通常是.ko
格式)的已打开文件描述符const char *param_values
: 模块加载时传递的参数字符串,格式为"param1=value1 param2=value2"
;若无参数则传入NULL
int flags
: 控制模块加载行为的标志位0
: 默认行为MODULE_INIT_IGNORE_MODVERSIONS
: 忽略模块版本检查MODULE_INIT_IGNORE_VERMAGIC
: 忽略模块魔数校验(vermagic)
5. 返回值 Link to heading
- 成功时返回 0
- 失败时返回 -1,并设置
errno
EPERM
: 权限不足(需要CAP_SYS_MODULE
能力)EINVAL
: 参数错误或模块格式无效EEXIST
: 模块已存在ENOENT
: 模块文件不存在ENOMEM
: 内存不足- 其他常见错误码:
EACCES
,EISDIR
,EBADF
等
6. 相似函数,或关联函数 Link to heading
init_module()
: 通过路径名加载模块(旧接口)delete_module()
: 卸载内核模块syscall(SYS_finit_module, ...)
: 在较老的 glibc 中使用系统调用号调用/sbin/insmod
,/sbin/rmmod
: 用户态工具,用于加载/卸载模块
7. 示例代码 Link to heading
示例1:基本使用 - 加载内核模块 Link to heading
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <string.h>
#ifndef SYS_finit_module
# define SYS_finit_module 313 // x86_64 架构下的系统调用号
#endif
int main() {
int fd;
long ret;
// 打开模块文件(假设模块文件存在)
fd = open("example_module.ko", O_RDONLY);
if (fd == -1) {
perror("打开模块文件失败");
exit(EXIT_FAILURE);
}
printf("成功打开模块文件,fd = %d\n", fd);
// 使用 finit_module 加载模块(无参数)
ret = syscall(SYS_finit_module, fd, "", 0);
if (ret == -1) {
if (errno == EPERM) {
printf("权限不足:需要 CAP_SYS_MODULE 权限\n");
} else {
perror("finit_module 调用失败");
}
} else {
printf("模块加载成功\n");
}
close(fd);
return 0;
}
示例2:带参数加载模块 Link to heading
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <string.h>
#ifndef SYS_finit_module
# define SYS_finit_module 313
#endif
int main() {
int fd;
long ret;
const char *params = "param1=123 param2=hello";
fd = open("my_module.ko", O_RDONLY);
if (fd == -1) {
perror("打开模块文件失败");
exit(EXIT_FAILURE);
}
printf("加载模块并传递参数: %s\n", params);
ret = syscall(SYS_finit_module, fd, params, 0);
if (ret == -1) {
perror("finit_module 调用失败");
} else {
printf("模块加载成功\n");
}
close(fd);
return 0;
}
注意事项 Link to heading
- 需要超级用户权限或拥有
CAP_SYS_MODULE
能力才能执行此操作 - 模块必须与当前运行的内核兼容(架构、版本等)
- 不推荐在生产环境中动态加载不可信模块,存在安全风险
总结 Link to heading
finit_module
是现代 Linux 内核中推荐使用的模块加载方式,相比 init_module
更加安全可靠。它通过文件描述符的方式加载模块,有效避免路径相关的安全隐患。在编写涉及内核模块管理的程序时,建议优先使用 finit_module
并妥善处理权限与错误情况。
如果你希望继续介绍其他系统调用,例如 delete_module
, pivot_root
, mount
, umount2
, setns
, 或者用户态工具链相关内容,也可以告诉我,我可以继续为你扩展。