seq_file的用法

本文介绍了两种不同的方式来实现Linux内核模块中的文件操作:simple方式使用single_open简化流程但可能遇到缓冲区溢出的问题;复杂方式通过seq_open提供更灵活的迭代控制,避免内存分配错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*

初始化代码

*/

entry = create_proc_entry(cache_file_path, 0, NULL);

if (entry) {

entry->proc_fops = &flashcache_cache_operations;

entry->data = dmc;

}

kfree(s);


/*

销毁函数代码

*/

remove_proc_entry(cache_file_path,NULL);

kfree(s);



简单的方式single_open

static int

flashcache_version_show(struct seq_file*seq, void *v)

{

seq_printf(seq, "FlashcacheVersion : %s\n", flashcache_sw_version);

#ifdef COMMIT_REV

seq_printf(seq, "git commit:%s\n", COMMIT_REV);

#endif

return 0;

}


static int

flashcache_version_open(struct inode*inode, struct file *file)

{

return single_open(file,&flashcache_version_show, PDE(inode)->data);

}


static struct file_operationsflashcache_version_operations = {

.open = flashcache_version_open,

.read = seq_read,

.llseek = seq_lseek,

.release = single_release,

};

优缺点:

这种方法的好处就在于简单,但是经过测试在一个show函数里的seq_printf会先输出到seq_filebuff里面,当输出数据过大的时候很出现缓冲区溢出的错误,导致cannotallocate memory的错误。


复杂的方式seq_open

/*

如果当前迭代位置超过了列表的边界,则返回NULL

*/

static void*flashcache_cache_start(struct seq_file *seq, loff_t *pos)

{

struct cache_c *dmc = seq->private;


if(*pos >= (dmc->num_sets *dmc->assoc)) {

return NULL;

}

return &dmc->cache[*pos];

}

/*

如果当前迭代位置超过了列表的边界,则返回NULL

*/

static void*flashcache_cache_next(struct seq_file *seq, void *v, loff_t *pos)

{

struct cache_c *dmc = seq->private;

(*pos)++;

if(*pos >= (dmc->num_sets *dmc->assoc)) {

return NULL;

}

return &dmc->cache[*pos];

}

static voidflashcache_cache_stop(struct seq_file *seq, void *v)

{

}

/*

如果该函数输出的数据较少,则返回到next函数中继续迭代。当show函数产生一定数量的输出之后,迭代进入到start函数中。当nextstart函数中有一个返回NULL,则迭代停止

*/

static int flashcache_cache_show(structseq_file *seq, void *v)

{

struct cacheblock *cache = (structcacheblock *)v;


if(cache->cache_state & VALID){

seq_printf(seq, "%llu ",(u_int64_t)cache->dbn);

}

return 0;

}


static struct seq_operationsflashcache_cache_seq_ops = {

.start = flashcache_cache_start,

.next = flashcache_cache_next,

.stop = flashcache_cache_stop,

.show = flashcache_cache_show,

};


/*

在这里设置seq_fileprivateprivate作为私有成员在迭代中传递

*/

static int

flashcache_cache_open(struct inode*inode, struct file *file)

{

struct seq_file *seq;

int ret;

ret = seq_open(file,&flashcache_cache_seq_ops);

if(ret) {

return ret;

}

seq = (struct seq_file*)file->private_data;

seq->private = PDE(inode)->data;


return 0;

}


static struct file_operationsflashcache_cache_operations = {

.open = flashcache_cache_open,

.read = seq_read,

.llseek = seq_lseek,

.release = seq_release,

};


参考文献


