/proc文件系統用於內核調試

火星人 @ 2014-03-26 , reply:0



一、/proc文件系統簡介

Linux提供了一個特殊的文件系統——/proc,通過建立內核與進程之間發送信息的機制,使得可以在進程運行時動態地讀寫內核內部的數據結構、改變內核設置。與其他文件系統的不同之處在於,/proc是處於內存之中的。

/proc中的每個文件都綁定於一個內核函數,當用戶讀取某個文件時,將調用指定函數讀取所需信息返回給用戶空間,對於內核模塊調試而言,需要查看內核所處的狀態等信息,此時將可以通過在/proc下創建對應文件,通過讀取該文件來及時返回指定內核模塊信息。由於大多數/proc文件是只讀項,這裡介紹只讀的情況。

二、使用/proc文件系統

1、傳統/proc介面

使用/proc時需要包含頭文件,首先需要創建一個函數,使得進程讀取指定文件時,可以通過該函數來返回指定信息,該函數稱為read_proc方法。當讀取文件時,內核將分配一個內存頁,該內存頁指針將作為參數傳遞給read_proc方法,在方法中填寫所需內容到緩衝區中,最後返回給用戶。Read_proc定義:
QUOTE:
Int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);

其中page是所分配的內存頁,start將指示實際數據寫到的內存頁的位置,offset指示讀取的虛擬文件的位置,count是讀取的位元組數,eof是一個簡單的標誌,data用於內部記錄。

一旦定義好了read­_proc函數,就應該將其與一個/proc入口項連接起來,其介面函數為:
QUOTE:
struct proc_dir_entry * create_proc_read_entry(const char *name, mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc,void *data);

通過該函數,可以在指定目錄下(/proc或者其某個子目錄)創建一個名為name的文件,其中的read_proc就是開始所編寫的函數,調用該函數后,read_proc就與name文件結合起來了,當查看name文件時將調用read_proc文件輸出內核信息。

2、seq_file介面

在使用/proc具有很多局限,由於內核分配的內存頁只有一頁,在讀取大文件時介面函數的調用將會很複雜,同時在/proc項的刪除時,該文件可能正在被使用。另外,內核不會對同名的文件入口項進行檢查,導致無法區分。

使用seq_file將為大的內核虛擬文件提供更簡單的介面。seq_file假定我們正在創建的虛擬文件要順序遍歷一個項目序列,這些項目就是要返回給用戶空間的信息。

seq_file包括三方面的內容:

一組iterator介面,使得可以遍歷整個虛擬文件

一組便利的格式化輸出工具

一組封裝的file_operation操作,實現了對虛擬文件的大部分操作

我們通過創建iterator來使用相應介面,對項目序列進行遍歷,輸出所有內容。使用時首先要包含頭文件, 之後建立4個iterator對象,start、next、stop和show。

對應的對象為:
QUOTE:
struct seq_operations

{

void *(*start)(struct seq_file *m, loff_t *pos);

void (*stop)(struct seq_file *m, void *v);

void *(*next)(struct seq_file *m, void *v, loff_t *pos);

int (*show) (struct seq_file *m, void *v);

}

其中start方法用pos作參數,將返回一個iterator對象,表示的是文件讀取的起始位置。如果指定的位置超過文件末尾,應當返回NULL。對於一些複雜的應用程序,seq_file結構中的private成員將被使用。start函數還有一個特殊的返回值——SEQ_START_TOKEN,當在show函數之前想列印出一個頭部信息時可以使用該標誌。

next()函數任務是將iterator的位置前進到下一項,將返回一個iterator對象,如果已到達序列末尾將返回NULL。

當遍歷過程完成時將調用stop函數,已完成清除工作。比如使用了動態內存進行分配時在此處可以完成內存釋放工作。

show函數將當前iterator對象所指向的對象以格式化形式進行輸出。正常完成時將返回0,否則返回錯誤碼。

3、seq_file格式化輸出

為了將信息輸出到用戶空間,定義了一組格式化的輸出工具,其中最常用的即是seq_printf(),類似於printk的功能,但使用seq_file指針作為參數。通常並不會檢查其返回值,但是如果返回值為非零時則說明發生了錯誤,如buffer已滿無法繼續寫入數據。

對於直接輸出字元,可以使用:
QUOTE:
int seq_putc(struct seq_file *m, char c);

int seq_puts(struct seq_file *m, const char *s);

int seq_escape(struct seq_file *m, const char *s, const char * esc);

4、使用seq_file連接/proc

以上完成了具體操作的定義,下面應該將其與具體的/proc下的文件連接起來。首先創建一個file_operation結構,該結構封裝了在/proc所需的必要操作。為了將這些操作與文件連接起來,首先要使用對應的open操作來完成:
QUOTE:
Static int ct_open(struct inode *inode, struct file *file)

{Return seq_open(file, &ct_seq_ops);}

在成功完成該調用后,seq_open將seq_file指針存放在file->private_data中。因此ct_open是唯一一個由我們自己定義的file_operation中的操作,其他的read,llseek,release都是由seq_file代碼本身實現的,即file_operation應該定義為:
QUOTE:
static struct file_operations ct_file_ops = {

.owner = THIS_MODULE,

.open = ct_open,

.read = seq_read,

.llseek = seq_lseek,

.release = seq_release};

最後,則是創建/proc文件本身,通過使用create_proc_entry來實現,如:
QUOTE:
static int ct_init(void){

struct proc_dir_entry * entry;

entry = create_proc_entry(「sequence」, 0, NULL);

if(entry)

entry->proc_fops = &ct_file_ops;

return 0;}

module_init(ct_init);





[火星人 via ] /proc文件系統用於內核調試已經有160次圍觀

http://www.coctec.com/docs/linux/show-post-185889.html