Proc中使用seq_file,数据如何传递

本文深入探讨了Linux中proc文件系统的实现机制,重点讲解了如何使用proc_create_data和single_open函数在不同proc操作间传递全局变量。通过示例代码,详细分析了从创建proc节点到读写操作的全过程,揭示了数据传递的底层机制。

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

测试代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>

static unsigned int variable;
static struct proc_dir_entry *test_dir, *test_entry;

#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
static int test_proc_read(char *buf, char **start, off_t off, int count,
        int *eof, void *data)
{
    unsigned int *ptr_var = data;
    return sprintf(buf, "test_rw = %u\n", *ptr_var);
}

static int test_proc_write(struct file *file, const char *buffer,
        unsigned long count, void *data)
{
    unsigned int *ptr_var = data;

    *ptr_var = simple_strtoul(buffer, NULL, 10);

    return count;
}
#else
static int test_proc_show(struct seq_file *seq, void *v)
{
    unsigned int *ptr_var = seq->private;
    seq_printf(seq, "%u\n", *ptr_var);
    return 0;
}

static ssize_t test_proc_write(struct file *file, const char __user *buffer,
        size_t count, loff_t *ppos)
{
    struct seq_file *seq = file->private_data;
    unsigned int *ptr_var = seq->private;
    int err;
    char *kbuffer;

        if (!buffer || count > PAGE_SIZE - 1)
                    return -EINVAL;

    kbuffer = (char *)__get_free_page(GFP_KERNEL);
    if (!kbuffer)
        return -ENOMEM;

    err = -EFAULT;
    if (copy_from_user(kbuffer, buffer, count))
        goto out;
    kbuffer[count] = '\0';

    *ptr_var = simple_strtoul(kbuffer, NULL, 10);
    return count;

out:
    free_page((unsigned long)buffer);
    return err;
}

static int test_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, test_proc_show, PDE_DATA(inode));
}

static const struct file_operations test_proc_fops =
{
    .owner = THIS_MODULE,
    .open = test_proc_open,
    .read = seq_read,
    .write = test_proc_write,
    .llseek = seq_lseek,
    .release = single_release,
};
#endif

static __init int test_proc_init(void)
{
    test_dir = proc_mkdir("test_dir", NULL);
    if (test_dir) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
        test_entry = create_proc_entry("test_rw", 0666, test_dir);
        if (test_entry) {
            test_entry->nlink = 1;
            test_entry->data = &variable;
            test_entry->read_proc = test_proc_read;
            test_entry->write_proc = test_proc_write;
            return 0;
        }
#else
    test_entry = proc_create_data("test_rw",0666, test_dir, &test_proc_fops, &variable);
    if (test_entry)
        return 0;
#endif
    }

    return -ENOMEM;
}
module_init(test_proc_init);

static __exit void test_proc_cleanup(void)
{
    remove_proc_entry("test_rw", test_dir);
    remove_proc_entry("test_dir", NULL);
}
module_exit(test_proc_cleanup);

MODULE_AUTHOR("Barry Song <baohua@kernel.org>");
MODULE_DESCRIPTION("proc exmaple");
MODULE_LICENSE("GPL v2");

代码分析

variable是全局变量,如何在proc操作间传递?


创建节点 proc_create_data

会生成一个proc_dir_entry,->date 用于保存&variable

static unsigned int variable;

static __init int test_proc_init(void)
    test_entry = proc_create_data("test_rw",0666, test_dir, &test_proc_fops, &variable);
        pde = __proc_create(&parent, name, mode, 1);
        pde->proc_fops = proc_fops;
        pde->data      = data;
        proc_register(parent, pde)
        return pde;

结论1

test_entry->date == &variable
open阶段

PDE_DATA(inode)得到文件对应的proc_dir_entry,保存在:file->private_data->private。


file 与 seq_file
static const struct file_operations test_proc_fops =
{
    ...
    .open = test_proc_open,
    ...
};

static int test_proc_open(struct inode *inode, struct file *file)
    single_open(file, test_proc_show, PDE_DATA(inode));
        res = seq_open(file, op);
                p = kmem_cache_zalloc(seq_file_cache, GFP_KERNEL);
                file->private_data = p;
                p->op = op;
        ((struct seq_file *)file->private_data)->private = data;

所以
file->private_date 的值为一个 struct seq_file *p;
p->private = PDE_DATA(inode)
最终, file->private_date->private = PDE_DATA(inode);

PDE_DATA(inode)是什么
struct proc_inode {
    struct pid *pid;
    int fd;
    union proc_op op;
    struct proc_dir_entry *pde;
    struct ctl_table_header *sysctl;
    struct ctl_table *sysctl_entry;
    struct proc_ns ns;
    struct inode vfs_inode;
};

struct proc_inode {
    ...
    struct proc_dir_entry *pde;
    ...
}

/*
 * General functions
 */
static inline struct proc_inode *PROC_I(const struct inode *inode)
{
    return container_of(inode, struct proc_inode, vfs_inode);
}

static inline struct proc_dir_entry *PDE(const struct inode *inode)
{
    return PROC_I(inode)->pde;
}

static inline void *__PDE_DATA(const struct inode *inode)
{
    return PDE(inode)->data;
}

void *PDE_DATA(const struct inode *inode)
{
    return __PDE_DATA(inode);
}
EXPORT_SYMBOL(PDE_DATA);

所以 PDE_DATA(inode)即是 container_of(inode, struct proc_inode, vfs_inode)->pde->data
其中,container_of(inode, struct proc_inode, vfs_inode)->pde 值即为 &test_entry;
那么,container_of(inode, struct proc_inode, vfs_inode)->pde->data 值即为 &test_entry->data;
根据 结论1,可知
结论2

PDE_DATA(inode) = &variable
show/write 阶段,
  • 传参为struct seq_file *seq
static int test_proc_show(struct seq_file *seq, void *v)
{
    unsigned int *ptr_var = seq->private;
  • 传参为struct file *file
static ssize_t test_proc_write(struct file *file, const char __user *buffer,
        size_t count, loff_t *ppos)
{
    struct seq_file *seq = file->private_data;
    unsigned int *ptr_var = seq->private;

总结

proc_create_data(XXX,XXX,XXX,XXX, void data);
single_open(XXX, XXX, void
data);

如果需要传递参数, single_open的最后一个传参必须为proc_dir_entry->data, 表示方式有:PDE_DATA(inode)inode->datat等;
如果不需要传递数据, proc_create_datasingle_open其中一个void *dataNULL

转载于:https://www.cnblogs.com/gaoyang3513/p/10989700.html

#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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值