#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
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.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/spinlock.h> #include <linux/ktime.h> #define PROCFS_NAME "icmp_stat" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("ICMP packet size statistics module"); static int n_param = 100; module_param(n_param, int, S_IRUGO); MODULE_PARM_DESC(n_param, "Size of ICMP data to match (100-1000)"); static struct nf_hook_ops nfho; static struct list_head icmp_list; static DEFINE_SPINLOCK(icmp_lock); static int packet_count = 0; struct icmp_packet_info { struct list_head list; ktime_t timestamp; __be32 src_ip; unsigned int id; }; static unsigned int icmp_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *ip_header; struct icmphdr *icmp_header; unsigned int data_len; if (!skb) { return NF_ACCEPT; } ip_header = ip_hdr(skb); if (ip_header->protocol != IPPROTO_ICMP) { return NF_ACCEPT; } if (skb->len < sizeof(struct iphdr) + sizeof(struct icmphdr)) { return NF_ACCEPT; } icmp_header = (struct icmphdr *)(skb->data + (ip_header->ihl * 4)); if (icmp_header->type != ICMP_ECHO && icmp_header->type != ICMP_ECHOREPLY) { return NF_ACCEPT; } /* 计算数据部分长度 */ data_len = ntohs(ip_header->tot_len) - (ip_header->ihl * 4) - sizeof(struct icmphdr); if (data_len == n_param) { struct icmp_packet_info *info = kmalloc(sizeof(*info), GFP_ATOMIC); if (!info) { return NF_ACCEPT; } info->timestamp = ktime_get_real(); info->src_ip = ip_header->saddr; info->id = ntohs(icmp_header->un.echo.id); spin_lock(&icmp_lock); list_add(&info->list, &icmp_list); packet_count++; spin_unlock(&icmp_lock); } return NF_ACCEPT; } static void *icmp_seq_start(struct seq_file *s, loff_t *pos) { spin_lock(&icmp_lock); return seq_list_start(&icmp_list, *pos); } static void *icmp_seq_next(struct seq_file *s, void *v, loff_t *pos) { return seq_list_next(v, &icmp_list, pos); } static void icmp_seq_stop(struct seq_file *s, void *v) { spin_unlock(&icmp_lock); } static int icmp_seq_show(struct seq_file *s, void *v) { struct icmp_packet_info *info = list_entry(v, struct icmp_packet_info, list); struct timespec64 ts = ktime_to_timespec64(info->timestamp); seq_printf(s, "Time: %lld.%09ld | Source: %pI4 | ID: %u\n", (s64)ts.tv_sec, ts.tv_nsec, &info->src_ip, info->id); return 0; } static const struct seq_operations icmp_seq_ops = { .start = icmp_seq_start, .next = icmp_seq_next, .stop = icmp_seq_stop, .show = icmp_seq_show}; static int icmp_proc_open(struct inode *inode, struct file *file) { return seq_open(file, &icmp_seq_ops); } static const struct proc_ops icmp_proc_ops = { .proc_open = icmp_proc_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = seq_release, }; static int __init icmp_stat_init(void) { if (n_param < 100 || n_param > 1000) { printk(KERN_ERR "Invalid n_param value: %d. Must be between 100 and 1000\n", n_param); return -EINVAL; } INIT_LIST_HEAD(&icmp_list); nfho.hook = icmp_hook; nfho.hooknum = NF_INET_PRE_ROUTING; nfho.pf = PF_INET; nfho.priority = NF_IP_PRI_FIRST; nf_register_net_hook(&init_net, &nfho); proc_create(PROCFS_NAME, 0, NULL, &icmp_proc_ops); printk(KERN_INFO "ICMP stat module loaded. Tracking data size: %d bytes\n", n_param); return 0; } static void __exit icmp_stat_exit(void) { struct icmp_packet_info *info, *tmp; nf_unregister_net_hook(&init_net, &nfho); remove_proc_entry(PROCFS_NAME, NULL); spin_lock(&icmp_lock); list_for_each_entry_safe(info, tmp, &icmp_list, list) { list_del(&info->list); kfree(info); } spin_unlock(&icmp_lock); printk(KERN_INFO "ICMP stat module unloaded. Total packets tracked: %d\n", packet_count); } module_init(icmp_stat_init); module_exit(icmp_stat_exit); 修改代码使得 /proc/icmp_stat 的数据中多记录一条数据,数据内容是报文的总数
最新发布
08-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值