Linux驱动基础 | proc文件系统

前言

在Linux系统中,有一些特殊文件系统,用于进行文件接口统一的操作来完成各种功能,proc文件系统就是其中一个,记录有关Linux系统的所有详细信息,包括其内核、进程和配置参数。所以本篇就带大家一起学习下,本篇内核版本5.10

思考

看本篇内容之前思考两个问题:

  1. 你了解proc文件系统常用节点有哪些

  2. 尝试自己写一个内核模块,在/proc中创建名为"test"的目录,在"test"目录下创建个节点data,写进去字符后可以读出写入的数据

proc文件系统简介

proc文件系统是什么

Linux系统上的/proc目录是一种文件系统,即proc文件系统。

/与其它常见的文件系统不同的是,/proc是虚拟文件系统,它不是实际的存储设备中的文件,而是存在于内存中。

在Linux中存在着一类特殊的伪文件系统,进行文件接口统一的操作来完成各种功能,例如ptyfs、devfs、sysfs和procfs。而procfs就是其中应用最广泛的一种伪文件系统。

proc 文件系统的挂载点是 /proc,它可以获取进程的有用信息、系统的有用信息等,可以查看具体某个进程号的相关信息,也可以查看系统的信息,比如CPU,内存信息,中断信息,设备映射状态信息,内核日志信息等

proc文件系统的作用和特点

proc 文件系统提供了一些内核中各个子系统的信息,所以procfs中的文件通常是只读的,只在一些特殊情况下允许写入,例如禁用nmi_watchdog

echo 1 > /proc/sys/kernel/nmi_watchdog

或者用户自己单独写驱动程序创建自定义读写的proc节点,简单地使用 cat 和 echo 命令写入查看信息。

列举procfs常用节点

我们看看/proc目录下面的节点

这里总结下我常用的proc文件系统一些节点如下。

  • /proc/cpuinfo:CPU信息(型号、家族、缓存大小等)
  • /proc/meminfo:物理内存交换空间的列表
  • /proc/mounts:已挂载的文件系统列表
  • /proc/filesystems:被支持的文件系统
  • /proc/modules:内核中已插入的内核模块列表(类似于lsmod命令的输出)。
  • /proc/version:内核版本
  • /proc/cmdline:系统启动时输入的内核命令行参数
  • /proc/pid: pid表示进程的PID,这些子目录中包含的文件可以提供有关进程的状态和环境的重要细节信息
  • /proc/interrupts: 已注册的中断请求号
  • /proc/softirqs:已注册的软中断请求号
  • /proc/kmsg: 内核日志信息
  • /proc/devices:可用的设备驱动。如字符设备和块设备
  • /proc/slabinfo: slab系统的统计信息
  • /proc/uptime: 系统正常运行时间
  • /proc/kallsyms: 查看所有符合表
  • /proc/partitions:系统中的块设备分区表信息
  • /proc/iomem: 系统中的物理RAM和总线设备地址

procfs接口使用

procfs常用的API

下面介绍下本实验会用到的一些API函数,这些API函数定义在fs/proc/internal.h和fs/proc/generic.c

创建目录函数

在parent目录创建一个名为name的目录

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

参数:

  • name 要创建的目录名称
  • parent 父目录,如果为NULL,表示直接在/proc下面创建目录。
struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
                   struct proc_dir_entry *parent, void *data)

参数:

  • name 要创建的目录名称
  • mode 指定要创建目录的权限
  • parent 父目录,如果为NULL,表示直接在/proc下面创建目录。
  • data 保存私有数据的指针,如不要为NULL。

注:这个接口和proc_mkdir_data的区别在于他不能保存私有数据指针。

创建文件函数

Linux v5.6之后,可以通过下面的API来创建一个procfs条目:

struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct proc_ops *proc_fops)

参数:

  • name 要创建的目录名称。
  • mode 指定要创建目录的权限,例如S_IRUGO | S_IWUSR表示文件对所有用户可读,对文件所有者可写。
  • parent 父目录,如果为NULL,表示直接在/proc下面创建目录。
  • proc_fops 指向file_operations结构体的指针,定义了对这个文件执行诸如open、read、write、ioctl等操作时的回调函数。

注:在Linux v3.10到 v5.6之间的版本,const struct proc_ops *proc_fops参数改成了const struct file_operations *proc_ops。

Linux v5.6之后,可以通过下面的API来创建一个procfs条目:

struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
               struct proc_dir_entry *parent,   const struct proc_fops *p                roc_fops, void *data)
  • name 要创建的目录名称
  • mode 指定要创建目录的权限
  • parent 父目录,如果为NULL,表示直接在/proc下面创建目录。
  • proc_fops 指向file_operations结构体的指针,定义了对这个文件执行诸如open、read、write、ioctl等操作时的回调函数。
  • data 保存私有数据的指针,如不要为NULL。

注:1、在Linux v3.10到 v5.6之间的版本,const struct proc_ops *proc_fops参数改成了const struct file_operations *proc_ops。
2.这个接口和proc_create_data的区别在于他不能保存私有数据指针。

创建符合链接函数

在parent目录创建指定dest目录的名为name的符合链接

struct proc_dir_entry *proc_symlink(const char *name, struct proc_dir_entry *parent, const char *dest)

参数:

  • name 原始符号。
  • parent 符号所在的目录。
  • dest 所要创建的符号链接名字。

删除目录/文件函数

删除parent目录中名为name的文件或者目录

void remove_proc_entry(const char *name, struct proc_dir_entry *parent)

参数:

  • name 要删除的文件或者目录名。
  • parent 符号所在的目录,如果为NULL,表示在/proc目录下。

也可以一次性移除整个proc目录及其下面的所有条目

void proc_remove(struct proc_dir_entry *parent);

参数:

  • parent 符号所在的目录,如果为NULL,表示在/proc目录下。

procfs操作函数

接下来就是要实现以下的procfs操作函数:

// Linux v3.10 ~ v5.5
static struct file_operations proc_fops = {
  .open = open_proc,
  .read = read_proc,
  .write = write_proc,
  .release = release_proc
};

// Linux v5.6及之后
static struct proc_ops proc_fops = {
  .proc_open = open_proc,
  .proc_read = read_proc,
  .proc_write = write_proc,
  .proc_release = release_proc
};

代码实验

需求:尝试自己写一个内核模块,在/proc中创建名为"test"的目录,在"test"目录下创建两个节点,分别是"read"和"write"节点。从"read"节点中可以读取内核模块的某个全局变量的值,通过往"write"节点中写数据可以修改某个全局变量的值

代码实现

file:proc_test.c
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/uaccess.h>

#define PROCFS_DIR_NAME  "test"
#define PROCFS_FILE_NAME "data"
#define BUFFER_SIZE 128

static char example_buffer[BUFFER_SIZE] = {0};
static struct proc_dir_entry *example_dir = NULL;

static ssize_t test_proc_write(struct file *file, const char __user *buf,
       		size_t count, loff_t *pos)
{
	if (count > BUFFER_SIZE)
        	return -EFAULT;

	memset(example_buffer, 0, BUFFER_SIZE);

    	if (copy_from_user(example_buffer, buf, count))
        	return -EFAULT;

	return count;
}

static ssize_t test_proc_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	return simple_read_from_buffer(buf, size, ppos, example_buffer, strlen(example_buffer));
}

static const struct proc_ops proc_test_fops = {
  	.proc_read      = test_proc_read,
	.proc_write     = test_proc_write,
};

static int __init proc_test_init(void)
{
   example_dir = proc_mkdir(PROCFS_DIR_NAME, NULL);
   if (!example_dir)
	return -1;
   
   proc_create_data(PROCFS_FILE_NAME, 0644, example_dir, &proc_test_fops, NULL);

   return 0;
}

static void __exit proc_test_exit(void)
{
    if (example_dir)
	  proc_remove(example_dir);
}


module_init(proc_test_init);
module_exit(proc_test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("pan");
MODULE_DESCRIPTION("A simple example of procfs usage");

Makefile

KERNELDIR ?= /home/forlinx/OK3588_Linux_fs/kernel

obj-m += proc_test.o
 
all: modules
 
modules:
      $(MAKE) ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- -C $(KERNELDIR) M=$(shell pwd) modules
 
modules_install:
        $(MAKE) -C $(KERNELDIR) M=$(shell pwd) modules_install
 
clean:
        $(MAKE) -C $(KERNELDIR) M=$(shell pwd) modules clean

测试验证:

# insmod proc_test.ko
# echo hello > /proc/test/data
# cat /proc/test/data
hello

总结

本篇我们学会了常用的procfs节点信息以及实践操作procfs节点例子,本篇学会的同学可以一键支持三连下,欢迎关注公众号[Linux随笔录],持续分享干货!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值