天天看点

Linux下proc文件接口详解

Linux下proc文件接口​

proc目录介绍​

Linux系统上的/proc目录是一种文件系统,即proc文件系统。与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。​

基于/proc文件系统如上所述的特殊性,其内的文件也常被称作虚拟文件,并具有一些独特的特点。例如,其中有些文件虽然使用查看命令查看时会返回大量信息,但文件本身的大小却会显示为0字节。此外,这些特殊文件中大多数文件的时间及日期属性通常为当前系统时间和日期,这跟它们随时会被刷新(存储于RAM中)有关。 ​

为了查看及使用上的方便,这些文件通常会按照相关性进行分类存储于不同的目录甚至子目录中,如/proc/scsi目录中存储的就是当前系统上所有SCSI设备的相关信息,/proc/N中存储的则是系统当前正在运行的进程的相关信息,其中N为正在运行的进程。 ​

大多数虚拟文件可以使用文件查看命令如cat、more或者less进行查看,有些文件信息表述的内容可以一目了然,但也有文件的信息却不怎么具有可读性。不过,这些可读性较差的文件在使用一些命令如apm、free、lspci或top查看时却可以有着不错的表现。​

二、proc常见的一些文件功能介绍​

2.1、/proc/apm ​

高级电源管理(APM)版本信息及电池相关状态信息,通常由apm命令使用; ​

2.2、/proc/buddyinfo ​

用于诊断内存碎片问题的相关信息文件;​

2.3、/proc/cmdline ​

在启动时传递至内核的相关参数信息,这些信息通常由lilo或grub等启动管理工具进行传递; ​

2.4、/proc/cpuinfo ​

处理器的相关信息的文件;​

2.5、/proc/crypto ​

系统上已安装的内核使用的密码算法及每个算法的详细信息列表;​

2.6、/proc/devices ​

系统已经加载的所有块设备和字符设备的信息,包含主设备号和设备组(与主设备号对应的设备类型)名;​

2.7、/proc/diskstats ​

每块磁盘设备的磁盘I/O统计信息列表;(内核2.5.69以后的版本支持此功能)​

2.8、/proc/dma ​

每个正在使用且注册的ISA DMA通道的信息列表; ​

2.9、/proc/execdomains ​

内核当前支持的执行域(每种操作系统独特“个性”)信息列表; ​

2.10、/proc/fb ​

帧缓冲设备列表文件,包含帧缓冲设备的设备号和相关驱动信息;​

2.11、/proc/filesystems ​

当前被内核支持的文件系统类型列表文件,被标示为nodev的文件系统表示不需要块设备的支持;通常mount一个设备时,如果没有指定文件系统类型将通过此文件来决定其所需文件系统的类型; ​

2.12、/proc/interrupts ​

X86或X86_64体系架构系统上每个IRQ相关的中断号列表;多路处理器平台上每个CPU对于每个I/O设备均有自己的中断号; ​

2.13、/proc/iomem ​

每个物理设备上的记忆体(RAM或者ROM)在系统内存中的映射信息; ​

2.14、/proc/ioports ​

当前正在使用且已经注册过的与物理设备进行通讯的输入-输出端口范围信息列表;如下面所示,第一列表示注册的I/O端口范围,其后表示相关的设备; ​

2.15、/proc/kallsyms ​

模块管理工具用来动态链接或绑定可装载模块的符号定义,由内核输出;(内核2.5.71以后的版本支持此功能);通常这个文件中的信息量相当大;​

2.16、/proc/kcore ​

系统使用的物理内存,以ELF核心文件(core file)格式存储,其文件大小为已使用的物理内存(RAM)加上4KB;这个文件用来检查内核数据结构的当前状态,因此,通常由GBD通常调试工具使用,但不能使用文件查看命令打开此文件; ​

2.17、/proc/kmsg ​

此文件用来保存由内核输出的信息,通常由/sbin/klogd或/bin/dmsg等程序使用,不要试图使用查看命令打开此文件; ​

2.18、/proc/loadavg ​

保存关于CPU和磁盘I/O的负载平均值,其前三列分别表示每1秒钟、每5秒钟及每15秒的负载平均值,类似于uptime命令输出的相关信息;第四列是由斜线隔开的两个数值,前者表示当前正由内核调度的实体(进程和线程)的数目,后者表示系统当前存活的内核调度实体的数目;第五列表示此文件被查看前最近一个由内核创建的进程的PID; ​

2.19、/proc/locks ​

