设备驱动基础学习--/proc下增加节点
在需要创建一个由一系列数据顺序组合而成的/proc虚拟文件或一个较大的/proc虚拟文件时,推荐使用seq_file接口。
数据结构struct seq_fille定义在include/linux/seq_file.h
struct seq_file {
char *buf; //seq_file接口使用的缓存页指针
size_t size; //seq_file接口使用的缓存页大小
size_t from; //从seq_file中向用户态缓冲区拷贝时相对于buf的偏移地址
size_t count; //buf中可以拷贝到用户态的字符数目
loff_t index; //start、next的处理的下标pos数值
loff_t read_pos; //当前已拷贝到用户态的数据量大小
u64 version;
struct mutex lock; //针对此seq_file操作的互斥锁,所有seq_*的访问都会上锁
const struct seq_operations *op; //操作实际底层数据的函数
void *private;
};
这里我们大致介绍下struct seq_operations中的各个函数的作用:
void * (*start) (struct seq_file *m, loff_t *pos);
start 方法会首先被调用,它的作用是在设置访问的起始点。
m:指向的是本seq_file的结构体,在正常情况下无需处理。
pos:是一个整型位置值,指示开始读取的位置。对于这个位置的意义完全取决于底层实现,不一定是字节为单位的位置,可能是一个元素的序列号。返回值如果非NULL,则是一个指向迭代器实现的私有数据结构体指针。如果访问出错则返回NULL。
设置好了访问起始点,seq_file内部机制可能会使用show方法获取start返回值指向的结构体中的数据到内部缓存,并适时送往用户空间。
int (*show) (struct seq_file *m, void *v);
所以show方法就是负责将v指向元素中的数据输出到seq_file的内部缓存,但是其中必须借助seq_file提供的一些类似printf的接口函数:
int seq_printf(struct seq_file *sfile, const char *fmt, ...);//专为 seq_file 实现的类似 printf 的函数;用于将数据常用的格式串和附加值参数.
//你必须将给 show 函数的 set_file 结构指针传递给它。如果seq_printf 返回-1,意味着缓存区已满,部分输出被丢弃。但是大部分时候都忽略了其返回值。
int seq_putc(struct seq_file *sfile, char c);
int seq_puts(struct seq_file *sfile, const char *s);//类似 putc 和 puts 函数的功能,sfile参数和返回值与 seq_printf相同。
int seq_escape(struct seq_file *m, const char *s, const char *esc);//这个函数类似 seq_puts ,但是它会将 s 中所有在 esc 中出现的字符以八进制格式输出到缓存。
//esc 的常用值是"\t\n\\", 它使内嵌的空格不会搞乱输出或迷惑 shell 脚本.
在show函数返回之后,seq_file机制可能需要移动到下一个数据元素,那就必须使用next方法。
void * (*next) (struct seq_file *m, void *v, loff_t *pos);
v:是之前调用start或next返回的元素指针,可能是上一个show已经完成输出所指向的元素。
pos:需要移动到的元素索引值。
在next实现中应当递增pos指向的值,但是具体递增的数量和迭代器的实现有关,不一定是1。而next的返回值如果非NULL,则是下一个需要输出到缓存的元素指针,否则表明已经输出结束,将会调用stop方法做清理。
void (*stop) (struct seq_file *m, void *v);
在stop实现中,参数m指向本seq_file的结构体,在正常情况下无需处理。而v是指向上一个next或start返回的元素指针。在需要做退出处理的时候才需要实现具体的功能。但是许多情况下可以直接返回。
在next和start的实现中可能需要对一个序列的函数进行遍历,而在内核中,对于一个序列数据结构体的实现一般是使用双向链表或者哈希链表,所有seq_file同时提供了一些对于内核双向链表和哈希链表的封装接口函数,方便程序员实现对于通过链表链接的结构体序列的操作。这些函数名一般是seq_list_*或者seq_hlist_*,这些函数的实现都在fs/seq_file.c中。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
static struct proc_dir_entry *proc_root;
static struct proc_dir_entry *proc_entry;
#define USER_ROOT_DIR "fellow_root"
#define USER_ENTRY "fellow_entry"
#define INFO_LEN 16
char *info;
static int proc_fellow_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n",info);
return 0;
}
static int proc_fellow_open(struct inode *inode, struct file *file)
{
return single_open(file, proc_fellow_show, NULL);//而调用single_open函数只需直接指定show的函数指针即可,个人猜测可能是在single_open函数中实现了seq_operations结构体 。如果使用seq_open则需要实现seq_operations。
}
static ssize_t proc_fellow_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
{
if ( count > INFO_LEN)
return -EFAULT;
if(copy_from_user(info, buffer, count))
{
return -EFAULT;
}
return count;
}
static const struct file_operations fellow_proc_fops = {
.owner = THIS_MODULE,
.open = proc_fellow_open,
.read = seq_read,
.write = proc_fellow_write,
.llseek = seq_lseek,
.release = single_release,
};
static int fellow_create_proc_entry(void)
{
int error = 0;
proc_root = proc_mkdir(USER_ROOT_DIR, NULL); //创建目录
if (NULL==proc_root)
{
printk(KERN_ALERT"Create dir /proc/%s error!\n", USER_ROOT_DIR);
return -ENOMEM;
}
printk(KERN_INFO"Create dir /proc/%s\n", USER_ROOT_DIR);
// proc_entry =create_proc_entry(USER_ENTRY, 0666, proc_root);
proc_entry = proc_create(USER_ENTRY, 0666, proc_root, &fellow_proc_fops); //在proc_root下创建proc_entry.
if (NULL ==proc_entry)
{
printk(KERN_ALERT"Create entry %s under /proc/%s error!\n", USER_ENTRY,USER_ROOT_DIR);
error = -ENOMEM;
goto err_out;
}
//proc_entry->write_proc= fellow_writeproc;
//proc_entry->read_proc = fellow_readproc;
printk(KERN_INFO"Create /proc/%s/%s\n", USER_ROOT_DIR,USER_ENTRY);
return 0;
err_out:
//proc_entry->read_proc = NULL;
//proc_entry->write_proc= NULL;
remove_proc_entry(USER_ENTRY, proc_root);
remove_proc_entry(USER_ROOT_DIR, NULL);
return error;
}
static int fellowproc_init(void)
{
int ret = 0;
printk("fellowproc_init\n");
info = kmalloc(INFO_LEN * sizeof(char), GFP_KERNEL);
if (!info)
{
ret = -ENOMEM;
goto fail;
}
memset(info, 0, INFO_LEN);
fellow_create_proc_entry();
return 0;
fail:
return ret;
}
static void fellowproc_exit(void)
{
kfree(info);
remove_proc_entry(USER_ENTRY, proc_root);
remove_proc_entry(USER_ROOT_DIR, NULL);
}
MODULE_AUTHOR("fellow");
MODULE_LICENSE("GPL");
module_init(fellowproc_init);
module_exit(fellowproc_exit);
运行结果如下:

