在Linux驱动中使用proc子系统

背景

proc文件系统是个简单有用的东东:驱动创建一个proc虚拟文件,应用层通过读写该文件,即可实现与内核的交互。

本文适用于3.10以后的内核,v3.10以前的内核调用create_proc_entry

参考:

创建proc目录

struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent);

描述:创建proc目录。

参数解析:

  • name就是要创建的文件夹名称。
  • parent是要创建节点的父节点。也就是要在哪个文件夹之下创建新文件夹,需要将那个文件夹的proc_dir_entry传入。

如果是在/proc目录下创建文件夹,则parent为NULL。

例如:

struct proc_dir_entry *mytest_dir = proc_mkdir("mytest", NULL);

创建proc文件

创建方法是调用以下函数:

static inline struct proc_dir_entry *proc_create(const char *name, mode_t mode,
struct proc_dir_entry *parent,
const struct file_operations *proc_fops);

描述:/proc中创建文件节点。

参数解析:

  • name就是要创建的文件名。
  • mode是文件的访问权限,以UGO的模式表示。
  • parent,参考proc_mkdir
  • proc_fops:该文件的操作函数。

例如:

#if 0
struct proc_dir_entry *mytest_dir = proc_mkdir("mytest", NULL);
struct proc_dir_entry *mytest_file = proc_create("mytest", 0x0644, mytest_dir, mytest_proc_fops);
#else
struct proc_dir_entry *mytest_file = proc_create("mytest/mytest", 0x0644, NULL, mytest_proc_fops);
#endif

文件操作符

接下来看看mytest_proc_fops的定义。以seq_single_为前缀的函数都是kernel中现成的。

static const struct file_operations mytest_proc_fops = {
.open = mytest_proc_open,
.read = seq_read,
.write = mytest_proc_write,
.llseek = seq_lseek,
.release = single_release,
};

open

mytest_proc_open函数中,只需要调用single_open函数,并传入一个show函数即可。

例如:

static int mytest_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, mytest_proc_show, inode->i_private);
}

内核参考文档(Documentation\filesystems\seq_file.txt)是这么说的:

关于single_开头的函数,有以下说明:

The extra-simple version

For extremely simple virtual files, there is an even easier interface.  A
module can define only the show() function, which should create all the
output that the virtual file will contain. The file's open() method then
calls: int single_open(struct file *file,
int (*show)(struct seq_file *m, void *p),
void *data); When output time comes, the show() function will be called once. The data
value given to single_open() can be found in the private field of the
seq_file structure. When using single_open(), the programmer should use
single_release() instead of seq_release() in the file_operations structure
to avoid a memory leak.
data参数

其中第三个参数,也就是single_open的data参数。关于这个参数,内核中有些地方传入的NULL,有些地方传入的inode->i_private,也有传入其他值的。

来看看data在single_open函数中如何被使用的:

if (!res)
((struct seq_file *)file->private_data)->private = data;

这与前面的英文说明一致,data是seq_file结构体的private成员。

那么data如何真正被使用的呢?

发现show函数的第一个参数为seq_file类型,在show函数中,可以将seq_file的private成员转换成对应的类型进行使用。

show参数

下面来看看mytest_proc_show函数如何实现。

传递给single_open的show函数指针,将在proc file输出时被调用。

例如,cat /proc/mytest/mytest时,mytest_proc_show函数将会被调用。

假设我们的mytest只是记录了一个标志,内核中会根据该标准执行不同的操作。

来看看mytest_proc_show的实现:

static int task_lifecycle_proc_show(struct seq_file *seq, void *v)
{
seq_puts(seq, mytest_flag ? "true\n" : "false\n"); return 0;
}

write

接下来再看看mytest_proc_write函数的实现。

顾名思义,mytest_proc_write函数会在写mytest文件时被调用。

功能就是记录写入数据到mytest文件。

