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> |
- 在proc目錄下建立子目錄函數
static inline struct proc_dir_entry *proc_mkdir(const char *name,struct proc_dir_entry *parent) |
示例: //注意隻能建立單層目錄
//在proc目錄下建立aaa檔案夾 proc_mkdir("aaa",NULL); |
- 在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); |
- 删除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);