保存当前由内核锁定的文件的相关信息,包含内核内部的调试数据;每个锁定占据一行,且具有一个惟一的编号;如下输出信息中每行的第二列表示当前锁定使用的锁定类别,POSIX表示目前较新类型的文件锁,由lockf系统调用产生,FLOCK是传统的UNIX文件锁,由flock系统调用产生;第三列也通常由两种类型,ADVISORY表示不允许其他用户锁定此文件,但允许读取,MANDATORY表示此文件锁定期间不允许其他用户任何形式的访问; ​

2.20、/proc/mdstat ​

保存RAID相关的多块磁盘的当前状态信息,在没有使用RAID机器上,其显示为如下状态: ​

2.21、/proc/meminfo ​

系统中关于当前内存的利用状况等的信息,常由free命令使用;可以使用文件查看命令直接读取此文件,其内容显示为两列,前者为统计属性,后者为对应的值; ​

2.22、/proc/mounts ​

在内核2.4.29版本以前,此文件的内容为系统当前挂载的所有文件系统,在2.4.19以后的内核中引进了每个进程使用独立挂载名称空间的方式,此文件则随之变成了指向/proc/self/mounts(每个进程自身挂载名称空间中的所有挂载点列表)文件的符号链接;/proc/self是一个独特的目录,后文中会对此目录进行介绍; ​

2.23、/proc/modules ​

当前装入内核的所有模块名称列表,可以由lsmod命令使用,也可以直接查看;如下所示,其中第一列表示模块名,第二列表示此模块占用内存空间大小,第三列表示此模块有多少实例被装入,第四列表示此模块依赖于其它哪些模块,第五列表示此模块的装载状态(Live:已经装入;Loading:正在装入;Unloading:正在卸载),第六列表示此模块在内核内存(kernel memory)中的偏移量; ​

2.24、/proc/partitions ​

块设备每个分区的主设备号(major)和次设备号(minor)等信息,同时包括每个分区所包含的块(block)数目(如下面输出中第三列所示); ​

2.25、/proc/pci ​

内核初始化时发现的所有PCI设备及其配置信息列表,其配置信息多为某PCI设备相关IRQ信息,可读性不高,可以用“/sbin/lspci –vb”命令获得较易理解的相关信息;在2.6内核以后,此文件已为/proc/bus/pci目录及其下的文件代替; ​

2.26、/proc/slabinfo ​

在内核中频繁使用的对象(如inode、dentry等)都有自己的cache,即slab pool,而/proc/slabinfo文件列出了这些对象相关slap的信息;详情可以参见内核文档中slapinfo的手册页; ​

2.27、/proc/stat ​

实时追踪自系统上次启动以来的多种统计信息;如下所示,其中,​

“cpu”行后的八个值分别表示以1/100(jiffies)秒为单位的统计值(包括系统运行于用户模式、低优先级用户模式,运系统模式、空闲模式、I/O等待模式的时间等); ​

“intr”行给出中断的信息,第一个为自系统启动以来,发生的所有的中断的次数;然后每个数对应一个特定的中断自系统启动以来所发生的次数; ​

“ctxt”给出了自系统启动以来CPU发生的上下文交换的次数。 ​

“btime”给出了从系统启动到现在为止的时间,单位为秒; ​

“processes (total_forks)自系统启动以来所创建的任务的个数目; ​

“procs_running”:当前运行队列的任务的数目; ​

“procs_blocked”:当前被阻塞的任务的数目; ​

2.28、/proc/swaps ​

当前系统上的交换分区及其空间利用信息,如果有多个交换分区的话,则会每个交换分区的信息分别存储于/proc/swap目录中的单独文件中,而其优先级数字越低,被使用到的可能性越大;下面是作者系统中只有一个交换分区时的输出信息; ​

2.29、/proc/uptime ​

系统上次启动以来的运行时间,如下所示,其第一个数字表示系统运行时间,第二个数字表示系统空闲时间,单位是秒;​

2.30、/proc/version ​

当前系统运行的内核版本号,在作者的RHEL5.3上还会显示系统安装的gcc版本,如下所示; ​

2.31、/proc/vmstat ​

当前系统虚拟内存的多种统计数据,信息量可能会比较大,这因系统而有所不同,可读性较好;下面为作者机器上输出信息的一个片段;(2.6以后的内核支持此文件)​

2.32、/proc/zoneinfo ​

内存区域(zone)的详细信息列表,信息量较大,下面列出的是一个输出片段: ​