static ssize_t task_lifecycle_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
char mode; if (count > 0) {
if (get_user(mode, buffer))
return -EFAULT; mytest_flag = (mode != '0');
} return count;
}

至此,proc文件创建完毕。

通过读写mytest文件,即可实现对mytest_flag的控制。

例子

驱动

/*************************
* 使用seq_file接口实现可读写的proc文件
* 功能同proc_test02.c
* Author: jx
* Date: 2014-08-08
*************************/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/slab.h> static char *str = NULL; /*********************
* seq_operations->show
* 必须返回0,否则什么也显示不出来
*********************/
static int
my_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "current kernel time is %ld\n", jiffies);
seq_printf(m, "str is %s\n", str); return 0;
} /***********************
* file_operations->open
***********************/
static int
my_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, my_proc_show, NULL);
} /************************
* file_operations->write
************************/
static ssize_t
my_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
{
char *tmp = kzalloc((count+1), GFP_KERNEL);
if (!tmp)
return -ENOMEM; if (copy_from_user(tmp,buffer,count)) {
kfree(tmp);
return -EFAULT;
} kfree(str);
str = tmp; return count;
} static struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = my_proc_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
.write = my_proc_write,
}; static int __init my_init(void)
{
struct proc_dir_entry* file; //创建proc文件并关联file_operations
file = proc_create("abc_rw", 0644, NULL, &my_fops);
if (!file)
return -ENOMEM; return 0;
} static void __exit my_exit(void)
{
remove_proc_entry("abc_rw", NULL);
kfree(str);
} module_init(my_init);
module_exit(my_exit); MODULE_AUTHOR("JX");
MODULE_LICENSE("GPL");

Makefile

用于本地机器调试验证使用。

obj-m :=proc.o
# For Ubuntu
KERNEL := /lib/modules/`uname -r`/build
#KERNEL := /lib/modules/`uname -r`/kernel all:
make -C $(KERNEL) M=`pwd` modules
install:
make -C $(KERNEL) M=`pwd` modules_install
depmod -A
clean:
make -C $(KERNEL) M=`pwd` clean

