代码模板-Linux内核中如何启动一个kernel thread?(#include <linux/kthread.h> ; kthread_run(func, &data, “name“);)

背景

Linux内核模块中有些时候需要启动一个thread来处理事务。本文提供一个模板,方便未来快速取用。

机制用法

头文件:

#include <linux/kthread.h> 

接口:

static int my_kthread_func(void *data) {
    int count = 0;
    int *arg = (int *)data;
    while (!thread_should_stop) {
    	//sleep
    }
}

注册:
thread = kthread_run(my_kthread_func, &data, "my_kthread");

一般linux内核提供一个thread_should_stop来控制thread的开关。
另外就是在while循环中一定要sleep或者schedule否则会造成一直占用CPU,其他无法调度,甚至影响系统功能,包括网络和命令行。

代码模块

kthread_module.c:

#include <linux/module.h>    // 必须包含,支持模块操作
#include <linux/kernel.h>   // 必须包含,支持内核操作
#include <linux/init.h>     // 必须包含,支持模块初始化和清理操作
#include <linux/kthread.h>  // 必须包含,支持内核线程操作
#include <linux/delay.h>

MODULE_LICENSE("GPL");   // 模块许可证
MODULE_AUTHOR("Your Name"); // 作者名字
MODULE_DESCRIPTION("A simple kernel thread module with parameter"); // 模块描述
MODULE_VERSION("0.01"); // 模块版本

// 全局变量,用于停止内核线程
static int thread_should_stop = 0;

// 内核线程函数
static int my_kthread_func(void *data) {
    int count = 0;
    int *arg = (int *)data; // 将void*转换为int*类型,以便访问传递的参数
    while (!thread_should_stop) {
        printk(KERN_INFO "内核线程运行中,参数值为: %d\n", *arg);
	usleep_range(1000 * 1000, 1000 * 1000);
	count++;
	if (count == 5) {
		thread_should_stop = 1;
	}
    }
    printk(KERN_INFO "内核线程停止\n");
    return 0;
}

int data = 42; // 传递给内核线程的参数
// 初始化函数
static int __init kthread_module_init(void) {
    struct task_struct *thread;

    printk(KERN_INFO "内核模块加载成功\n");

    // 创建内核线程
    thread = kthread_run(my_kthread_func, &data, "my_kthread");
    if (IS_ERR(thread)) {
        printk(KERN_INFO "无法创建内核线程\n");
        return PTR_ERR(thread);
    }

    return 0;
}

// 清理函数
static void __exit kthread_module_exit(void) {
    printk(KERN_INFO "内核模块卸载\n");
    thread_should_stop = 1; // 发送停止信号
    // 这里需要停止并销毁内核线程,但示例中未包含这部分代码
    // 通常需要保存线程的句柄并在适当的时候调用kthread_stop(thread)
}

module_init(kthread_module_init);
module_exit(kthread_module_exit);

Makefile:

obj-m += kthread_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

实操

  • 编译:make
  • 加载:insmod kthread_module.ko
  • 卸载:rmmod kthread_module.ko
    在这里插入图片描述

综述

Linux内核模块中有些时候需要启动一个thread来处理事务。本文提供一个模板,方便未来快速取用。未来可以把这个接口封装一个pthread的方式来操作,这样同一份代码在内核态和用户态都能使用。

以下是我的代码#include <linux/module.h> #include <linux/kernel.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include <linux/ip.h> #include <linux/icmp.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/jiffies.h> #include <linux/spinlock.h> #include <linux/slab.h> // 内存分配 #include <linux/version.h> // 内核版本兼容性 #define MIN_SIZE 100 #define MAX_SIZE 1000 // 数据结构:存储Ping包统计信息 struct ping_stat { u64 timestamp; __be32 src_ip; }; // 全局统计结构 static struct { struct ping_stat *stats; unsigned int count; unsigned int max_count; spinlock_t lock; } ping_data; static int packet_len = 0; module_param(packet_len, int, S_IRUGO); // Netfilter钩子函数实现 static unsigned int ping_stat_hook( void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *ip_header; struct icmphdr *icmp_header; unsigned int data_size; // 校验包有效性 if (!skb || skb->pkt_type != PACKET_HOST) return NF_ACCEPT; ip_header = ip_hdr(skb); if (!ip_header || ip_header->protocol != IPPROTO_ICMP) return NF_ACCEPT; icmp_header = icmp_hdr(skb); if (!icmp_header || icmp_header->type != ICMP_ECHO) return NF_ACCEPT; // 计算ICMP数据部分大小 data_size = ntohs(ip_header->tot_len) - (ip_header->ihl * 4) - sizeof(struct icmphdr); if (packet_len != data_size) return NF_ACCEPT; // 获取时间戳并锁定共享数据 u64 now = ktime_get_real_ns(); spin_lock(&ping_data.lock); // 记录统计信息 ping_data.stats[ping_data.count].timestamp = now; ping_data.stats[ping_data.count].src_ip = ip_header->saddr; ping_data.count++; spin_unlock(&ping_data.lock); return NF_ACCEPT; } // Netfilter钩子配置 static struct nf_hook_ops nfho = { .hook = ping_stat_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_FIRST, }; // /proc文件输出函数 static int ping_stat_proc_show(struct seq_file *m, void *v) { unsigned int i; char ip_str[16]; spin_lock(&ping_data.lock); seq_printf(m, "%-10s %-20s %-16s\n", "PacketSeq", "Timestamp(ns)", "Source IP"); for (i = 0; i < ping_data.count; i++) { snprintf(ip_str, 16, "%pI4", &ping_data.stats[i].src_ip); seq_printf(m, "%-10zu %-20llu %-16s\n", i+1, ping_data.stats[i].timestamp, ip_str); } spin_unlock(&ping_data.lock); return 0; } static int ping_stat_proc_open(struct inode *inode, struct file *file) { return single_open(file, ping_stat_proc_show, NULL); } static const struct file_operations ping_stat_fops = { .owner = THIS_MODULE, .open = ping_stat_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; // 模块初始化函数 static int __init ping_stat_init(void) { printk(KERN_INFO "Ping统计模块初始化, packet_len=%d\n", packet_len); // 参数校验 if (packet_len == 0) { printk(KERN_ERR "错误:必须指定packet_len参数!\n"); return -EINVAL; } if (packet_len < MIN_SIZE || packet_len > MAX_SIZE) { printk(KERN_ERR "包长度必须在%d-%d字节之间\n", MIN_SIZE, MAX_SIZE); return -EINVAL; } // 初始化数据结构 spin_lock_init(&ping_data.lock); ping_data.count = 0; ping_data.max_count = 100; ping_data.stats = kcalloc(ping_data.max_count, sizeof(struct ping_stat), GFP_KERNEL); if (!ping_data.stats) return -ENOMEM; // 注册Netfilter钩子 if (nf_register_net_hook(&init_net, &nfho) != 0) { kfree(ping_data.stats); printk(KERN_ERR "Netfilter钩子注册失败\n"); return -EFAULT; } // 创建/proc文件(兼容4.15) proc_create("ping_stat", 0, NULL, &ping_stat_fops); printk(KERN_INFO "Ping统计模块已加载\n"); return 0; } // 模块退出函数 static void __exit ping_stat_exit(void) { nf_unregister_net_hook(&init_net, &nfho); remove_proc_entry("ping_stat", NULL); if (ping_data.stats) kfree(ping_data.stats); printk(KERN_INFO "Ping统计模块已卸载\n"); } module_init(ping_stat_init); module_exit(ping_stat_exit); MODULE_LICENSE("GPL"); 请根据代码回答下面的问题 4. 系统构架描述 4.1. 概述 4.2. 模块结构 5. 任务(或进程/线程)设计 5.1. 原因 5.2. 任务(或进程/线程)内部流程 5.3. 任务(或进程/线程)间通信
08-13
root@4f90412002c9:/eap/PLATFORM/build# make BOARD_TYPE=en7529_EAP61xGPv1_common PID=eap WEB_PAGES_ID=VI_2.0 cpu_timer.ko Makefile.flags:54: "userspace cpu bit width: 64:, 32:y" Makefile.flags:61: "kernel cpu bit width: 64:, 32:y" Makefile:41: "product_configs/en7529_EAP61xGPv1_common/spec/CT/custom.mk not found!" ecn/Makefile.sdk:33: "/eap/PLATFORM/build/../build/product_configs/en7529_EAP61xGPv1_common/config.sdk Checked" ecn/Makefile.sdk:35: "/eap/PLATFORM/build/../build/product_configs/en7529_EAP61xGPv1_common/config.bba Checked" ecn/Makefile.sdk:87: "BOOT_LED_CONF" /eap/PLATFORM/build/../sdk/ecn/en7529/bootloader/Uboot/u-boot-2014.04-rc1/include/tp_led_gpio_def.h ecn/Makefile.sdk:88: "UBOOT_DIR" /eap/PLATFORM/build/../sdk/ecn/en7529/bootloader/Uboot make[1]: Entering directory '/eap/PLATFORM/sdk/ecn/en7529/linux-4.4.115' CC [M] /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.o LD [M] /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpu_timer.o Building modules, stage 2. MODPOST 1 modules CC /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpu_timer.mod.o LD [M] /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpu_timer.ko make[1]: Leaving directory '/eap/PLATFORM/sdk/ecn/en7529/linux-4.4.115' built and installed <cpu_timer.ko> at Tue Oct 14 11:41:44 CST 2025. root@4f90412002c9:/eap/PLATFORM/build# make BOARD_TYPE=en7529_EAP61xGPv1_common PID=eap WEB_PAGES_ID=VI_2.0 cpu_timer.ko Makefile.flags:54: "userspace cpu bit width: 64:, 32:y" Makefile.flags:61: "kernel cpu bit width: 64:, 32:y" Makefile:41: "product_configs/en7529_EAP61xGPv1_common/spec/CT/custom.mk not found!" ecn/Makefile.sdk:33: "/eap/PLATFORM/build/../build/product_configs/en7529_EAP61xGPv1_common/config.sdk Checked" ecn/Makefile.sdk:35: "/eap/PLATFORM/build/../build/product_configs/en7529_EAP61xGPv1_common/config.bba Checked" ecn/Makefile.sdk:87: "BOOT_LED_CONF" /eap/PLATFORM/build/../sdk/ecn/en7529/bootloader/Uboot/u-boot-2014.04-rc1/include/tp_led_gpio_def.h ecn/Makefile.sdk:88: "UBOOT_DIR" /eap/PLATFORM/build/../sdk/ecn/en7529/bootloader/Uboot make[1]: Entering directory '/eap/PLATFORM/sdk/ecn/en7529/linux-4.4.115' CC [M] /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.o /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.c:18:23: error: unknown type name 'useconds_t' static void busy_wait(useconds_t usecs) ^ /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.c: In function 'cpu0_stress_func': /eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.c:45:13: warning: implicit declaration of function 'busy_wait' [-Wimplicit-function-declaration] busy_wait(busy_us); ^ scripts/Makefile.build:269: recipe for target '/eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.o' failed make[2]: *** [/eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer/cpuTimer.o] Error 1 Makefile:1439: recipe for target '_module_/eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer' failed make[1]: *** [_module_/eap/PLATFORM/build/../smb_priv/kernel_modules/cpu_timer] Error 2 make[1]: Leaving directory '/eap/PLATFORM/sdk/ecn/en7529/linux-4.4.115' Makefile.smb_priv:626: recipe for target 'cpu_timer.ko_build' fai
10-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值