/proc 文件操作 create_proc_entry remove_proc_entry

074121_4O1g_274829.png

074210_RiWc_274829.png

075205_Ipmb_274829.png

主要功能是,加载模块时先调用 proc_init;创建文件在proc 目录下,然后,read_proc回调procfile_read()

sprintf()将数值写入文件,运行.ko文件,会在/proc目录创建proctest文件,cat /proc/proctest 查看文件数据会得到helloworld!

最后卸载模块,删除文件。


设备模块 安装后会在/proc中产生设备文件 

运行 insmod key.ko 就会产生一个设备名文件 

查看设备号的指令  cat /proc/devices  就可以找到所有设备名和设备号


转载于:https://my.oschina.net/u/274829/blog/283275

huaxi@ubuntu:~/c_projects/kernel_module$ make clean make -C /lib/modules/4.15.0-142-generic/build M=/home/huaxi/c_projects/kernel_module clean make[1]: Entering directory '/usr/src/linux-headers-4.15.0-142-generic' CLEAN /home/huaxi/c_projects/kernel_module/.tmp_versions make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-142-generic' huaxi@ubuntu:~/c_projects/kernel_module$ make make -C /lib/modules/4.15.0-142-generic/build M=/home/huaxi/c_projects/kernel_module modules make[1]: Entering directory '/usr/src/linux-headers-4.15.0-142-generic' CC [M] /home/huaxi/c_projects/kernel_module/ping_stat.o /home/huaxi/c_projects/kernel_module/ping_stat.c:138:21: error: variable ‘ping_fops’ has initializer but incomplete type static const struct proc_ops ping_fops = { ^ /home/huaxi/c_projects/kernel_module/ping_stat.c:139:5: error: unknown field ‘proc_open’ specified in initializer .proc_open = ping_proc_open, ^ /home/huaxi/c_projects/kernel_module/ping_stat.c:139:21: warning: excess elements in struct initializer .proc_open = ping_proc_open, ^ /home/huaxi/c_projects/kernel_module/ping_stat.c:139:21: note: (near initialization for ‘ping_fops’) /home/huaxi/c_projects/kernel_module/ping_stat.c:140:5: error: unknown field ‘proc_read’ specified in initializer .proc_read = seq_read, ^ /home/huaxi/c_projects/kernel_module/ping_stat.c:140:21: warning: excess elements in struct initializer .proc_read = seq_read, ^ /home/huaxi/c_projects/kernel_module/ping_stat.c:140:21: note: (near initialization for ‘ping_fops’) /home/huaxi/c_projects/kernel_module/ping_stat.c:141:5: error: unknown field ‘proc_lseek’ specified in initializer .proc_lseek = seq_lseek, ^ /home/huaxi/c_projects/kernel_module/ping_stat.c:141:21: warning: excess elements in struct initializer .proc_lseek = seq_lseek, ^ /home/huaxi/c_projects/kernel_module/ping_stat.c:141:21: note: (near initialization for ‘ping_fops’) /home/huaxi/c_projects/kernel_module/ping_stat.c:142:5: error: unknown field ‘proc_release’ specified in initializer .proc_release = single_release, ^ /home/huaxi/c_projects/kernel_module/ping_stat.c:142:21: warning: excess elements in struct initializer .proc_release = single_release, ^ /home/huaxi/c_projects/kernel_module/ping_stat.c:142:21: note: (near initialization for ‘ping_fops’) /home/huaxi/c_projects/kernel_module/ping_stat.c: In function ‘ping_stat_init’: /home/huaxi/c_projects/kernel_module/ping_stat.c:152:56: error: passing argument 4 of ‘proc_create’ from incompatible pointer type [-Werror=incompatible-pointer-types] proc_entry = proc_create("ping_stats", 0444, NULL, &ping_fops); ^ In file included from /home/huaxi/c_projects/kernel_module/ping_stat.c:3:0: ./include/linux/proc_fs.h:32:24: note: expected ‘const struct file_operations *’ but argument is of type ‘const struct proc_ops *’ struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops); ^ cc1: some warnings being treated as errors scripts/Makefile.build:337: recipe for target '/home/huaxi/c_projects/kernel_module/ping_stat.o' failed make[2]: *** [/home/huaxi/c_projects/kernel_module/ping_stat.o] Error 1 Makefile:1584: recipe for target '_module_/home/huaxi/c_projects/kernel_module' failed make[1]: *** [_module_/home/huaxi/c_projects/kernel_module] Error 2 make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-142-generic' Makefile:8: recipe for target 'all' failed make: *** [all] Error
最新发布
08-19
#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/slab.h> #include <linux/spinlock.h> #include <linux/timekeeping.h> #include <linux/version.h> #define MAX_ENTRIES 1000 // 最大统计条目数 #define PROC_NAME “ping_stat” // proc文件名 // 统计数据结构 struct ping_stat_entry { struct timespec64 timestamp; // 时间戳 __be32 src_ip; // 源IP地址 struct list_head list; // 链表指针 }; // 全局变量 static struct list_head stat_list; // 统计链表 static DEFINE_SPINLOCK(stat_lock); // 保护链表的自旋锁 static unsigned int param_n = 100; // 默认数据大小 static unsigned int packet_count = 0; // 总包计数 static struct proc_dir_entry *proc_entry; // proc文件入口 // 模块参数声明 module_param(param_n, uint, 0); MODULE_PARM_DESC(param_n, “ICMP data size to filter (100-1000)”); // Netfilter钩子函数 static unsigned int hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *ip_header; struct icmphdr *icmp_header; unsigned int data_size; struct timespec64 now; struct ping_stat_entry *new_entry; // 获取IP头 ip_header = ip_hdr(skb); if (!ip_header || ip_header->version != 4) return NF_ACCEPT; // 检查是否为ICMP协议 if (ip_header->protocol != IPPROTO_ICMP) return NF_ACCEPT; // 获取ICMP头 icmp_header = (struct icmphdr *)((char *)ip_header + (ip_header->ihl * 4)); if (!icmp_header) return NF_ACCEPT; // 检查是否为回显请求(ping) if (icmp_header->type != ICMP_ECHO) return NF_ACCEPT; // 计算数据部分大小 data_size = ntohs(ip_header->tot_len) - (ip_header->ihl * 4) - sizeof(struct icmphdr); // 检查数据大小是否符合要求 if (data_size != param_n) return NF_ACCEPT; // 获取当前时间 ktime_get_real_ts64(&now); // 创建新条目 new_entry = kmalloc(sizeof(*new_entry), GFP_ATOMIC); if (!new_entry) return NF_ACCEPT; // 填充数据 new_entry->timestamp = now; new_entry->src_ip = ip_header->saddr; // 加锁保护链表 spin_lock(&stat_lock); // 更新统计信息 packet_count++; // 维护链表大小 if (packet_count > MAX_ENTRIES) { struct ping_stat_entry *old_entry; old_entry = list_first_entry(&stat_list, struct ping_stat_entry, list); list_del(&old_entry->list); kfree(old_entry); } // 添加到链表尾部 list_add_tail(&new_entry->list, &stat_list); spin_unlock(&stat_lock); return NF_ACCEPT; } // Netfilter钩子配置 static struct nf_hook_ops nfho = { .hook = hook_func, .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_FIRST, }; // seq_file操作:开始迭代 static void *ping_seq_start(struct seq_file *s, loff_t *pos) { spin_lock(&stat_lock); return seq_list_start(&stat_list, *pos); } // seq_file操作:下一个元素 static void *ping_seq_next(struct seq_file *s, void *v, loff_t *pos) { return seq_list_next(v, &stat_list, pos); } // seq_file操作:结束迭代 static void ping_seq_stop(struct seq_file *s, void *v) { spin_unlock(&stat_lock); } // seq_file操作:显示元素 static int ping_seq_show(struct seq_file *s, void *v) { struct ping_stat_entry *entry = list_entry(v, struct ping_stat_entry, list); char ip_str[16]; // 格式化IP地址 snprintf(ip_str, sizeof(ip_str), "%pI4", &entry->src_ip); // 输出统计信息 seq_printf(s, "Time: %lld.%09ld | Source: %s\n", (long long)entry->timestamp.tv_sec, entry->timestamp.tv_nsec, ip_str); return 0; } // seq_file操作结构 static const struct seq_operations ping_seq_ops = { .start = ping_seq_start, .next = ping_seq_next, .stop = ping_seq_stop, .show = ping_seq_show, }; // 打开proc文件 static int ping_proc_open(struct inode *inode, struct file *file) { return seq_open(file, &ping_seq_ops); } // proc文件操作 static const struct proc_ops ping_proc_ops = { .proc_open = ping_proc_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = seq_release, }; // 模块初始化 static int __init ping_stat_init(void) { // 检查参数范围 if (param_n < 100 || param_n > 1000) { printk(KERN_ERR “Invalid param_n value (%d), must be 100-1000\n”, param_n); return -EINVAL; } // 初始化链表和锁 INIT_LIST_HEAD(&stat_list); // 注册Netfilter钩子 if (nf_register_net_hook(&init_net, &nfho)) { printk(KERN_ERR "Failed to register netfilter hook\n"); return -ENODEV; } // 创建proc文件 proc_entry = proc_create(PROC_NAME, 0, NULL, &ping_proc_ops); if (!proc_entry) { nf_unregister_net_hook(&init_net, &nfho); printk(KERN_ERR "Failed to create proc entry\n"); return -ENOMEM; } printk(KERN_INFO "Ping stat module loaded, filtering %d byte packets\n", param_n); return 0; } // 模块退出 static void __exit ping_stat_exit(void) { struct ping_stat_entry *entry, *tmp; // 移除proc文件 if (proc_entry) proc_remove(proc_entry); // 注销Netfilter钩子 nf_unregister_net_hook(&init_net, &nfho); // 清理链表内存 spin_lock(&stat_lock); list_for_each_entry_safe(entry, tmp, &stat_list, list) { list_del(&entry->list); kfree(entry); } spin_unlock(&stat_lock); printk(KERN_INFO "Ping stat module unloaded\n"); } module_init(ping_stat_init); module_exit(ping_stat_exit); MODULE_LICENSE(“GPL”); MODULE_AUTHOR(“Your Name”); MODULE_DESCRIPTION(“ICMP Ping Packet Statistics Module”);我希望在ping_seq_show函数中加入显示报文数目,包字节大小,时间用年月日时分秒的格式显示
08-05
### 创建 proc 文件条目 `create_proc_entry` 是用于创建 `/proc` 文件系统的文件或目录项的一个函数。此接口允许内核模块向进程文件系统添加自定义入口,从而提供动态数据给用户空间程序访问[^1]。 #### 函数原型 ```c struct proc_dir_entry *create_proc_entry(const char *name, umode_t mode, struct proc_dir_entry *parent); ``` 参数说明如下: - `const char *name`: 新创建的文件名字符串。 - `umode_t mode`: UNIX 权限模式位掩码(读写权限等)。 - `struct proc_dir_entry *parent`: 父级目录节点;如果为 NULL,则默认放置于根路径下。 返回值是一个指向新分配并初始化后的结构体实例指针 (`struct proc_dir_entry`) 或者当操作失败时则返回 NULL。 #### 使用示例 下面展示了一个简单的例子来演示如何利用该 API 实现一个只读属性的小工具,在其中可以查看当前 CPU 频率信息: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/proc_fs.h> static int read_cpu_freq(char *page, char **start, off_t offset, int count, int *eof, void *data); // 定义 /proc/cpu_frequency 结点 static struct proc_dir_entry *our_proc_file; int init_module(void){ our_proc_file = create_proc_entry("cpu_frequency", 0444, NULL); // 只读方式打开 if (NULL == our_proc_file) { remove_proc_entry("cpu_frequency", &proc_root); printk(KERN_ALERT "Error: Could not initialize /proc/cpu_frequency\n"); return -ENOMEM; } our_proc_file->read_proc = read_cpu_freq; our_proc_file->owner = THIS_MODULE; printk(KERN_INFO "/proc/cpu_frequency created.\n"); return 0; } void cleanup_module(void){ remove_proc_entry("cpu_frequency", NULL); printk(KERN_INFO "/proc/cpu_frequency removed.\n"); } ``` 上述代码片段展示了怎样通过调用 `create_proc_entry()` 方法注册一个新的名为 “cpu_frequency” 的虚拟文件到 `/proc` 下面,并设置好相应的回调处理逻辑以便响应来自应用程序层面对它的查询请求。 需要注意的是现代 Linux 内核版本已经逐渐弃用了这种直接操纵底层对象的方式转而推荐开发者采用更安全高效的替代方案比如 seq_file 接口或是 debugfs 子系统来进行相似功能开发工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值