proc檔案系統和sysfs檔案系統類似,是虛拟檔案系統,存在于記憶體中,用于核心和使用者程式互動等,檢視核心資訊。
基于/proc檔案系統如上所述的特殊性,其内的檔案也常被稱作虛拟檔案,并具有一些獨特的特點。例如,其中有些檔案雖然使用檢視指令檢視時會傳回大量資訊,但檔案本身的大小卻會顯示為0位元組。此外,這些特殊檔案中大多數檔案的時間及日期屬性通常為目前系統時間和日期,這跟它們随時會被重新整理(存儲于RAM中)有關。
為了檢視及使用上的友善,這些檔案通常會按照相關性進行分類存儲于不同的目錄甚至子目錄中,如/proc/scsi目錄中存儲的就是目前系統上所有SCSI裝置的相關資訊,/proc/N中存儲的則是系統目前正在運作的程序的相關資訊,其中N為正在運作的程序(可以想象得到,在某程序結束後其相關目錄則會消失)。
大多數虛拟檔案可以使用檔案檢視指令如cat、more或者less進行檢視,有些檔案資訊表述的内容可以一目了然,但也有檔案的資訊卻不怎麼具有可讀性。其中使用者程式ps、top、free等的内容就來自proc檔案系統中的資訊。
一、核心資訊
- /proc/cmdline:在啟動時傳遞至核心的相關參數資訊,這些資訊通常由lilo或grub等啟動管理工具進行傳遞
proc檔案系統相關内容 - /proc/cpuinfo:處理器的相關資訊
proc檔案系統相關内容 - /proc/crypto:系統上已安裝的核心使用的密碼算法及每個算法的詳細資訊清單
proc檔案系統相關内容 - /proc/devices:系統已經加載的所有塊裝置和字元裝置的資訊
proc檔案系統相關内容 - /proc/diskstats:每塊磁盤裝置的磁盤I/O統計資訊清單
proc檔案系統相關内容 - /proc/dma:每個正在使用且注冊的ISA DMA通道的資訊清單
proc檔案系統相關内容 - /proc/filesystems:目前被核心支援的檔案系統類型清單檔案,被标示為nodev的檔案系統表示不需要塊裝置的支援;通常mount一個裝置時,如果沒有指定檔案系統類型将通過此檔案來決定其所需檔案系統的類型
proc檔案系統相關内容 - /proc/iomem:每個實體裝置上的記憶體(RAM或者ROM)在系統記憶體中的映射資訊
proc檔案系統相關内容 - /proc/meminfo:系統中關于目前記憶體的利用狀況等的資訊,常由free指令使用
proc檔案系統相關内容 - /proc/mounts:此檔案的内容為系統目前挂載的所有檔案系統
proc檔案系統相關内容 - /proc/modules:目前裝入核心的所有子產品名稱清單,可以由lsmod指令使用,也可以直接檢視
proc檔案系統相關内容 - /proc/partitions:塊裝置每個分區的主裝置号(major)和次裝置号(minor)等資訊,同時包括每個分區所包含的塊(block)數目
proc檔案系統相關内容 - /proc/slabinfo:對象相關slap的資訊
- /proc/uptime:系統上次啟動以來的運作時間,如下所示,其第一個數字表示系統運作時間,第二個數字表示系統空閑時間,機關是秒;
proc檔案系統相關内容 - /proc/version:目前系統運作的核心版本号
proc檔案系統相關内容
二、使用者程序資訊
/proc目錄中包含許多以數字命名的子目錄,這些數字表示系統目前正在運作程序的程序号,裡面包含對應程序相關的多個資訊檔案。
- cmdline — 啟動目前程序的完整指令,但僵屍程序目錄中的此檔案不包含任何資訊
- cwd — 指向目前程序運作目錄的一個符号連結
- exe — 指向啟動目前程序的可執行檔案(完整路徑)的符号連結,通過/proc/N/exe可以啟動目前程序的一個拷貝
- fd — 這是個目錄,包含目前程序打開的每一個檔案的檔案描述符(file descriptor),這些檔案描述符是指向實際檔案的一個符号連結
- limits — 目前程序所使用的每一個受限資源的軟限制、硬限制和管理單元;此檔案僅可由實際啟動目前程序的UID使用者讀取;(2.6.24以後的核心版本支援此功能);
- maps — 目前程序關聯到的每個可執行檔案和庫檔案在記憶體中的映射區域及其通路權限所組成的清單;
- mem — 目前程序所占用的記憶體空間,由open、read和lseek等系統調用使用,不能被使用者讀取;
- root — 指向目前程序運作根目錄的符号連結;
- stat — 目前程序的狀态資訊,包含一系統格式化後的資料列,可讀性差,通常由ps指令使用;
- statm — 目前程序占用記憶體的狀态資訊,通常以“頁面”(page)表示;
- status — 與stat所提供資訊類似,但可讀性較好
- task — 目錄檔案,包含由目前程序所運作的每一個線程的相關資訊,每個線程的相關資訊檔案均儲存在一個由線程号(tid)命名的目錄中,這類似于其内容類似于每個程序目錄中的内容;
三、proc程式設計
與sysfs虛拟檔案系統相比,proc的使用沒有sysfs那麼組織嚴謹,更加随意,是以在核心子產品中用proc檔案系統與使用者空間進行資訊互動還是很友善的。
在核心子產品中使用proc,需要包含頭檔案
其中常用的函數包括:
- 建立一個檔案:
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent);
name: 檔案名稱
mode:檔案屬性,常用S_IWUSR | S_IRUGO
parent:父目錄,即建立的檔案在那個目錄下,若為NULL,則在/proc目錄下建立檔案
2. 建立一個隻讀的檔案:
struct proc_dir_entry* create_proc_read_entry(const char* name, mode_t mode, struct proc_dir_entry* parent, read_proc_t* read_proc, void* data);
read_proc是讀操作回調函數,data是多個檔案共享一個操作函數時區分檔案的私有資料
3. 建立一個目錄:
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent);
參數意義同上,隻是這個函數在proc檔案系統中建立的是一個目錄
4. 建立一個符号連結:
struct proc_dir_entry* proc_symlink(const chat *name, struct proc_dir_entry *parent, const char *dest)
在parent目錄下建立符号連結,效果如同:ln –s dest name
5. 建立一個裝置節點
struct proc_dir_entry* proc_mknod(const char *name, mode_t mode, struct proc_dir_entry *parent, kdev_t rdev)
在parent目錄下建立一個裝置節點,其中rdev可以由MKDEV宏生成,mode參數必須包含S_IFBLK或S_IFCHR表示建立的是塊裝置還是字元裝置。
效果如同:mknod --mode=mode name rdev
6. 移除proc檔案系統中的入口
void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
上面建立的所有對象,都可以通過這個函數移除
為了對proc檔案系統中的檔案進行讀寫操作,需要設定相應的回調函數,在傳回的對應proc檔案的struct proc_dir_entry結構中,設定其中的:
entry->read_proc和entry->write_proc參數。
其中entry->read_proc的函數原型為:
int read_func(char* page, char** start, off_t off, int count, int* eof, void* data); 從核心讀取資料
核心傳回的資料需要寫入page,其中page為核心位址,off和count分别是寫入在page中的偏移位址和可以寫入的最大位元組,則兩個參數主要是用于more和less指令,一般若内容較少可以忽略這兩個參數。若這兩個參數被使用,則需要設定eof為1來表示已到達檔案尾部。
data參數用于同一個read_func函數為多個proc檔案服務時,進行區分所操作的proc檔案,并可以存放私有資料。
這個函數的傳回值是複制到page記憶體中的位元組數。
entry->write_proc的函數原型為:
int write_func(struct file* file, const char* buffer, unsigned long count, void* data); 寫入資料到核心
參數count表示可以從buffer中通路的最大位元組數,由于buffer是使用者空間記憶體,需要使用copy_from_user複制buffer中的資料到核心空間。file參數一般被忽略,而data同上也是用于區分多個檔案時的操作對象。
參數data的使用執行個體一般為:
struct proc_dir_entry* entry;
struct my_file_data *file_data; //檔案資料
file_data = kmalloc(sizeof(struct my_file_data), GFP_KERNEL); //配置設定空間
entry->data = file_data; //這裡是重點,需要将其指派給對應proc檔案的proc_dir_entry結構,在read和write是才能區分
int foo_read_func(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len;
if(data == file_data) {
}
else {
}
return len;
}
需要注意的是,若entry->data的參數是動态配置設定的,在調用remove_proc_entry時,需要先将對應的data空間釋放。
#include
#include
#include
#include
#include
#include
#define MODULE_VERSION "1.0"
#define MODULE_NAME "procfs_example"
#define FOOBAR_LEN 8
struct fb_data_t {
char name[FOOBAR_LEN + 1];
char value[FOOBAR_LEN + 1];
};
static struct proc_dir_entry *example_dir, *foo_file, *bar_file, *jiffies_file, *tty_device, *symlink;
struct fb_data_t foo_data, bar_data;
static int proc_read_jiffies(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len;
len = sprintf(page, "jiffies = %ld\n", jiffies);
return len;
}
static int proc_read_foobar(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len;
struct fb_data_t *fb_data = (struct fb_data_t *)data;
len = sprintf(page, "%s = ’%s’\n", fb_data->name, fb_data->value);
return len;
}
static int proc_write_foobar(struct file *file, const char *buffer, unsigned long count, void *data)
{
int len;
struct fb_data_t *fb_data = (struct fb_data_t *)data;
if(count > FOOBAR_LEN)
len = FOOBAR_LEN;
else
len = count;
if(copy_from_user(fb_data->value, buffer, len))
return -EFAULT;
fb_data->value[len] = ’\0’;
return len;
}
static int __init init_procfs_example(void)
{
int rv = 0;
example_dir = proc_mkdir(MODULE_NAME, NULL);
if(example_dir == NULL) {
rv = -ENOMEM;
goto out;
}
jiffies_file = create_proc_read_entry("jiffies", 0444, example_dir, proc_read_jiffies, NULL);
if(jiffies_file == NULL) {
rv = -ENOMEM;
goto no_jiffies;
}
foo_file = create_proc_entry("foo", 0644, example_dir);
if(foo_file == NULL) {
rv = -ENOMEM;
goto no_foo;
}
strcpy(foo_data.name, "foo");
strcpy(foo_data.value, "foo");
foo_file->data = &foo_data;
foo_file->read_proc = proc_read_foobar;
foo_file->write_proc = proc_write_foobar;
bar_file = create_proc_entry("bar", 0644, example_dir);
if(bar_file == NULL) {
rv = -ENOMEM;
goto no_bar;
}
strcpy(bar_data.name, "bar");
strcpy(bar_data.value, "bar");
bar_file->data = &bar_data;
bar_file->read_proc = proc_read_foobar;
bar_file->write_proc = proc_write_foobar;
tty_device = proc_mknod("tty", S_IFCHR | 0666, example_dir, MKDEV(5, 0));
if(tty_device == NULL) {
rv = -ENOMEM;
goto no_tty;
}
symlink = proc_symlink("jiffies_too", example_dir, "jiffies");
if(symlink == NULL) {
rv = -ENOMEM;
goto no_symlink;
}
printk(KERN_INFO "%s %s initialised\n", MODULE_NAME, MODULE_VERSION);
return 0;
no_symlink:
remove_proc_entry("tty", example_dir);
no_tty:
remove_proc_entry("bar", example_dir);
no_bar:
remove_proc_entry("foo", example_dir);
no_foo:
remove_proc_entry("jiffies", example_dir);
no_jiffies:
remove_proc_entry(MODULE_NAME, NULL);
out:
return rv;
}
static void __exit cleanup_procfs_example(void)
{
remove_proc_entry("jiffies_too", example_dir);
remove_proc_entry("tty", example_dir);
remove_proc_entry("bar", example_dir);
remove_proc_entry("foo", example_dir);
remove_proc_entry("jiffies", example_dir);
remove_proc_entry(MODULE_NAME, NULL);
printk(KERN_INFO "%s %s removed\n",
MODULE_NAME, MODULE_VERSION);
}
module_init(init_procfs_example);
module_exit(cleanup_procfs_example);
MODULE_AUTHOR("Erik Mouw");
MODULE_DESCRIPTION("procfs examples");