七兮技术分享

Linux中proc文件系统介绍

28 05月
作者:七兮|分类:运维知识|标签:proc linux

/proc 文件系统是一个虚拟文件系统,通过它可以使用一种新的方法在 Linux内核空间和用户间之间进行通信。在 /proc 文件系统中,我们可以将对虚拟文件的读写作为与内核中实体进行通信的一种手段,但是与普通文件不同的是,这些虚拟文件的内容都是动态创建的。本文对 /proc 虚拟文件系统进行了介绍,并展示了它的用法。

最初开发 /proc 文件系统是为了提供有关系统中进程的信息。但是由于这个文件系统非常有用,因此内核中的很多元素也开始使用它来报告信息,或启用动态运行时配置。清单 1 是对 /proc 中部分元素进行一次交互查询的结果。它显示的是 /proc 文件系统的根目录中的内容。注意,在左边是一系列数字编号的文件。每个实际上都是一个目录,表示系统中的一个进程。由于在 GNU/linux 中创建的第一个进程是 init 进程,因此它的 process-id 为 1。然后对这个目录执行一个 ls 命令,这会显示很多文件。每个文件都提供了有关这个特殊进程的详细信息。/proc 中另外一些有趣的文件有:cpuinfo,它标识了处理器的类型和速度;pci,显示在 PCI 总线上找到的设备;modules,标识了当前加载到内核中的模块。

另外,我们还可以使用 sysctl 来配置这些内核条目。/proc 文件系统并不是 GNU/Linux 系统中的惟一一个虚拟文件系统。在这种系统上,sysfs 是一个与 /proc 类似的文件系统,但是它的组织更好(从 /proc 中学习了很多教训)。不过 /proc 已经确立了自己的地位,因此即使 sysfs 与 /proc 相比有一些优点,/proc 也依然会存在。还有一个 debugfs 文件系统,不过(顾名思义)它提供的更多是调试接口。debugfs 的一个优点是它将一个值导出给用户空间非常简单。

1)linux内核是一个非常庞大、非常复杂的一个单独的程序,对于这样的一个程序来说调试是非常复杂的。
(2)项kernel这样庞大的项目,给里面添加/更改一个功能是非常麻烦的,因为你这添加的一个功能可能会影响其他已经有的。
(3)早期内核版本中尽管调试很麻烦,但是高手们还可以凭借个人超凡脱俗的能力去驾驭。但是到了2.4左右的版本的时候,这个难度已经非常大了。
(4)为了降低内核调试和学习的难度,内核开发者们在内核中添加了一些属性专门用于调试内核,proc文件系统就是一个尝试。
(5)proc文件系统的思路是:在内核中构建一个虚拟文件系统/proc,内核运行时将内核中一些关键的数据结构以文件的方式呈现在/proc目录中的一些特定文件中,这样相当于将不可见的内核中的数据结构以可视化的方式呈现给内核的开发者。
(6)proc文件系统给了开发者一种调试内核的方法:我们通过实时的观察/proc/xxx文件,来观看内核中特定数据结构的值。在我们添加一个新功能的前后来对比,就可以知道这个新功能产生的影响对还是不对。
(7)proc目录下的文件大小都是0,因为这些文件本身并不存在于硬盘中,他也不是一个真实文件,他只是一个接口,当我们去读取这个文件时,其实内核并不是去硬盘上找这个文件,而是映射为内核内部一个数据结构被读取并且格式化成字符串返回给我们。所以尽管我们看到的还是一个文件内容字符串,和普通文件一样的;但是实际上我们知道这个内容是实时的从内核中数据结构来的,而不是硬盘中来的。

Linux中proc文件系统介绍

这些文件和目录的解释,如下

cmdline:系统启动时输入给内核命令行参数
cpuinfo:CPU的硬件信息 (型号, 家族, 缓存大小等)
devices:主设备号及设备组的列表,当前加载的各种设备(块设备/字符设备)
dma:使用的DMA通道
filesystems:当前内核支持的文件系统,当没有给 mount(1) 指明哪个文件系统的时候, mount(1) 就依靠该文件遍历不同的文件系统
interrupts :中断的使用及触发次数,调试中断时很有用
ioports I/O:当前在用的已注册 I/O 端口范围
kcore:该伪文件以 core 文件格式给出了系统的物理内存映象(比较有用),可以用 GDB 查探当前内核的任意数据结构。该文件的总长度是物理内存 (RAM) 的大小再加上 4KB
kmsg:可以用该文件取代系统调用 syslog(2) 来记录内核日志信息,对应dmesg命令
kallsym:内核符号表,该文件保存了内核输出的符号定义, modules(X)使用该文件动态地连接和捆绑可装载的模块
loadavg:负载均衡,平均负载数给出了在过去的 1、 5,、15 分钟里在运行队列里的任务数、总作业数以及正在运行的作业总数。
locks:内核锁 。
meminfo物理内存、交换空间等的信息,系统内存占用情况,对应df命令。
misc:杂项 。
modules:已经加载的模块列表,对应lsmod命令 。
mounts:已加载的文件系统的列表,对应mount命令,无参数。
partitions:系统识别的分区表 。
slabinfo:sla池信息。
stat:全面统计状态表,CPU内存的利用率等都是从这里提取数据。对应ps命令。
swaps:对换空间的利用情况。
version:指明了当前正在运行的内核版本。


1、常用proc中的文件介绍

(1)/proc/cmdline

Linux中proc文件系统介绍

(2)/proc/cpuinfo

Linux中proc文件系统介绍

(3)/proc/devices

Linux中proc文件系统介绍

(4)/proc/interrupts

Linux中proc文件系统介绍


2、proc文件系统的使用

cat以手工查看
程序中可以文件IO访问
在shell程序中用cat命令结合正则表达式来获取并处理内核信息


