一、procfsprocfs是比較老的一種用戶態(tài)與內(nèi)核態(tài)的數(shù)據(jù)交換方式,內(nèi)核的很多數(shù)據(jù)都是通過這種方式出口給用戶的,內(nèi)核的很多參數(shù)也是通過這種方式來讓用戶方便設(shè)置的。除了sysctl出口到/proc下的參數(shù),procfs提供的大部分內(nèi)核參數(shù)是只讀的。實(shí)際上,很多應(yīng)用嚴(yán)重地依賴于procfs,因此它幾乎是必不可少的組件。前面部分的幾個(gè)例子實(shí)際上已經(jīng)使用它來出口內(nèi)核數(shù)據(jù),但是并沒有講解如何使用,本節(jié)將講解如何使用procfs。 Procfs提供了如下API:
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)該函數(shù)用于創(chuàng)建一個(gè)正常的proc條目,參數(shù)name給出要建立的proc條目的名稱,參數(shù)mode給出了建立的該proc條目的訪問權(quán)限,參數(shù)parent指定建立的proc條目所在的目錄。如果要在/proc下建立proc條目,parent應(yīng)當(dāng)為NULL。否則它應(yīng)當(dāng)為proc_mkdir返回的struct proc_dir_entry結(jié)構(gòu)的指針。
extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent) 該函數(shù)用于刪除上面函數(shù)創(chuàng)建的proc條目,參數(shù)name給出要?jiǎng)h除的proc條目的名稱,參數(shù)parent指定建立的proc條目所在的目錄。
struct proc_dir_entry *proc_mkdir(const char * name, struct proc_dir_entry *parent) 該函數(shù)用于創(chuàng)建一個(gè)proc目錄,參數(shù)name指定要?jiǎng)?chuàng)建的proc目錄的名稱,參數(shù)parent為該proc目錄所在的目錄。
extern struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
struct proc_dir_entry *parent);
struct proc_dir_entry *proc_symlink(const char * name,
struct proc_dir_entry * parent, const char * dest)該函數(shù)用于建立一個(gè)proc條目的符號(hào)鏈接,參數(shù)name給出要建立的符號(hào)鏈接proc條目的名稱,參數(shù)parent指定符號(hào)連接所在的目錄,參數(shù)dest指定鏈接到的proc條目名稱。
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)該函數(shù)用于建立一個(gè)規(guī)則的只讀proc條目,參數(shù)name給出要建立的proc條目的名稱,參數(shù)mode給出了建立的該proc條目的訪問權(quán)限,參數(shù)base指定建立的proc條目所在的目錄,參數(shù)read_proc給出讀去該proc條目的操作函數(shù),參數(shù)data為該proc條目的專用數(shù)據(jù),它將保存在該proc條目對(duì)應(yīng)的struct file結(jié)構(gòu)的private_data字段中。
struct proc_dir_entry *create_proc_info_entry(const char *name,
mode_t mode, struct proc_dir_entry *base, get_info_t *get_info)該函數(shù)用于創(chuàng)建一個(gè)info型的proc條目,參數(shù)name給出要建立的proc條目的名稱,參數(shù)mode給出了建立的該proc條目的訪問權(quán)限,參數(shù)base指定建立的proc條目所在的目錄,參數(shù)get_info指定該proc條目的get_info操作函數(shù)。實(shí)際上get_info等同于read_proc,如果proc條目沒有定義個(gè)read_proc,對(duì)該proc條目的read操作將使用get_info取代,因此它在功能上非常類似于函數(shù)create_proc_read_entry。
struct proc_dir_entry *proc_net_create(const char *name,
mode_t mode, get_info_t *get_info)該函數(shù)用于在/proc/net目錄下創(chuàng)建一個(gè)proc條目,參數(shù)name給出要建立的proc條目的名稱,參數(shù)mode給出了建立的該proc條目的訪問權(quán)限,參數(shù)get_info指定該proc條目的get_info操作函數(shù)。
struct proc_dir_entry *proc_net_fops_create(const char *name,
mode_t mode, struct file_operations *fops)該函數(shù)也用于在/proc/net下創(chuàng)建proc條目,但是它也同時(shí)指定了對(duì)該proc條目的文件操作函數(shù)。
void proc_net_remove(const char *name) 該函數(shù)用于刪除前面兩個(gè)函數(shù)在/proc/net目錄下創(chuàng)建的proc條目。參數(shù)name指定要?jiǎng)h除的proc名稱。 除了這些函數(shù),值得一提的是結(jié)構(gòu)struct proc_dir_entry,為了創(chuàng)建一了可寫的proc條目并指定該proc條目的寫操作函數(shù),必須設(shè)置上面的這些創(chuàng)建proc條目的函數(shù)返回的指針指向的struct proc_dir_entry結(jié)構(gòu)的write_proc字段,并指定該proc條目的訪問權(quán)限有寫權(quán)限。 為了使用這些接口函數(shù)以及結(jié)構(gòu)struct proc_dir_entry,用戶必須在模塊中包含頭文件linux/proc_fs.h。 在源代碼包中給出了procfs示例程序procfs_exam.c,它定義了三個(gè)proc文件條目和一個(gè)proc目錄條目,讀者在插入該模塊后應(yīng)當(dāng)看到如下結(jié)構(gòu):
$ ls /proc/myproctest aint astring bigprocfile $ 讀者可以通過cat和echo等文件操作函數(shù)來查看和設(shè)置這些proc文件。特別需要指出,bigprocfile是一個(gè)大文件(超過一個(gè)內(nèi)存頁(yè)),對(duì)于這種大文件,procfs有一些限制,因?yàn)樗峁┑木彺?,只有一個(gè)頁(yè),因此必須特別小心,并對(duì)超過頁(yè)的部分做特別的考慮,處理起來比較復(fù)雜并且很容易出錯(cuò),所有procfs并不適合于大數(shù)據(jù)量的輸入輸出,后面一節(jié)seq_file就是因?yàn)檫@一缺陷而設(shè)計(jì)的,當(dāng)然seq_file依賴于procfs的一些基礎(chǔ)功能。 二、seq_file一般地,內(nèi)核通過在procfs文件系統(tǒng)下建立文件來向用戶空間提供輸出信息,用戶空間可以通過任何文本閱讀應(yīng)用查看該文件信息,但是procfs有一個(gè)缺陷,如果輸出內(nèi)容大于1個(gè)內(nèi)存頁(yè),需要多次讀,因此處理起來很難,另外,如果輸出太大,速度比較慢,有時(shí)會(huì)出現(xiàn)一些意想不到的情況,Alexander Viro實(shí)現(xiàn)了一套新的功能,使得內(nèi)核輸出大文件信息更容易,該功能出現(xiàn)在2.4.15(包括2.4.15)以后的所有2.4內(nèi)核以及2.6內(nèi)核中,尤其是在2.6內(nèi)核中,已經(jīng)大量地使用了該功能。 要想使用seq_file功能,開發(fā)者需要包含頭文件linux/seq_file.h,并定義與設(shè)置一個(gè)seq_operations結(jié)構(gòu)(類似于file_operations結(jié)構(gòu)):
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函數(shù)用于指定seq_file文件的讀開始位置,返回實(shí)際讀開始位置,如果指定的位置超過文件末尾,應(yīng)當(dāng)返回NULL,start函數(shù)可以有一個(gè)特殊的返回SEQ_START_TOKEN,它用于讓show函數(shù)輸出文件頭,但這只能在pos為0時(shí)使用,next函數(shù)用于把seq_file文件的當(dāng)前讀位置移動(dòng)到下一個(gè)讀位置,返回實(shí)際的下一個(gè)讀位置,如果已經(jīng)到達(dá)文件末尾,返回NULL,stop函數(shù)用于在讀完seq_file文件后調(diào)用,它類似于文件操作close,用于做一些必要的清理,如釋放內(nèi)存等,show函數(shù)用于格式化輸出,如果成功返回0,否則返回出錯(cuò)碼。 Seq_file也定義了一些輔助函數(shù)用于格式化輸出:
int seq_putc(struct seq_file *m, char c); 函數(shù)seq_putc用于把一個(gè)字符輸出到seq_file文件。
int seq_puts(struct seq_file *m, const char *s); 函數(shù)seq_puts則用于把一個(gè)字符串輸出到seq_file文件。
int seq_escape(struct seq_file *, const char *, const char *); 函數(shù)seq_escape類似于seq_puts,只是,它將把第一個(gè)字符串參數(shù)中出現(xiàn)的包含在第二個(gè)字符串參數(shù)中的字符按照八進(jìn)制形式輸出,也即對(duì)這些字符進(jìn)行轉(zhuǎn)義處理。
int seq_printf(struct seq_file *, const char *, ...)
__attribute__ ((format (printf,2,3)));函數(shù)seq_printf是最常用的輸出函數(shù),它用于把給定參數(shù)按照給定的格式輸出到seq_file文件。
int seq_path(struct seq_file *, struct vfsmount *, struct dentry *, char *); 函數(shù)seq_path則用于輸出文件名,字符串參數(shù)提供需要轉(zhuǎn)義的文件名字符,它主要供文件系統(tǒng)使用。 在定義了結(jié)構(gòu)struct seq_operations之后,用戶還需要把打開seq_file文件的open函數(shù),以便該結(jié)構(gòu)與對(duì)應(yīng)于seq_file文件的struct file結(jié)構(gòu)關(guān)聯(lián)起來,例如,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函數(shù)應(yīng)該如下定義:
static int exam_seq_open(struct inode *inode, struct file *file)
{
return seq_open(file, &exam_seq_ops);
};注意,函數(shù)seq_open是seq_file提供的函數(shù),它用于把struct seq_operations結(jié)構(gòu)與seq_file文件關(guān)聯(lián)起來。 最后,用戶需要如下設(shè)置struct file_operations結(jié)構(gòu):
struct file_operations exam_seq_file_ops = {
.owner = THIS_MODULE,
.open = exm_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};注意,用戶僅需要設(shè)置open函數(shù),其它的都是seq_file提供的函數(shù)。 然后,用戶創(chuàng)建一個(gè)/proc文件并把它的文件操作設(shè)置為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;對(duì)于簡(jiǎn)單的輸出,seq_file用戶并不需要定義和設(shè)置這么多函數(shù)與結(jié)構(gòu),它僅需定義一個(gè)show函數(shù),然后使用single_open來定義open函數(shù)就可以,以下是使用這種簡(jiǎn)單形式的一般步驟: 1.定義一個(gè)show函數(shù)
int exam_show(struct seq_file *p, void *v)
{
…
}2. 定義open函數(shù)
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結(jié)構(gòu)
struct file_operations exam_single_seq_file_operations = {
.open = exam_single_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};注意,如果open函數(shù)使用了single_open,release函數(shù)必須為single_release,而不是seq_release。 在源代碼包中給出了一個(gè)使用seq_file的具體例子seqfile_exam.c,它使用seq_file提供了一個(gè)查看當(dāng)前系統(tǒng)運(yùn)行的所有進(jìn)程的/proc接口,在編譯并插入該模塊后,用戶通過命令"cat /proc/ exam_esq_file"可以查看系統(tǒng)的所有進(jìn)程。 三、debugfs內(nèi)核開發(fā)者經(jīng)常需要向用戶空間應(yīng)用輸出一些調(diào)試信息,在穩(wěn)定的系統(tǒng)中可能根本不需要這些調(diào)試信息,但是在開發(fā)過程中,為了搞清楚內(nèi)核的行為,調(diào)試信息非常必要,printk可能是用的最多的,但它并不是最好的,調(diào)試信息只是在開發(fā)中用于調(diào)試,而printk將一直輸出,因此開發(fā)完畢后需要清除不必要的printk語句,另外如果開發(fā)者希望用戶空間應(yīng)用能夠改變內(nèi)核行為時(shí),printk就無法實(shí)現(xiàn)。因此,需要一種新的機(jī)制,那只有在需要的時(shí)候使用,它在需要時(shí)通過在一個(gè)虛擬文件系統(tǒng)中創(chuàng)建一個(gè)或多個(gè)文件來向用戶空間應(yīng)用提供調(diào)試信息。 有幾種方式可以實(shí)現(xiàn)上述要求: 使用procfs,在/proc創(chuàng)建文件輸出調(diào)試信息,但是procfs對(duì)于大于一個(gè)內(nèi)存頁(yè)(對(duì)于x86是4K)的輸出比較麻煩,而且速度慢,有時(shí)回出現(xiàn)一些意想不到的問題。 使用sysfs(2.6內(nèi)核引入的新的虛擬文件系統(tǒng)),在很多情況下,調(diào)試信息可以存放在那里,但是sysfs主要用于系統(tǒng)管理,它希望每一個(gè)文件對(duì)應(yīng)內(nèi)核的一個(gè)變量,如果使用它輸出復(fù)雜的數(shù)據(jù)結(jié)構(gòu)或調(diào)試信息是非常困難的。 使用libfs創(chuàng)建一個(gè)新的文件系統(tǒng),該方法極其靈活,開發(fā)者可以為新文件系統(tǒng)設(shè)置一些規(guī)則,使用libfs使得創(chuàng)建新文件系統(tǒng)更加簡(jiǎn)單,但是仍然超出了一個(gè)開發(fā)者的想象。 為了使得開發(fā)者更加容易使用這樣的機(jī)制,Greg Kroah-Hartman開發(fā)了debugfs(在2.6.11中第一次引入),它是一個(gè)虛擬文件系統(tǒng),專門用于輸出調(diào)試信息,該文件系統(tǒng)非常小,很容易使用,可以在配置內(nèi)核時(shí)選擇是否構(gòu)件到內(nèi)核中,在不選擇它的情況下,使用它提供的API的內(nèi)核部分不需要做任何改動(dòng)。 使用debugfs的開發(fā)者首先需要在文件系統(tǒng)中創(chuàng)建一個(gè)目錄,下面函數(shù)用于在debugfs文件系統(tǒng)下創(chuàng)建一個(gè)目錄:
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent); 參數(shù)name是要?jiǎng)?chuàng)建的目錄名,參數(shù)parent指定創(chuàng)建目錄的父目錄的dentry,如果為NULL,目錄將創(chuàng)建在debugfs文件系統(tǒng)的根目錄下。如果返回為-ENODEV,表示內(nèi)核沒有把debugfs編譯到其中,如果返回為NULL,表示其他類型的創(chuàng)建失敗,如果創(chuàng)建目錄成功,返回指向該目錄對(duì)應(yīng)的dentry條目的指針。 下面函數(shù)用于在debugfs文件系統(tǒng)中創(chuàng)建一個(gè)文件:
struct dentry *debugfs_create_file(const char *name, mode_t mode,
struct dentry *parent, void *data,
struct file_operations *fops);參數(shù)name指定要?jiǎng)?chuàng)建的文件名,參數(shù)mode指定該文件的訪問許可,參數(shù)parent指向該文件所在目錄,參數(shù)data為該文件特定的一些數(shù)據(jù),參數(shù)fops為實(shí)現(xiàn)在該文件上進(jìn)行文件操作的fiel_operations結(jié)構(gòu)指針,在很多情況下,由seq_file(前面章節(jié)已經(jīng)講過)提供的文件操作實(shí)現(xiàn)就足夠了,因此使用debugfs很容易,當(dāng)然,在一些情況下,開發(fā)者可能僅需要使用用戶應(yīng)用可以控制的變量來調(diào)試,debugfs也提供了4個(gè)這樣的API方便開發(fā)者使用:
struct dentry *debugfs_create_u8(const char *name, mode_t mode,
struct dentry *parent, u8 *value);
struct dentry *debugfs_create_u16(const char *name, mode_t mode,
struct dentry *parent, u16 *value);
struct dentry *debugfs_create_u32(const char *name, mode_t mode,
struct dentry *parent, u32 *value);
struct dentry *debugfs_create_bool(const char *name, mode_t mode,
struct dentry *parent, u32 *value);參數(shù)name和mode指定文件名和訪問許可,參數(shù)value為需要讓用戶應(yīng)用控制的內(nèi)核變量指針。 當(dāng)內(nèi)核模塊卸載時(shí),Debugfs并不會(huì)自動(dòng)清除該模塊創(chuàng)建的目錄或文件,因此對(duì)于創(chuàng)建的每一個(gè)文件或目錄,開發(fā)者必須調(diào)用下面函數(shù)清除:
void debugfs_remove(struct dentry *dentry); 參數(shù)dentry為上面創(chuàng)建文件和目錄的函數(shù)返回的dentry指針。 在源代碼包中給出了一個(gè)使用debufs的示例模塊debugfs_exam.c,為了保證該模塊正確運(yùn)行,必須讓內(nèi)核支持debugfs,debugfs是一個(gè)調(diào)試功能,因此它位于主菜單Kernel hacking,并且必須選擇Kernel debugging選項(xiàng)才能選擇,它的選項(xiàng)名稱為Debug Filesystem。為了在用戶態(tài)使用debugfs,用戶必須mount它,下面是在作者系統(tǒng)上的使用輸出:
$ mkdir -p /debugfs $ mount -t debugfs debugfs /debugfs $ insmod ./debugfs_exam.ko $ ls /debugfs debugfs-exam $ ls /debugfs/debugfs-exam u8_var u16_var u32_var bool_var $ cd /debugfs/debugfs-exam $ cat u8_var 0 $ echo 200 > u8_var $ cat u8_var 200 $ cat bool_var N $ echo 1 > bool_var $ cat bool_var Y 四、relayfsrelayfs是一個(gè)快速的轉(zhuǎn)發(fā)(relay)數(shù)據(jù)的文件系統(tǒng),它以其功能而得名。它為那些需要從內(nèi)核空間轉(zhuǎn)發(fā)大量數(shù)據(jù)到用戶空間的工具和應(yīng)用提供了快速有效的轉(zhuǎn)發(fā)機(jī)制。 Channel是relayfs文件系統(tǒng)定義的一個(gè)主要概念,每一個(gè)channel由一組內(nèi)核緩存組成,每一個(gè)CPU有一個(gè)對(duì)應(yīng)于該channel的內(nèi)核緩存,每一個(gè)內(nèi)核緩存用一個(gè)在relayfs文件系統(tǒng)中的文件文件表示,內(nèi)核使用relayfs提供的寫函數(shù)把需要轉(zhuǎn)發(fā)給用戶空間的數(shù)據(jù)快速地寫入當(dāng)前CPU上的channel內(nèi)核緩存,用戶空間應(yīng)用通過標(biāo)準(zhǔn)的文件I/O函數(shù)在對(duì)應(yīng)的channel文件中可以快速地取得這些被轉(zhuǎn)發(fā)出的數(shù)據(jù)mmap來。寫入到channel中的數(shù)據(jù)的格式完全取決于內(nèi)核中創(chuàng)建channel的模塊或子系統(tǒng)。 relayfs的用戶空間API: relayfs實(shí)現(xiàn)了四個(gè)標(biāo)準(zhǔn)的文件I/O函數(shù),open、mmap、poll和close
注意:用戶態(tài)應(yīng)用在使用上述API時(shí)必須保證已經(jīng)掛載了relayfs文件系統(tǒng),但內(nèi)核在創(chuàng)建和使用channel時(shí)不需要relayfs已經(jīng)掛載。下面命令將把relayfs文件系統(tǒng)掛載到/mnt/relay。
mount -t relayfs relayfs /mnt/relay relayfs內(nèi)核API: relayfs提供給內(nèi)核的API包括四類:channel管理、寫函數(shù)、回調(diào)函數(shù)和輔助函數(shù)。 Channel管理函數(shù)包括:
寫函數(shù)包括:
回調(diào)函數(shù)包括:
輔助函數(shù)包括:
前面已經(jīng)講過,每一個(gè)channel由一組channel緩存組成,每個(gè)CPU對(duì)應(yīng)一個(gè)該channel的緩存,每一個(gè)緩存又由一個(gè)或多個(gè)子緩存組成,每一個(gè)緩存是子緩存組成的一個(gè)環(huán)型緩存。 函數(shù)relay_open用于創(chuàng)建一個(gè)channel并分配對(duì)應(yīng)于每一個(gè)CPU的緩存,用戶空間應(yīng)用通過在relayfs文件系統(tǒng)中對(duì)應(yīng)的文件可以訪問channel緩存,參數(shù)base_filename用于指定channel的文件名,relay_open函數(shù)將在relayfs文件系統(tǒng)中創(chuàng)建base_filename0..base_filenameN-1,即每一個(gè)CPU對(duì)應(yīng)一個(gè)channel文件,其中N為CPU數(shù),缺省情況下,這些文件將建立在relayfs文件系統(tǒng)的根目錄下,但如果參數(shù)parent非空,該函數(shù)將把channel文件創(chuàng)建于parent目錄下,parent目錄使用函數(shù)relay_create_dir創(chuàng)建,函數(shù)relay_remove_dir用于刪除由函數(shù)relay_create_dir創(chuàng)建的目錄,誰創(chuàng)建的目錄,誰就負(fù)責(zé)在不用時(shí)負(fù)責(zé)刪除。參數(shù)subbuf_size用于指定channel緩存中每一個(gè)子緩存的大小,參數(shù)n_subbufs用于指定channel緩存包含的子緩存數(shù),因此實(shí)際的channel緩存大小為(subbuf_size x n_subbufs),參數(shù)overwrite用于指定該channel的操作模式,relayfs提供了兩種寫模式,一種是覆蓋式寫,另一種是非覆蓋式寫。使用哪一種模式完全取決于函數(shù)subbuf_start的實(shí)現(xiàn),覆蓋寫將在緩存已滿的情況下無條件地繼續(xù)從緩存的開始寫數(shù)據(jù),而不管這些數(shù)據(jù)是否已經(jīng)被用戶應(yīng)用讀取,因此寫操作決不失敗。在非覆蓋寫模式下,如果緩存滿了,寫將失敗,但內(nèi)核將在用戶空間應(yīng)用讀取緩存數(shù)據(jù)時(shí)通過函數(shù)relay_subbufs_consumed()通知relayfs。如果用戶空間應(yīng)用沒來得及消耗緩存中的數(shù)據(jù)或緩存已滿,兩種模式都將導(dǎo)致數(shù)據(jù)丟失,唯一的區(qū)別是,前者丟失數(shù)據(jù)在緩存開頭,而后者丟失數(shù)據(jù)在緩存末尾。一旦內(nèi)核再次調(diào)用函數(shù)relay_subbufs_consumed(),已滿的緩存將不再滿,因而可以繼續(xù)寫該緩存。當(dāng)緩存滿了以后,relayfs將調(diào)用回調(diào)函數(shù)buf_full()來通知內(nèi)核模塊或子系統(tǒng)。當(dāng)新的數(shù)據(jù)太大無法寫入當(dāng)前子緩存剩余的空間時(shí),relayfs將調(diào)用回調(diào)函數(shù)subbuf_start()來通知內(nèi)核模塊或子系統(tǒng)將需要使用新的子緩存。內(nèi)核模塊需要在該回調(diào)函數(shù)中實(shí)現(xiàn)下述功能: 初始化新的子緩存; 如果1正確,完成當(dāng)前子緩存; 如果2正確,返回是否正確完成子緩存切換; 在非覆蓋寫模式下,回調(diào)函數(shù)subbuf_start()應(yīng)該如下實(shí)現(xiàn):
static int subbuf_start(struct rchan_buf *buf,
void *subbuf,
void *prev_subbuf,
unsigned int prev_padding)
{
if (prev_subbuf)
*((unsigned *)prev_subbuf) = prev_padding;
if (relay_buf_full(buf))
return 0;
subbuf_start_reserve(buf, sizeof(unsigned int));
return 1;
}如果當(dāng)前緩存滿,即所有的子緩存都沒讀取,該函數(shù)返回0,指示子緩存切換沒有成功。當(dāng)子緩存通過函數(shù)relay_subbufs_consumed()被讀取后,讀取者將負(fù)責(zé)通知relayfs,函數(shù)relay_buf_full()在已經(jīng)有讀者讀取子緩存數(shù)據(jù)后返回0,在這種情況下,子緩存切換成功進(jìn)行。 在覆蓋寫模式下, subbuf_start()的實(shí)現(xiàn)與非覆蓋模式類似:
static int subbuf_start(struct rchan_buf *buf,
void *subbuf,
void *prev_subbuf,
unsigned int prev_padding)
{
if (prev_subbuf)
*((unsigned *)prev_subbuf) = prev_padding;
subbuf_start_reserve(buf, sizeof(unsigned int));
return 1;
}只是不做relay_buf_full()檢查,因?yàn)榇四J较拢彺媸黔h(huán)行的,可以無條件地寫。因此在此模式下,子緩存切換必定成功,函數(shù)relay_subbufs_consumed() 也無須調(diào)用。如果channel寫者沒有定義subbuf_start(),缺省的實(shí)現(xiàn)將被使用。 可以通過在回調(diào)函數(shù)subbuf_start()中調(diào)用輔助函數(shù)subbuf_start_reserve()在子緩存中預(yù)留頭空間,預(yù)留空間可以保存任何需要的信息,如上面例子中,預(yù)留空間用于保存子緩存填充字節(jié)數(shù),在subbuf_start()實(shí)現(xiàn)中,前一個(gè)子緩存的填充值被設(shè)置。前一個(gè)子緩存的填充值和指向前一個(gè)子緩存的指針一道作為subbuf_start()的參數(shù)傳遞給subbuf_start(),只有在子緩存完成后,才能知道填充值。subbuf_start()也被在channel創(chuàng)建時(shí)分配每一個(gè)channel緩存的第一個(gè)子緩存時(shí)調(diào)用,以便預(yù)留頭空間,但在這種情況下,前一個(gè)子緩存指針為NULL。 內(nèi)核模塊使用函數(shù)relay_write()或__relay_write()往channel緩存中寫需要轉(zhuǎn)發(fā)的數(shù)據(jù),它們的區(qū)別是前者失效了本地中斷,而后者只搶占失效,因此前者可以在任何內(nèi)核上下文安全使用,而后者應(yīng)當(dāng)在沒有任何中斷上下文將寫channel緩存的情況下使用。這兩個(gè)函數(shù)沒有返回值,因此用戶不能直接確定寫操作是否失敗,在緩存滿且寫模式為非覆蓋模式時(shí),relayfs將通過回調(diào)函數(shù)buf_full來通知內(nèi)核模塊。 函數(shù)relay_reserve()用于在channel緩存中預(yù)留一段空間以便以后寫入,在那些沒有臨時(shí)緩存而直接寫入channel緩存的內(nèi)核模塊可能需要該函數(shù),使用該函數(shù)的內(nèi)核模塊在實(shí)際寫這段預(yù)留的空間時(shí)可以通過調(diào)用relay_commit()來通知relayfs。當(dāng)所有預(yù)留的空間全部寫完并通過relay_commit通知relayfs后,relayfs將調(diào)用回調(diào)函數(shù)deliver()通知內(nèi)核模塊一個(gè)完整的子緩存已經(jīng)填滿。由于預(yù)留空間的操作并不在寫channel的內(nèi)核模塊完全控制之下,因此relay_reserve()不能很好地保護(hù)緩存,因此當(dāng)內(nèi)核模塊調(diào)用relay_reserve()時(shí)必須采取恰當(dāng)?shù)耐綑C(jī)制。 當(dāng)內(nèi)核模塊結(jié)束對(duì)channel的使用后需要調(diào)用relay_close() 來關(guān)閉channel,如果沒有任何用戶在引用該channel,它將和對(duì)應(yīng)的緩存全部被釋放。 函數(shù)relay_flush()強(qiáng)制在所有的channel緩存上做一個(gè)子緩存切換,它在channel被關(guān)閉前使用來終止和處理最后的子緩存。 函數(shù)relay_reset()用于將一個(gè)channel恢復(fù)到初始狀態(tài),因而不必釋放現(xiàn)存的內(nèi)存映射并重新分配新的channel緩存就可以使用channel,但是該調(diào)用只有在該channel沒有任何用戶在寫的情況下才可以安全使用。 回調(diào)函數(shù)buf_mapped() 在channel緩存被映射到用戶空間時(shí)被調(diào)用。 回調(diào)函數(shù)buf_unmapped()在釋放該映射時(shí)被調(diào)用。內(nèi)核模塊可以通過它們觸發(fā)一些內(nèi)核操作,如開始或結(jié)束channel寫操作。 在源代碼包中給出了一個(gè)使用relayfs的示例程序relayfs_exam.c,它只包含一個(gè)內(nèi)核模塊,對(duì)于復(fù)雜的使用,需要應(yīng)用程序配合。該模塊實(shí)現(xiàn)了類似于文章中seq_file示例實(shí)現(xiàn)的功能。 當(dāng)然為了使用relayfs,用戶必須讓內(nèi)核支持relayfs,并且要mount它,下面是作者系統(tǒng)上的使用該模塊的輸出信息:
$ mkdir -p /relayfs $ insmod ./relayfs-exam.ko $ mount -t relayfs relayfs /relayfs $ cat /relayfs/example0 … $ relayfs是一種比較復(fù)雜的內(nèi)核態(tài)與用戶態(tài)的數(shù)據(jù)交換方式,本例子程序只提供了一個(gè)較簡(jiǎn)單的使用方式,對(duì)于復(fù)雜的使用,請(qǐng)參考relayfs用例頁(yè)面http://relayfs./examples.html。 小結(jié)本文是該系列文章最后一篇,它詳細(xì)地講解了其余四種用戶空間與內(nèi)核空間的數(shù)據(jù)交換方式,并通過實(shí)際例子程序向讀者講解了如何在內(nèi)核開發(fā)中使用這些技術(shù),其中seq_file是單向的,即只能向內(nèi)核傳遞,而不能從內(nèi)核獲取,而另外三種方式均可以進(jìn)行雙向數(shù)據(jù)交換,即既可以從用戶應(yīng)用傳遞給內(nèi)核,又可以從內(nèi)核傳遞給應(yīng)用態(tài)應(yīng)用。procfs一般用于向用戶出口少量的數(shù)據(jù)信息,或用戶通過它設(shè)置內(nèi)核變量從而控制內(nèi)核行為。seq_file實(shí)際上依賴于procfs,因此為了使用seq_file,必須使內(nèi)核支持procfs。debugfs用于內(nèi)核開發(fā)者調(diào)試使用,它比其他集中方式都方便,但是僅用于簡(jiǎn)單類型的變量處理。relayfs是一種非常復(fù)雜的數(shù)據(jù)交換方式,要想準(zhǔn)確使用并不容易,但是如果使用得當(dāng),它遠(yuǎn)比procfs和seq_file功能強(qiáng)大。 參考資料
|
|
|