学习seq_file(1)—— single_open方法解析
最编程
2024-08-13 21:04:41
...
作者
彭东林
pengdonglin137@163.com
平台
Linux-4.14.13
Qemu + vexpress
概述
从内核中导出信息到用户空间有很多方法,可以自己去实现file_operations的read函数或者mmap函数,但是这种方法不够简单,而且也会有一些限制,比如一次read读取大于1页时,驱动里就不得不去进行复杂的缓冲区管理。为此,就需要学习一下seq_file的用法,为了更简单和方便,内核提供了single_xxx系列接口,它是对seq_file的进一步封装。
正文
示例程序
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/seq_file.h> 4 #include <linux/debugfs.h> 5 #include <linux/fs.h> 6 7 static struct dentry *seq_file_demo_dir; 8 9 static int seq_file_demo_show(struct seq_file *seq, void *v) 10 { 11 seq_printf(seq, "Hello World\n"); 12 return 0; 13 } 14 15 static int seq_file_demo_open(struct inode *inode, struct file *file) 16 { 17 return single_open(file, &seq_file_demo_show, NULL); 18 } 19 20 static const struct file_operations seq_file_demo_fops = { 21 .owner = THIS_MODULE, 22 .open = seq_file_demo_open, 23 .read = seq_read, 24 .llseek = seq_lseek, 25 .release = single_release, 26 }; 27 28 static int __init seq_file_demo_init(void) 29 { 30 seq_file_demo_dir = debugfs_create_file("seq_file_demo", 0444, NULL, 31 NULL, &seq_file_demo_fops); 32 return 0; 33 } 34 35 static void __exit seq_file_demo_exit(void) 36 { 37 if (seq_file_demo_dir) 38 debugfs_remove(seq_file_demo_dir); 39 } 40 41 module_init(seq_file_demo_init); 42 module_exit(seq_file_demo_exit); 43 MODULE_LICENSE("GPL");
上面的demo在/sys/kernel/debug/下创建了一个"seq_file_demo",读取这个文件会得到"Hello World"字符串。上面的函数中需要我们实现的只有一个:seq_file_demo_show,直接调用seq_file提供的输出函数即可,不用我们去考虑缓冲区的分配、释放以及越界等问题,可以尽情畅快的输出。
上面的代码里有些标准化的函数,看着有些碍眼,所以在Linux-4.15的include/linux/seq_file.h中提供了下面的宏定义:
1 #define DEFINE_SHOW_ATTRIBUTE(__name) \ 2 static int __name ## _open(struct inode *inode, struct file *file) \ 3 { \ 4 return single_open(file, __name ## _show, inode->i_private); \ 5 } \ 6 \ 7 static const struct file_operations __name ## _fops = { \ 8 .owner = THIS_MODULE, \ 9 .open = __name ## _open, \ 10 .read = seq_read, \ 11 .llseek = seq_lseek, \ 12 .release = single_release, \ 13 }
利用上面的宏可以对我们的驱动做进一步简化:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/seq_file.h> 4 #include <linux/debugfs.h> 5 #include <linux/fs.h> 6 7 static struct dentry *seq_file_demo_dir; 8 9 static int seq_file_demo_show(struct seq_file *seq, void *v) 10 { 11 seq_printf(seq, "Hello World\n"); 12 return 0; 13 } 14 DEFINE_SHOW_ATTRIBUTE(seq_file_demo); 15 16 static int __init seq_file_demo_init(void) 17 { 18 seq_file_demo_dir = debugfs_create_file("seq_file_demo", 0444, NULL, 19 NULL, &seq_file_demo_fops); 20 return 0; 21 } 22 23 static void __exit seq_file_demo_exit(void) 24 { 25 if (seq_file_demo_dir) 26 debugfs_remove(seq_file_demo_dir); 27 } 28 29 module_init(seq_file_demo_init); 30 module_exit(seq_file_demo_exit); 31 MODULE_LICENSE("GPL");
这样我们只需要专心实现show函数即可。
如果看一下single_open,会发现它是对seq_file的进一步封装:
1 int single_open(struct file *file, int (*show)(struct seq_file *, void *), 2 void *data) 3 { 4 struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL); 5 int res = -ENOMEM; 6 7 if (op) { 8 op->start = single_start; 9 op->next = single_next; 10 op->stop = single_stop; 11 op->show = show; 12 res = seq_open(file, op); 13 if (!res) 14 ((struct seq_file *)file->private_data)->private = data; 15 else 16 kfree(op); 17 } 18 return res; 19 }
上面设置了single_xx的start、next以及stop回调函数,实现很简单:
1 static void *single_start(struct seq_file *p, loff_t *pos) 2 { 3 return NULL + (*pos == 0); 4 } 5 6 static void *single_next(struct seq_file *p, void *v, loff_t *pos) 7 { 8 ++*pos; 9 return NULL; 10 } 11 12 static void single_stop(struct seq_file *p, void *v) 13 { 14 }
其中,*ops表示的是要输出的元素的索引编号,从0开始,依次递增;
single_start的返回值表示要输出的元素的首地址,这个函数的作用是找到索引号为*pos的元素,并返回该元素的首地址,此外也可以做一些加锁的操作
single_next的入参中v表示刚刚show过的元素的首地址,*pos是该元素的索引,这个函数的目的是计算并返回下一个要show的元素的首地址以及索引号
single_stop里可以做一些释放锁的操作
show需要自己实现,向用户show出当前元素的相关信息
未完待续
推荐阅读
-
机器学习进阶-目标跟踪-KCF目标跟踪方法 1.cv2.multiTracker_create(构造选框集合) 2. cv2.TrackerKCF_create(获得KCF追踪器) 3. cv2.resize(变化图像大小) 4.cv2.selectROI(在图像上框出选框)
-
学习seq_file(1)—— single_open方法解析
-
深入解析Xilinx XDMA驱动并学习其使用方法
-
全面解析AUTOCAD文字线型格式,并学习使用C#实现方法
-
从零开始学Python3精华97课:第1节 - Python里的万物皆为对象深度解析与学习笔记
-
【摩尔线程+Colossal-AI强强联手】MusaBert登上CLUE榜单TOP10:技术细节揭秘 - 技术实力:摩尔线程凭借"软硬兼备"的技术底蕴,让MusaBert得以从底层优化到顶层。其内置多功能GPU配备AI加速和并行计算模块,提供了全面的AI与科学计算支持,为AI推理和低资源条件下的大模型训练等场景带来了高效、经济且环保的算力。 - 算法层面亮点:依托Colossal-AI AI大模型开发系统,MusaBert在训练过程中展现出了卓越的并行性能与易用性,特别在预处理阶段对DataLoader进行了优化,适应低资源环境高效处理海量数据。同时,通过精细的建模优化、领域内数据增强以及Adan优化器等手段,挖掘和展示了预训练语言模型出色的语义理解潜力。基于MusaBert,摩尔线程自主研发的MusaSim通过对比学习方法微调,结合百万对标注数据,MusaSim在多个任务如语义相似度、意图识别和情绪分析中均表现出色。 - 数据资源丰富:MusaBert除了自家高质量语义相似数据外,还融合了悟道开源200GB数据、CLUE社区80GB数据,以及浪潮公司提供的1TB高质量数据,保证模型即便在较小规模下仍具备良好性能。 当前,MusaBert已成功应用于摩尔线程的智能客服与数字人项目,并广泛服务于语义相似度、情绪识别、阅读理解与声韵识别等领域。为了降低大模型开发和应用难度,MusaBert及其相关高质量模型代码已在Colossal-AI仓库开源,可快速训练优质中文BERT模型。同时,通过摩尔线程与潞晨科技的深度合作,仅需一张多功能GPU单卡便能高效训练MusaBert或更大规模的GPT2模型,显著降低预训练成本,进一步推动双方在低资源大模型训练领域的共享目标。 MusaBert荣登CLUE榜单TOP10,象征着摩尔线程与潞晨科技联合研发团队在中文预训练研究领域的领先地位。展望未来,双方将携手探索更大规模的自然语言模型研究,充分运用上游数据资源,产出更为强大的模型并开源。持续强化在摩尔线程多功能GPU上的大模型训练能力,特别是在消费级显卡等低资源环境下,致力于降低使用大模型训练的门槛与成本,推动人工智能更加普惠。而潞晨科技作为重要合作伙伴,将继续发挥关键作用。
-
探讨智能:从深度学习到强化学习(七)——DDPG与基于模型的RL方法解析
-
互联网+比赛中的实战应用:基于深度学习人脸识别系统的性别与年龄识别方法解析(第三部分)
-
理解深度学习基础:从神经网络构造到实践 - 1.评分函数介绍 2.SVM损失函数解析 3.正规化惩罚项说明 4.Softmax与交叉熵损失函数详解 5.前向传播中的最优化挑战 6.批量大小(batch_size)实操指南...
-
深度学习解析(六):从前往后的计算流程——前向传播与反向传播方法