如何在/proc目录下创建目录或文件:

要在/proc文件系统中创建一个虚拟文件,请使用proc_create()或proc_create_data()函数。这个函数可以接收一个文件名、一组权限和这个文件在/proc文件系统中出现的位置。这两个函数的返回值是一个proc_dir_entry类型指针(或者为NULL,说明在发生了错误时)。然后就可以使用这个返回的指针来配置这个虚拟文件的其他参数,例如在对该文件执行读操作时应该调用的函数。函数的原型和proc_dir_entry结构中的一部分如下图所示。


struct proc_dir_entry {

/*

* number of callers into module in progress;

* negative -> it's going away RSN

*/

atomic_t in_use;

atomic_t count; /* use count */

struct list_head pde_openers; /* who did ->open, but not ->release */

/* protects ->pde_openers and all struct pde_opener instances */

spinlock_t pde_unload_lock;

struct completion *pde_unload_completion;

const struct inode_operations *proc_iops; //Inode operations functions

const struct file_operations *proc_fops; //File operations functions

void *data; //私有数据指针,在操作函数中可以使用的到

unsigned int low_ino;

nlink_t nlink;

kuid_t uid; //文件的user id

kgid_t gid; //文件的group id

loff_t size;

struct proc_dir_entry *parent; //父目录

struct rb_root_cached subdir;

struct rb_node subdir_node;

umode_t mode; //访问权限

u8 namelen;

char name[]; //创建的虚拟文件或目录名

} __randomize_layout;

 

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

struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,struct proc_dir_entry *parent,const struct file_operations *proc_fops,void *data);

创建目录的函数原型如下:


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

struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,struct proc_dir_entry *parent, void *data);

前者不可以传入私有数据,后者可以。关于私有数据的操作,我们后面会使用一个例子来说明。


要从/proc中删除一个文件,可以使用remove_proc_entry()函数。要使用这个函数,我们需要提供文件名字符串,以及这个文件在/proc文件系统中的位置(parent)。函数原型如下:


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

当在/proc文件系统下创建文件之后用户是怎么访问这个文件的,其实看到proc_create()函数的最后一个参数,传入的是一个file_operations结构体指针,其实,就是通过里面的read和write回调函数来实现的。下面我们来看一个具体的例子。


在/proc目录下创建hello_proc文件,并往里面写入数据或获取里面的数据。


#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
 
/* 定义一个proc_dir_entry结构体类型变量*/
struct proc_dir_entry *hello_proc = NULL;
/* 定义一个全局数据,用来保存用户空间返回的数据 */
static char hello_data[20] = {};
 
/* 如果使用cat此节点,则传入的count为4K,直到读取的数据大小为4K,也就是直到此函数返回0
   当此函数返回0时,读取到的内容是不显示的。
 */
static ssize_t hello_proc_read(struct file *fp, char __user *user_buf, size_t count, loff_t *ppos)
{
int ret = 0;
/* 首先清空用户空间的user_buf地址的内容,有可能显示杂乱信息 */
    if (clear_user(user_buf, count)) {
        printk(KERN_ERR "clear error\n");
        return -EIO;
    }
/* 从hello_data数组中读取数据到用户空间user_buf,读取的长度应该是字符串的大小 */
ret = simple_read_fROM_buffer(user_buf, count, ppos, hello_data, strlen(hello_data));
    return ret;
}
 
/*
  用户空间使用echo往此节点写入数据,只有要写入的数据写完之后,也就是返回count是,此函数此不会被调用
*/
static ssize_t hello_proc_write(struct file *fp, const char __user *user_buf, size_t count, loff_t *ppos)
{
int ret;
    printk("hello_proc_write:count is %d.\n",count);
/* 写入数据之前,将数组清空 */
memset(hello_data,0,sizeof(hello_data));
/* 将用户空间写入的数据保存到数据中 */
ret = simple_write_to_buffer(hello_data, sizeof(hello_data),ppos,user_buf,count);
printk("hello_proc_write:ret is %d.\n",ret);
printk("hello_proc_write:user_buf is %s",hello_data);
    /* 返回用户空间写入字符串的大小 */
return count;
}
 
/* 定义一个file_operations结构体变量 */      
static const struct file_operations hello_proc_fops = {
    .owner      = THIS_MODULE,
.read= hello_proc_read,//使用cat时的回调函数
.write      = hello_proc_write,//使用echo时的回调函数
};
 
/* 驱动入口函数 */
static int __init proc_test_init(void)
{
/* 调用proc_create()函数创建"hello_proc"文件 */
    hello_proc = proc_create("hello_proc", 0,NULL,&hello_proc_fops);
    return 0;
}
 
/* 驱动出口函数 */
static void __exit proc_test_exit(void)
{
/* 删除此文件 */
if(hello_proc)
remove_proc_entry("hello_proc", NULL);
}
 
module_init(proc_test_init);
module_exit(proc_test_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("proc filesystem test by qxhut");


将此文件上传到服务器并编译模块,然后将此模块上传到开发板上面。既可以使用

本文来自 七兮网络 转载请注明;

本文地址:https://www.qxhut.cn/?id=171。

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络收集整理,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。如果您喜欢该程序和内容,请支持正版,购买注册,得到更好的正版服务。我们非常重视版权问题,如有侵权请邮件与我们联系处理。敬请谅解!

浏览12.5k 评论0
返回
目录
返回
首页
Linux中expect 如何获取完整的最新win10系统(已更新至2004版本)

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

登录
用户名
密码
注册
用户名
密码
确认密码
邮箱
QQ
验证码
找回密码
用户名
邮箱
※ 重置链接将发送到邮箱
请先 登录 再评论,若不是会员请先 注册