在Linux驱动中使用proc子系统的更多相关文章

  1. Linux内核中SPI/I2c子系统剖析

    Linux内核中,SPI和I2C两个子系统的软件架构是一致的,且Linux内核的驱动模型都以bus,driver,device三种抽象对象为基本元素构建起来.下文的分析将主要用这三种抽象对象的创建过程 ...

  2. Linux 驱动——Button8(输入子系统)

    输入子系统由驱动层.输入子系统核心.事件处理层三部分组成.一个输入事件,如鼠标移动.键盘按下等通过Driver->Inputcore->Event handler->userspac ...

  3. Linux驱动架构之pinctrl子系统分析(一)

    1.前言在嵌入式系统中,许多SoC的内部都包含了pin控制器,通过芯片内部的pin控制器,我们可以配置一个或者一组引脚的状态和功能特性,Linux内核为了统一各SoC厂商的引脚管理,提供了pinctr ...

  4. linux驱动中printk的使用注意事项

    今天在按键驱动中增加printk(KERN_INFO "gpio_keys_gpio_isr()\n");在驱动加载阶段可以输出调试信息,但驱动加载起来后的信息,在串口端看不到输出 ...

  5. Linux驱动中的EPROBE_DEFER是个啥

    ​Linux kernel 驱动中,有不少驱动会引用到 EPROBE_DEFER 这个错误号.比如下面这个例子,对 devm_gpiod_get 的返回值进行判断,如果有错误且错误号不是 -EPRBO ...

  6. Linux驱动中completion接口浅析(wait_for_complete例子,很好)【转】

    转自:http://blog.csdn.net/batoom/article/details/6298267 completion是一种轻量级的机制,它允许一个线程告诉另一个线程工作已经完成.可以利用 ...

  7. Linux驱动中completion接口浅析(wait_for_complete例子,很好)

    completion是一种轻量级的机制,它允许一个线程告诉另一个线程工作已经完成.可以利用下面的宏静态创建completion:                          DECLARE_CO ...

  8. 为什么linux驱动中变量或者函数都用static修饰?(知乎问题)

    static定义的全局变量 或函数也只能作用于当前的文件. 世界硬件厂商太多,定义static为了防止变量或 函数 重名,定义成static, 就算不同硬件驱动中的 变更 或函数重名了也没关系 .

  9. Linux驱动编程--基于I2C子系统的I2C驱动

    代码中,我添加了很多注释,应该不难理解,有错误大家可以指出来,我再改正 #include <linux/kernel.h> #include <linux/module.h> ...

  10. Linux驱动中常用的宏

    .module_i2c_driver(adxl34x_driver)展开为 static int __int adxl34x_driver_init(void) { return i2c_regist ...

随机推荐

  1. S/4 HANA 中的 Email Template

    电子邮件是非常常见的业务需求. SAP 了解这一点,并在 S/4 HANA(cloud和on premise)中引入了非常有趣的功能--Email Template.它将CDS视图和HTML模板结合了 ...

  2. GPS坐标、火星坐标、百度坐标之间的转换--提供javascript版本转换代码

    1.国内几种常用坐标系说明 WG-S84: GPS仪器记录的经纬度信息,Google Earth采用,Google Map中国范围外使用,高德地图中国范围外使用.GCJ-02: 火星坐标系,中国国家测 ...

  3. gRPC入门学习之旅(八)

    gRPC入门学习之旅(一) gRPC入门学习之旅(二) gRPC入门学习之旅(三) gRPC入门学习之旅(四) gRPC入门学习之旅(五) gRPC入门学习之旅(六) gRPC入门学习之旅(七) 3. ...

  4. 密码学—仿射密码Python程序

    文章目录 仿射密码 加密算法 解密算法 仿射密码 古典密码,且属于单表加密. 加密算法 仿射密码公式 c = m×k + b mod 26 c是密文,m是明文,m作为26字母中的明文,因此计算出来的密 ...

  5. Itext PDF 编辑 合并 图片转PDF以及表单域

    Itext PDF 编辑 合并 图片转PDF以及表单域 编辑PDF       ​x         import com.itextpdf.text.pdf.BaseFont; import com ...

  6. RocketMQ的单节点环境搭建

    1 介绍RocketMQ作为一款纯java.分布式.队列模型的开源消息中间件,支持事务消息.顺序消息.批量消息.定时消息.消息回溯等. 1.1 RocketMQ 特点支持发布/订阅(Pub/Sub)和 ...

  7. RocketMQ阅读源码前的准备

    本文将讲解如何在IDEA中导入 RocketMQ 源码,并运行 Broker 和 NameServer,编写一个消息发送与消息消费的示例. 一. 源码导入及调试 1.1 导入源码 RocketMQ 原 ...

  8. 开发人员必知的5种开源协议(GPL、LGPL、BSD、MIT、Apache License)

    软件开源是许多软件企业需要关注的问题,不同的开源软件协议,对应不同的源代码使用限制.只有了解这些开源软件协议,才能更好地使用和回馈开源软件,否则就有可能触犯法律.今天介绍四种常见的开源软件协议: GP ...

  9. .NET Core应用程序每次启动后使用string.GetHashCode()方法获取到的哈希值(hash)不相同

    前言 如标题所述,在ASP.NET Core应用程序中,使用string.GetHashCode()方法去获取字符串的哈希值,但每次重启这个ASP.NET Core应用程序之后,同样的字符串的哈希值( ...

  10. 图像处理技术OpencvSharp入门

    目录 第一部分 初识Opencv 1.C# 下Opencv库 2.安装OpenCvSharp 第二部分 OpencvSharp入门 1.加载图像文件 2.显示图像 第三部分 基础应用 1.颜色转换 2 ...