三、相关接口函数​

Proc文件接口,主要用于驱动代码调试,可以直接使用cat命令访问proc目录下的对应文件接口即可。​

需要使用的头文件:​

#include <linux/proc_fs.h>​

#include <linux/fs.h>​

  1. 在proc目录下创建子目录函数
static inline struct proc_dir_entry *proc_mkdir(const char *name,struct proc_dir_entry *parent)​

示例: //注意只能创建单层目录​

//在proc目录下创建aaa文件夹​

proc_mkdir("aaa",NULL);​

  1. 在proc目录下创建文件

static inline struct proc_dir_entry *proc_create(const char *name, //文件名称​

umode_t mode, //模式,默认为0​

struct proc_dir_entry *parent, //父目录结构​

const struct file_operations *proc_fops) //文件集合​

示例:​

//在proc目录下创建一个文件​

proc_create("aaa/tiny4412_proc_test", 0, NULL, &fops_proc);​

  1. 删除proc目录下之前创建的文件或者目录​

void remove_proc_entry(const char *name, //文件的路径​

struct proc_dir_entry *parent //父目录结构​

)​

示例:​

remove_proc_entry("aaa/tiny4412_proc_test", NULL);​

注意: 如果是删除目录,需要先把目录下的文件删除掉,每次删除必须保证目录是空的。​

四、示例代码

#include <linux/kernel.h>​

#include <linux/module.h>​

#include <linux/miscdevice.h>​

#include <linux/fs.h>​

#include <asm/uaccess.h>​

#include <asm/io.h>​

#include <linux/delay.h>​

#include <linux/gpio.h>​

#include <mach/gpio.h>​

#include <plat/gpio-cfg.h>​

#include <linux/proc_fs.h>​

static int tiny4412_open(struct inode *inode, struct file *file)​

{ ​

printk("tiny4412_open ok\n");​

return 0;​

}​

static ssize_t tiny4412_read(struct file *file, char __user *buf, size_t cnt, loff_t *loff)​

{​

copy_to_user(buf,"123456",6);​

printk("tiny4412_read调用成功.\n");​

return 0;​

}​

static int tiny4412_release(struct inode *inode, struct file *file)​

{​

return 0;​

}​

static struct file_operations tiny4412_fops=​

{​

.open=tiny4412_open,​

.read=tiny4412_read,​

.release=tiny4412_release,​

};​

static int __init tiny4412_init(void)​

{​

proc_mkdir("wbyq",0);​

/*创建内核接口: proc 存放内核信息*/​

proc_create("wbyq/tiny4412_proc",0, NULL, &tiny4412_fops);​

printk("驱动安装成功.\n");​

return 0;​

}​

static void __exit tiny4412_exit(void)​

{​

remove_proc_entry("wbyq/tiny4412_proc", NULL);​

remove_proc_entry("wbyq", NULL);​

printk("驱动卸载成功.\n");​

}​

/*驱动的入口:insmod xxx.ko*/​

module_init(tiny4412_init);​

/*驱动的出口: rmmod xxx.ko*/​

module_exit(tiny4412_exit);​

/*模块的许可证*/​

MODULE_LICENSE("GPL");​

/*模块的作者*/​

MODULE_AUTHOR("wbyq");​

五、完整示例代码

#include <linux/kernel.h> ​

#include <linux/module.h>​

#include <linux/miscdevice.h>​

#include <linux/fs.h>​

#include <linux/io.h>​

#include <asm/uaccess.h>​

#include <linux/gpio.h>​

#include <mach/gpio.h>​

#include <plat/gpio-cfg.h>​

#include <linux/device.h>​

#include <linux/mutex.h>​

#include <linux/list.h>​

#include <linux/proc_fs.h>​

static struct class *tiny4412_beep_class;​

static unsigned int major=0; //主设备号​

static LIST_HEAD(tiny4412_beep_list); //链表头​

static DEFINE_MUTEX(tiny4412_beep_mtx); //互斥锁​

#define DYNAMIC_MINORS 64 /* like dynamic majors */​

static DECLARE_BITMAP(beep_minors, DYNAMIC_MINORS);​

struct tiny4412_beep_device​

{​

int minor; /*次设备号*/​

const char *name; /*设备节点的名称*/​

const struct file_operations *fops; /*文件操作集合*/​

struct list_head list; //链表​

};​

int tiny4412_beep_register(struct tiny4412_beep_device *beep_dev)​