设备驱动基础学习--/proc下增加节点的更多相关文章
- 设备驱动基础学习--poll
使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询是否可对设备进行无阻塞的访问,这两个系统调用最终又会引发设备驱动中的poll()函数被执行,所以我们的问题就集中到了如何编 ...
- 设备驱动基础学习--platform driver简单实现
platform是一条虚拟的总线.设备用platform_device表示,驱动用platform_driver进行注册,Linux platform driver机制和传统的device drive ...
- 设备驱动基础学习--misc device简单实现
在Linux驱动中把无法归类的五花八门的设备定义为混杂设备(用miscdevice结构体表述).miscdevice共享一个主设备号MISC_MAJOR(即10),但次设备号不同. 所有的miscde ...
- Hasen的linux设备驱动开发学习之旅--时钟
/** * Author:hasen * 參考 :<linux设备驱动开发具体解释> * 简单介绍:android小菜鸟的linux * 设备驱动开发学习之旅 * 主题:时钟 * Date ...
- Introduction the naive“scull” 《linux设备驱动》 学习笔记
Introduction the naive "scull" 首先.什么是scull? scull (Simple Character Utility for Loading Lo ...
- 【驱动】Flash设备驱动基础·NOR·NAND
Flash存储器 ——>Flash存储器是近几年来发展最快的存储设备,通常也称作闪存.Flash属于EEPROM(电可擦除可编程只读存储器),是一类存取速度很高的存储器. ——>它既有RO ...
- linux驱动基础系列--Linux下Spi接口Wifi驱动分析
前言 本文纯粹的纸上谈兵,我并未在实际开发过程中遇到需要编写或调试这类驱动的时候,本文仅仅是根据源码分析后的记录!基于内核版本:2.6.35.6 .主要是想对spi接口的wifi驱动框架有一个整体的把 ...
- 一脸懵逼学习Hdfs---动态增加节点和副本数量管理(Hdfs动态扩容)
1:按照上篇博客写的,将各个进程都启动起来: 集群规划: 主机名 IP 安装的软件 运行的进程 master ...
- 2019-9-16:渗透测试,基础学习,Linux下软件安装,环境搭建,笔记
Centos linux下软件安装yum 通过分析rpm包头数据后,自动解决依赖关系,直接云端下载软件,根据不同版本系统获取不同软件信息,按顺序下载rpm包,安装软件yum search 软件名:搜索 ...
随机推荐
- web 项目添加 x86 的dll 引用,模块 DLL c:\WINDOWS\system32\inetsrv\aspnetcore.dll 未能加载。
最近的项目要添加一个 x86 编译的dll, 首先添加引用,编译,报错: 首先判断是 项目中不能添加 x86 的引用,所以把所有的项目都按照 x86 的方式编译一遍,同时对应IIS 的应用池,也修改为 ...
- 安装多个jdk环境7、8、11等,并且切换默认使用版本
背景 在公司开发时,不同项目往往使用不同的jdk.目前使用最多的应该是JDK1.8,但是有些老项目使用1.7甚至1.6等 或者你想学习JDK最新版本,一些新特新如JDK11,现在最新都有JDK13 1 ...
- jsp中引用的jstl 和fmt标签-详解
JSTL 核心标签库标签共有13个,功能上分为4类: 1.表达式控制标签:out.set.remove.catch 2.流程控制标签:if.choose.when.otherwise 3.循环标签:f ...
- 【转载】SpringMVC前台给后台传值的方式
转自:http://blog.csdn.net/flymoringbird/article/details/53126505 1. 基本数据类型(以int为例,其他类似): Controller代码: ...
- AttributeError: 'SQLAlchemy' object has no attribute 'Foreignkey'
在学习<Flask Web开发----基于Python的Web应用开发实战>的过程中,调试程序,如下图,运行报错: AttributeError: 'SQLAlchemy' object ...
- javascript脚本混淆
javascript脚本混淆 脚本病毒是一个一直以来就存在,且长期活跃着的一种与PE病毒完全不同的一类病毒类型,其制作的门槛低.混淆加密方式的千变万化,容易传播.容易躲避检测,不为广大网民熟知等诸多 ...
- 对象Bean与Map互转问题
一.摘要 在实际开发过程中,经常碰到需要进行对象与map之间互转的问题,其实对于对象.Map 之间进行互转有很多种方式,下面我们一起来梳理一下: 利用 JSON 工具包,将对象转成字符串,之后再转成 ...
- 自己动手系列----使用数组实现一个简单的Map
数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同.Java 语言中提供的数组是用来存储固定大小的同类型元素. 这里提一下,数组的优缺点: 优点: 1. 使用索 ...
- FireFox浏览器的about:config参数大全及其具体用途介绍
FireFox浏览器的about:config参数大全及其具体用途介绍,注意:这还远不是所有的about:config参数,由于设置参数太多,官方也只提供英文版本的说明,这里提供的FireFox ab ...
- C++-指针阅读能力提升
下面的标识符你肯定在工程中看不到,但是在面试题中却非常常见,掌握掌握还是不错的. int (*p1)(int*, int (*f)(int*)); int (*p2[5])( ...