一般地,內核通過在procfs文件系統下建立文件來向用戶空間提供輸出信息,用戶空間可以通過任何文本閱讀應用查看該文件信息,但是procfs 有一個缺陷,如果輸出內容大于1個內存頁,需要多次讀,因此處理起來很難,另外,如果輸出太大,速度比較慢,有時會出現一些意想不到的情況, Alexander Viro實現了一套新的功能,使得內核輸出大文件信息更容易,該功能出現在2.4.15(包括2.4.15)以后的所有2.4內核以及2.6內核中,尤其 是在2.6內核中,已經大量地使用了該功能。
要想使用seq_file功能,開發者需要包含頭文件linux/seq_file.h,并定義與設置一個seq_operations結構(類似于file_operations結構):?
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函數用于指定seq_file文件的讀開始位置,返回實際讀開始位置,如果指定的位置超過文件末尾,應當返回NULL,start函數可以有一個特殊的返回SEQ_START_TOKEN,它用于讓show函數輸出文件頭,但這只能在pos為0時使用,next函數用于把seq_file文件的當前讀位置移動到下一個讀位置,返回實際的下一個讀位置,如果已經到達文件末尾,返回NULL,stop函數用于在讀完seq_file文件后調用,它類似于文件操作close,用于做一些必要的清理,如釋放內存等,show函數用于格式化輸出,如果成功返回0,否則返回出錯碼。
Seq_file也定義了一些輔助函數用于格式化輸出:
/*函數seq_putc用于把一個字符輸出到seq_file文件*/
int seq_putc(struct seq_file *m, char c);
/*函數seq_puts則用于把一個字符串輸出到seq_file文件*/
int seq_puts(struct seq_file *m, const char *s);
/*函數seq_escape類似于seq_puts,只是,它將把第一個字符串參數中出現的包含在第二個字符串參數
中的字符按照八進制形式輸出,也即對這些字符進行轉義處理*/
int seq_escape(struct seq_file *, const char *, const char *);
/*函數seq_printf是最常用的輸出函數,它用于把給定參數按照給定的格式輸出到seq_file文件*/
int seq_printf(struct seq_file *, const char *, ...)__attribute__ ((format(printf,2,3)));
/*函數seq_path則用于輸出文件名,字符串參數提供需要轉義的文件名字符,它主要供文件系統使用*/
int seq_path(struct seq_file *, struct vfsmount *, struct dentry *, char *);
在定義了結構struct seq_operations之后,用戶還需要把打開seq_file文件的open函數,以便該結構與對應于seq_file文件的struct file結構關聯起來,例如,struct seq_operations定義為:
struct seq_operations exam_seq_ops = {
.start = exam_seq_start,
.stop = exam_seq_stop,
.next = exam_seq_next,
.show = exam_seq_show
};
那么,open函數應該如下定義:
?
static int exam_seq_open(struct inode *inode, struct file *file)
{
return seq_open(file, &exam_seq_ops);
};
注意,函數seq_open是seq_file提供的函數,它用于把struct seq_operations結構與seq_file文件關聯起來。
最后,用戶需要如下設置struct file_operations結構:?
struct file_operations exam_seq_file_ops = {
.owner = THIS_MODULE,
.open = exm_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
注意,用戶僅需要設置open函數,其它的都是seq_file提供的函數。
然后,用戶創建一個/proc文件并把它的文件操作設置為exam_seq_file_ops即可:
struct proc_dir_entry *entry;
entry = create_proc_entry("exam_seq_file", 0, NULL);
if (entry)
entry->proc_fops = &exam_seq_file_ops;
對于簡單的輸出,seq_file用戶并不需要定義和設置這么多函數與結構,它僅需定義一個show函數,然后使用single_open來定義open函數就可以,以下是使用這種簡單形式的一般步驟:
1.定義一個show函數
int exam_show(struct seq_file *p, void *v)
{
…
}
2. 定義open函數
int exam_single_open(struct inode *inode, struct file *file)
{
return(single_open(file, exam_show, NULL));
}
注意要使用single_open而不是seq_open。
3. 定義struct file_operations結構??
struct file_operations exam_single_seq_file_operations = {
.open = exam_single_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
注意,如果open函數使用了single_open,release函數必須為single_release,而不是seq_release。 下面給出了一個使用seq_file的具體例子seqfile_exam.c,它使用seq_file提供了一個查看當前系統運行的所有進程的/proc接口,在編譯并插入該模塊后,用戶通過命令"cat /proc/exam_esq_file"可以查看系統的所有進程。
//kernel module: seqfile_exam.c
#include
#include
#include
#include
#include
#include
static struct proc_dir_entry *entry;
static void *l_start(struct seq_file *m, loff_t * pos)
{
loff_t index = *pos;
if (index == 0) {
seq_printf(m, "Current all the processes in system:\n"
"%-24s%-5s\n", "name", "pid");
return &init_task;
}
else {
return NULL;
}
}
static void *l_next(struct seq_file *m, void *p, loff_t * pos)
{
task_t * task = (task_t *)p;
task = next_task(task);
if ((*pos != 0) && (task == &init_task)) {
return NULL;
}
++*pos;
return task;
}
static void l_stop(struct seq_file *m, void *p)
{
}
static int l_show(struct seq_file *m, void *p)
{
task_t * task = (task_t *)p;
seq_printf(m, "%-24s%-5d\n", task->comm, task->pid);
return 0;
}
static struct seq_operations exam_seq_op = {
.start = l_start,
.next = l_next,
.stop = l_stop,
.show = l_show
};
static int exam_seq_open(struct inode *inode, struct file *file)
{
return seq_open(file, &exam_seq_op);
}
static struct file_operations exam_seq_fops = {
.open = exam_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static int __init exam_seq_init(void)
{
entry = create_proc_entry("exam_esq_file", 0, NULL);
if (entry)
entry->proc_fops = &exam_seq_fops;
return 0;
}
static void __exit exam_seq_exit(void)
{
remove_proc_entry("exam_esq_file", NULL);
}
module_init(exam_seq_init);
module_exit(exam_seq_exit);
MODULE_LICENSE("GPL");
?
評論
查看更多