29. init_module - 加载内核模块 Link to heading

函数介绍 Link to heading

init_module系统调用用于将编译好的内核模块加载到内核空间中。它是现代Linux系统中加载内核模块的核心系统调用。

函数原型 Link to heading

#include <linux/module.h>
#include <sys/syscall.h>

int init_module(void *module_image, unsigned long len, const char *param_values);

功能 Link to heading

将内核模块映像加载到内核并初始化模块。

参数 Link to heading

  • void *module_image: 指向模块二进制映像的指针
  • unsigned long len: 模块映像的长度(字节)
  • const char *param_values: 模块参数字符串(如"name=value")

返回值 Link to heading

  • 成功时返回0
  • 失败时返回-1,并设置errno

特殊限制 Link to heading

  • 需要root权限或CAP_SYS_MODULE能力
  • 模块必须与内核版本匹配
  • 模块签名验证(如果启用)
  • 不能加载已存在的同名模块

相似函数 Link to heading

  • finit_module(): 通过文件描述符加载模块
  • delete_module(): 卸载内核模块
  • 命令行工具insmod

示例代码 Link to heading

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <linux/module.h>

// init_module系统调用包装
static inline long init_module_wrapper(void *module_image, unsigned long len, 
                                      const char *param_values) {
    return syscall(__NR_init_module, module_image, len, param_values);
}

// finit_module系统调用包装
static inline long finit_module_wrapper(int fd, const char *param_values, int flags) {
    return syscall(__NR_finit_module, fd, param_values, flags);
}

int main() {
    printf("=== Init_module函数示例 ===\n");
    
    // 示例1: 基本使用说明
    printf("示例1: 基本使用说明\n");
    printf("注意:需要root权限才能加载内核模块\n");
    printf("普通用户应使用insmod命令\n\n");
    
    // 示例2: 错误处理演示
    printf("示例2: 错误处理演示\n");
    
    // 尝试加载无效模块
    if (init_module_wrapper(NULL, 0, "") == -1) {
        printf("加载无效模块: %s\n", strerror(errno));
        // 通常返回EINVAL(参数无效)
    }
    
    // 尝试加载不存在的模块文件
    int fd = open("/nonexistent/module.ko", O_RDONLY);
    if (fd == -1) {
        printf("打开模块文件失败: %s\n", strerror(errno));
    }
    
    // 示例3: 常见错误类型
    printf("示例3: 常见错误类型\n");
    printf("init_module可能的错误:\n");
    printf("- EPERM: 权限不足(需要root或CAP_SYS_MODULE)\n");
    printf("- EINVAL: 参数无效或模块映像损坏\n");
    printf("- ENOENT: 模块文件不存在\n");
    printf("- EEXIST: 同名模块已加载\n");
    printf("- ENOMEM: 内存不足\n");
    printf("- EFAULT: 指针地址无效\n");
    printf("- ENOEXEC: 模块格式不正确\n\n");
    
    // 示例4: 模块参数传递
    printf("示例4: 模块参数传递\n");
    printf("模块参数格式示例:\n");
    printf("- \"debug=1\": 设置debug参数为1\n");
    printf("- \"name=value size=1024\": 多个参数\n");
    printf("- \"\": 空参数字符串\n\n");
    
    // 示例5: finit_module vs init_module
    printf("示例5: finit_module vs init_module\n");
    printf("init_module: 传入内存中的模块映像\n");
    printf("finit_module: 通过文件描述符加载模块\n");
    printf("finit_module优势: 避免大内存分配\n\n");
    
    // 示例6: 安全考虑
    printf("示例6: 安全考虑\n");
    printf("安全使用建议:\n");
    printf("1. 确保有足够的权限\n");
    printf("2. 验证模块来源可信\n");
    printf("3. 检查模块与内核版本兼容性\n");
    printf("4. 生产环境建议使用modprobe命令\n\n");
    
    // 示例7: 实际使用场景
    printf("示例7: 实际使用场景\n");
    printf("系统管理员加载驱动模块:\n");
    printf("// 1. 读取模块文件到内存\n");
    printf("// 2. 调用init_module加载\n");
    printf("// 3. 验证加载结果\n\n");
    
    // 演示检查模块状态的方法
    printf("检查模块状态的方法:\n");
    printf("- 查看/proc/modules文件\n");
    printf("- 使用lsmod命令\n");
    printf("- 检查/sys/module/目录\n\n");
    
    return 0;
}