Linux模块与系统调用

模块与系统调用

1. 编写内核模块代码

首先,编写一个简单的“Hello World”内核模块文件 hello_module.c

#include <linux/init.h>     // 用于宏 __init 和 __exit
#include <linux/module.h>   // 用于模块编程基本宏
#include <linux/kernel.h>   // 用于 printk 宏

MODULE_LICENSE("GPL");               // 指定模块许可证
MODULE_AUTHOR("20242826");          // 指定模块作者
MODULE_DESCRIPTION("A simple hello world kernel module"); // 模块描述
MODULE_VERSION("1.0");               // 模块版本

// 初始化函数
static int __init hello_init(void) {
    printk(KERN_INFO "Hello, Kernel!\n"); // 向内核日志打印信息
    return 0;  // 返回0表示成功
}

// 清理函数
static void __exit hello_exit(void) {
    printk(KERN_INFO "Goodbye, Kernel!\n"); // 向内核日志打印信息
}

// 注册初始化和清理函数
module_init(hello_init);
module_exit(hello_exit);

在这个模块中:

  • hello_init 函数在模块加载时运行。
  • hello_exit 函数在模块卸载时运行。
  • printk 是用于内核日志输出的函数,类似于用户空间的 printf

2. 创建Makefile文件

创建一个 Makefile,用来编译内核模块。文件内容如下:

# Makefile
obj-m += hello_module.o   # 指定编译生成的目标对象

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

在这个 Makefile 中:

  • obj-m += hello_module.o 指定生成模块的目标文件。
  • make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 命令用当前内核的编译环境编译模块。
  • clean 命令用于清理编译生成的文件。

3. 编译内核模块

在包含 hello_module.cMakefile 的目录中,运行以下命令来编译模块:

make

编译成功后,将生成 hello_module.ko 文件,这就是编译完成的内核模块文件。

01

4. 加载和卸载内核模块

加载模块

要加载模块,可以使用 insmod 命令:

sudo insmod hello_module.ko

成功加载模块后,内核日志中会出现 Hello, Kernel! 的信息。可以通过以下命令查看内核日志:

sudo dmesg | tail

02

卸载模块

要卸载模块,可以使用 rmmod 命令:

sudo rmmod hello_module

卸载模块后,内核日志中会显示 Goodbye, Kernel! 信息。
03

5.编译模块实现输出当前进程信息的功能

#include <linux/init.h>      // 用于宏 __init 和 __exit
#include <linux/module.h>    // 用于模块编程基本宏
#include <linux/kernel.h>    // 用于 printk 宏
#include <linux/sched.h>     // 用于获取当前进程信息
#include <linux/sched/signal.h>

MODULE_LICENSE("GPL");               // 模块许可证
MODULE_AUTHOR("20242826");          // 模块作者
MODULE_DESCRIPTION("A module to print current process info"); // 模块描述
MODULE_VERSION("1.0");               // 模块版本

// 初始化函数
static int __init module2_init(void) {
    printk(KERN_INFO "Module2 loaded\n");
    printk(KERN_INFO "Current Process Info:\n");
    printk(KERN_INFO "Process ID: %d\n", current->pid);
    printk(KERN_INFO "Process Name: %s\n", current->comm);
    return 0;  // 返回0表示成功
}

// 清理函数
static void __exit module2_exit(void) {
    printk(KERN_INFO "Module2 unloaded\n");
}

// 注册初始化和清理函数
module_init(module2_init);
module_exit(module2_exit);

代码说明

  • current 是一个指向当前执行进程的 task_struct 指针,task_struct 是包含进程信息的内核结构体。
  • current->pid 表示当前进程的ID。
  • current->comm 表示当前进程的名称。
# Makefile
obj-m += module2.o   # 指定编译生成的目标对象

all:
	# 使用 Tab 键缩进
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	# 使用 Tab 键缩进
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

make
sudo insmod module2.ko
sudo dmesg | tail
# 卸载模块
sudo rmmod module2

04

6.编译模块实现读取进程链表的功能

#include <linux/init.h>        // 用于宏 __init 和 __exit
#include <linux/module.h>      // 用于模块编程基本宏
#include <linux/kernel.h>      // 用于 printk 宏
#include <linux/sched.h>       // 用于 task_struct 结构体
#include <linux/sched/signal.h> // 用于 next_task 宏

MODULE_LICENSE("GPL");
MODULE_AUTHOR("20242826");
MODULE_DESCRIPTION("A module to list all processes in the process list");
MODULE_VERSION("1.0");

static int __init module3_init(void) {
    struct task_struct *task;

    printk(KERN_INFO "Module3 loaded: Listing all processes\n");

    // 遍历进程链表
    for_each_process(task) {
        printk(KERN_INFO "Process: %s [PID: %d]\n", task->comm, task->pid);
    }

    return 0;
}

static void __exit module3_exit(void) {
    printk(KERN_INFO "Module3 unloaded\n");
}

module_init(module3_init);
module_exit(module3_exit);

代码说明

  • for_each_process(task) 是 Linux 内核提供的一个宏,用于遍历所有进程。它会依次遍历所有 task_struct 结构体,将每个进程的指针赋值给 task 变量。
  • task->comm 存储进程名称,task->pid 存储进程 ID。
    该模块在加载时输出所有进程的名称和进程ID。
# Makefile
obj-m += module3.o   # 指定编译生成的目标对象

all:
	# 使用 Tab 键缩进
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	# 使用 Tab 键缩进
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

make
sudo insmod module3.ko
sudo dmesg | tail -n 50
# 卸载模块
sudo rmmod module3

05

7. 清理编译生成的文件

执行以下命令清理生成的文件:

make clean

总结

以上步骤完成了Linux内核模块编写、编译、加载和卸载的流程。

posted @ 2025-01-02 11:29  Arisf  阅读(14)  评论(0编辑  收藏  举报