{​

struct tiny4412_beep_device *c;​

dev_t dev;​

INIT_LIST_HEAD(&beep_dev->list);​

mutex_lock(&tiny4412_beep_mtx);​

//查找传入的次设备号是否冲突​

list_for_each_entry(c, &tiny4412_beep_list, list)​

{​

if(c->minor == beep_dev->minor)​

{​

mutex_unlock(&tiny4412_beep_mtx);​

return -EBUSY;​

}​

}​

//自动分配​

if(beep_dev->minor == MISC_DYNAMIC_MINOR) ​

{​

int i = find_first_zero_bit(beep_minors,DYNAMIC_MINORS);​

if (i >= DYNAMIC_MINORS)​

{​

mutex_unlock(&tiny4412_beep_mtx);​

return -EBUSY;​

}​

beep_dev->minor = DYNAMIC_MINORS - i - 1;​

set_bit(i,beep_minors);​

}​

//合成设备号​

dev = MKDEV(major, beep_dev->minor);​

//创建设备节点​

device_create(tiny4412_beep_class,NULL,dev,NULL,"%s", beep_dev->name);​

list_add(&beep_dev->list,&tiny4412_beep_list);​

//解锁​

mutex_unlock(&tiny4412_beep_mtx);​

return 0;​

}​

int tiny4412_beep_deregister(struct tiny4412_beep_device *beep_dev)​

{​

int i = DYNAMIC_MINORS - beep_dev->minor - 1;​

mutex_lock(&tiny4412_beep_mtx);​

list_del(&beep_dev->list);​

//将dev目录下的文件删除掉​

device_destroy(tiny4412_beep_class, MKDEV(major, beep_dev->minor));​

if (i < DYNAMIC_MINORS && i >= 0)​

clear_bit(i, beep_minors);​

mutex_unlock(&tiny4412_beep_mtx);​

return 0;​

}​

EXPORT_SYMBOL_GPL(tiny4412_beep_register); ​

EXPORT_SYMBOL_GPL(tiny4412_beep_deregister); ​

//底层open函数​

static int tiny4412_beep_open(struct inode * inode, struct file * file)​

{​

//得到次设备号​

int minor = iminor(inode);​

struct tiny4412_beep_device *c;​

struct file_operations *new_fops,*old_fops;​

mutex_lock(&tiny4412_beep_mtx);​

//遍历链表--找到链表里相同的次设备号​

list_for_each_entry(c,&tiny4412_beep_list, list) ​

{​

if (c->minor == minor)​

{​

new_fops = fops_get(c->fops); //得到47次设备号对应的结构体地址 ​

break;​

}​

}​

file->f_op = new_fops; //改变指向--文件操作集合的指向​

if(file->f_op->open)​

{​

file->f_op->open(inode,file);​

}​

fops_put(old_fops);​

mutex_unlock(&tiny4412_beep_mtx);​

return 0;​

}​

static const struct file_operations tiny4412_beep_fops =​

{​

.owner = THIS_MODULE,​

.open = tiny4412_beep_open,​

};​

static ssize_t tiny4412_read(struct file *file, char __user *buf, size_t cnt, loff_t *loff)​

{​

struct tiny4412_beep_device *c;​

//遍历链表--找到链表里相同的次设备号​

list_for_each_entry(c,&tiny4412_beep_list, list) ​

{​

printk("%d %s\n",c->minor,c->name);​

}​

return 0;​

}​

static struct file_operations tiny4412_fops=​

{​

.read=tiny4412_read,​

};​

static int __init tiny4412_beep_class_init(void)​

{​

/*1. 创建设备类*/​

tiny4412_beep_class=class_create(THIS_MODULE,"tiny4412_beep");​

/*2. 注册字符设备*/​

major=register_chrdev(0,"tiny4412_beep",&tiny4412_beep_fops);​

proc_mkdir("wbyq",0);​

/*创建内核接口: proc 存放内核信息*/​

proc_create("wbyq/tiny4412_proc",0, NULL, &tiny4412_fops);​

return 0;​

}​

static void __exit tiny4412_beep_class_cleanup(void)​

{​

remove_proc_entry("wbyq/tiny4412_proc", NULL);​

remove_proc_entry("wbyq", NULL);​

//注销设备类​

class_destroy(tiny4412_beep_class);​

//注销字符设备​

unregister_chrdev(major,"tiny4412_beep");​

}​

module_init(tiny4412_beep_class_init);​

module_exit(tiny4412_beep_class_cleanup);​

继续阅读