seq file 講得很明白

2021-05-07 13:47:24 字數 3909 閱讀 8216

kernel module程式設計(八):讀取proc檔案之seq_file

在上次我們使用了

read_proc的方式通過/proc檔案讀取kernel module的資訊。作者給的例子他自己說是ugly。而我們在讀取大量資料時發現,受到使用者buffer大小的限制(page的大小),可能需要讀取多次,不僅需要記錄上次讀取的位置,而且由於每次讀取我們申請了訊號量,讀取完釋放,那麼如果多次讀取的間隔中,如果訊號量被寫所獲取就好出現混亂。

linux kernel提供seq_file更好的方式來解決這個問題,除非我們確定讀取的資訊量非常少,能夠在page中返回,我們應使用seq_file的方式而不是read_proc 。

ldd3中介紹的方式,我覺得是典型的西方人和中國人思維方式的不同。

在seq_file的介紹中,ldd3先從每個操作具體將其,然後到如何和proc檔案聯絡,最後到如何建立proc檔案,我喜歡反過來的方式,先建立proc,在一步步細化。老外是日月年,我們是年月日,嘿嘿。 seq_file的處理方式開看有點發展,步驟有些多,但是安全,是規範的處理方式。

步驟一:建立proc檔案。

通過乙個struct proc_dir_entry的元素,在/proc中建立檔案,如下:

struct proc_dir_entry * entry = create_proc_entry(

「scullseq

」,0,null)。

引數的內容和

read_proc,第乙個引數表示檔名,第二個引數表示檔案屬性,對於唯讀方式為0,第三個引數表示檔案路徑,null表示預設路徑,即/proc。

步驟二:關聯

proc的操作。

需要對檔案進行操作,見過檔案和struct file_operations相關聯,我們注意到這個資料結構也用於模組操作關聯中。具體操作如下:

#ifdef scull_seq_file

/* 步驟二:2、定義proc檔案所關聯的檔案運算元據 */

staticstruct file_operationsscull_proc_ops = ;

/* 步驟二:4、在前面的步驟二1~3中我們建立了proc檔案,關聯了proc檔案和file_operations,並進一步關聯了seq_file,這裡我們具體定義被關聯的seq_file */

staticstruct seq_operationsscull_seq_ops = ;

static struct proc_dir_entry * entry;

#endif … …

static int __init scull_init(void)

... ...

static void __exit scull_exit(void)

else

unregister_chrdev_region(dev,scull_dev_num);

wdebug(wei_kern_notice,"scull module exit/n");

} }

/* 步驟二:3、具體實現proc檔案的open操作,目的與seq_file相關聯。

*/int scull_proc_open(struct inode * inode , struct file * file)

步驟三:處理

seq_file操作過程。

seq_file操作定義了四個操作,格式如下:

void * start(struct seq_file * s, loff_t * v);

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

void

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

intshow (struct seq_file * s, void * v);

其中loff_t表示位置,這是由我們自己程式控制的,初始為0,在scull中我們依次讀取scull0-3,因此使用該偏移量來表示我們所讀取的裝置的序號。

我們利用

void * v來記錄裝置的入口位置。start根據編譯量,即我們的裝置的序號,返回scullx的入口位置,無論下一操作是next,stop,還是show,這個返回值會作為引數void *v輸入。next表示下一查詢,和start相似,只是多了void * v的輸入,同樣它的返回值也作為下一操作的引數void *v輸入。show用於通過/proc檔案輸出。stop表示一次讀取的結束。雖然在seq_file中和read_proc不一樣,不需要考慮每次可以輸出的buff的大小,但是實際讀取不會連續一片很大的資料輸出,在例子後面,我們將討論這些操作的執行。

輸出方式非常簡單,一般可以使用

seq_printf,另外還有seq_putc,seq_puts,seq_escape。例子如下:

#ifdef scull_seq_file

void *scull_seq_start(struct seq_file * s, loff_t * pos)

void *scull_seq_next(struct seq_file * s, void * v, loff_t * pos)

voidscull_seq_stop(struct seq_file * s, void * v)

intscull_seq_show(struct seq_file * s, void * v) }

} up(&dev->sem);

return 0;

}我們描述一下處理的過程:當我們讀取proc檔案,例如cat /proc/scullseq時,我們假設scull0和scull1都有較多資訊輸入。

一開始呼叫start,偏移量為0,返回scull0的入口,接著呼叫show,scull0的入口作為引數輸入,在show中,我們可以遍歷 scull0的資料結構,通過seq_printf輸出。

完成show後,由於輸出資訊多,進入stop,在例子中stop沒有實際操作,我們只是用來跟蹤處理的流程。

再次呼叫start,偏移量步進1,即1,返回scull0的入口,接著呼叫show,scull1的入口作為引數輸入,在show中,我們可以遍歷scull1的資料結構,通過seq_printf輸出。完成show後,由於輸出資訊多,進入stop。

再次呼叫start,偏移量步進1,即2,返回scull2的入口,接著呼叫show,scull2的入口作為引數輸入,在show中,我們可以遍歷scull2的資料結構,通過seq_printf輸出。

完成show後,由於輸出資訊非常少,kernel認為可以繼續進行操作,而不需要 stop,呼叫next(),在next引數中輸入的引數loff_t為2,next將其加一,為3,返回scull3的入口。接著呼叫 show,scull3的入口作為引數輸入,在show中,我們可以遍歷scull3的資料結構,通過seq_printf輸出。完成show後,由於輸出資訊非常少,kernel認為可以繼續進行操作,而不需要stop,呼叫next()。由於已經全部資訊返回,在next中發現沒有資料,返回 null。系統再次呼叫start,返回null,系統呼叫stop,結束這次輸出。

再次呼叫

start,返回null,表示已經沒有資料輸出,呼叫stop,結束所有的輸出。

值得注意

seq_file **在呼叫 start 和 stop 之間不睡眠或者進行其他非原子性任務. 你也肯定會看到在呼叫 start 後馬上有乙個 stop 呼叫. 因此, 對你的 start 方法來說請求訊號量或自旋鎖是安全的. 只要你的其他 seq_file 方法是原子的, 呼叫的整個序列是原子的.(http://www.deansys.com/doc/ldd3/ch04s03.html)

本文也即《linux device drivers》,ldd3的第四章debuging techniques的讀書筆記之三,但我們不限於此內容。

序列檔案 seq file 介面

內容簡介 本文主要講述序列檔案 seq file 介面的核心實現,如何使用它將linux核心裡面常用的資料結構通過檔案 主要關注proc檔案 匯出到使用者空間,最後定義了一些巨集以便於程式設計,減少重複 在分析序列檔案介面實現的過程中,還連帶涉及到一些應用陷阱和避免手段。序列檔案介面 unix的世界...

序列檔案 seq file 介面

內容簡介 本文主要講述序列檔案 seq file 介面的核心實現,如何使用它將linux核心裡面常用的資料結構通過檔案 主要關注proc檔案 匯出到使用者空間,最後定義了一些巨集以便於程式設計,減少重複 在分析序列檔案介面實現的過程中,還連帶涉及到一些應用陷阱和避免手段。序列檔案介面 unix的世界...

序列檔案 seq file 介面

內容簡介 本文主要講述序列檔案 seq file 介面的核心實現,如何使用它將linux核心裡面常用的資料結構通過檔案 主要關注proc檔案 匯出到 使用者空間,最後定義了一些巨集以便於程式設計,減少重複 在分析序列檔案介面實現的過程中,還連帶涉及到一些應用陷阱和避免手段。序列檔案介面 unix的世...