亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

在 Linux 下用戶空間與內核空間數據交換的方式

系統 2144 0

發布日期: 2006 年 2 月 16 日

燚 楊 (yang.y.yi@gmail.com), 計算機科學碩士

簡介: 本系列文章包括兩篇,它們文詳細地地介紹了Linux系統下用戶空間與內核空間數據交換的九種方式,包括內核啟動參數、模塊參數與sysfs、sysctl、系統調用、netlink、procfs、seq_file、debugfs和relayfs,并給出具體的例子幫助讀者掌握這些技術的使用。
本文是該系列文章的第二篇,它介紹了procfs、seq_file、debugfs和relayfs,并結合給出的例子程序詳細地說明了它們如何使用。

一、procfs

procfs是比較老的一種用戶態與內核態的數據交換方式,內核的很多數據都是通過這種方式出口給用戶的,內核的很多參數也是通過這種方式來讓用戶方便設置的。除了sysctl出口到/proc下的參數,procfs提供的大部分內核參數是只讀的。實際上,很多應用嚴重地依賴于procfs,因此它幾乎是必不可少的組件。前面部分的幾個例子實際上已經使用它來出口內核數據,但是并沒有講解如何使用,本節將講解如何使用procfs。

Procfs提供了如下API:
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
????????????????????????????????????????? struct proc_dir_entry *parent)

該函數用于創建一個正常的proc條目,參數name給出要建立的proc條目的名稱,參數mode給出了建立的該proc條目的訪問權限,參數parent指定建立的proc條目所在的目錄。如果要在/proc下建立proc條目,parent應當為NULL。否則它應當為proc_mkdir返回的struct proc_dir_entry結構的指針。
extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent)

該函數用于刪除上面函數創建的proc條目,參數name給出要刪除的proc條目的名稱,參數parent指定建立的proc條目所在的目錄。
struct proc_dir_entry *proc_mkdir(const char * name, struct proc_dir_entry *parent)

該函數用于創建一個proc目錄,參數name指定要創建的proc目錄的名稱,參數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)

該函數用于建立一個proc條目的符號鏈接,參數name給出要建立的符號鏈接proc條目的名稱,參數parent指定符號連接所在的目錄,參數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)

該函數用于建立一個規則的只讀proc條目,參數name給出要建立的proc條目的名稱,參數mode給出了建立的該proc條目的訪問權限,參數base指定建立的proc條目所在的目錄,參數read_proc給出讀去該proc條目的操作函數,參數data為該proc條目的專用數據,它將保存在該proc條目對應的struct file結構的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)

該函數用于創建一個info型的proc條目,參數name給出要建立的proc條目的名稱,參數mode給出了建立的該proc條目的訪問權限,參數base指定建立的proc條目所在的目錄,參數get_info指定該proc條目的get_info操作函數。實際上get_info等同于read_proc,如果proc條目沒有定義個read_proc,對該proc條目的read操作將使用get_info取代,因此它在功能上非常類似于函數create_proc_read_entry。
struct proc_dir_entry *proc_net_create(const char *name,
??????? mode_t mode, get_info_t *get_info)

該函數用于在/proc/net目錄下創建一個proc條目,參數name給出要建立的proc條目的名稱,參數mode給出了建立的該proc條目的訪問權限,參數get_info指定該proc條目的get_info操作函數。
struct proc_dir_entry *proc_net_fops_create(const char *name,
??????? mode_t mode, struct file_operations *fops)

該函數也用于在/proc/net下創建proc條目,但是它也同時指定了對該proc條目的文件操作函數。
void proc_net_remove(const char *name)

該函數用于刪除前面兩個函數在/proc/net目錄下創建的proc條目。參數name指定要刪除的proc名稱。

除了這些函數,值得一提的是結構struct proc_dir_entry,為了創建一了可寫的proc條目并指定該proc條目的寫操作函數,必須設置上面的這些創建proc條目的函數返回的指針指向的struct proc_dir_entry結構的write_proc字段,并指定該proc條目的訪問權限有寫權限。

為了使用這些接口函數以及結構struct proc_dir_entry,用戶必須在模塊中包含頭文件linux/proc_fs.h。

在源代碼包中給出了procfs示例程序procfs_exam.c,它定義了三個proc文件條目和一個proc目錄條目,讀者在插入該模塊后應當看到如下結構:
$ ls /proc/myproctest
aint??????? astring??????? bigprocfile
$

讀者可以通過cat和echo等文件操作函數來查看和設置這些proc文件。特別需要指出,bigprocfile是一個大文件(超過一個內存頁),對于這種大文件,procfs有一些限制,因為它提供的緩存,只有一個頁,因此必須特別小心,并對超過頁的部分做特別的考慮,處理起來比較復雜并且很容易出錯,所有procfs并不適合于大數據量的輸入輸出,后面一節seq_file就是因為這一缺陷而設計的,當然seq_file依賴于procfs的一些基礎功能。

回頁首

二、seq_file

一般地,內核通過在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也定義了一些輔助函數用于格式化輸出:
int seq_putc(struct seq_file *m, char c);

函數seq_putc用于把一個字符輸出到seq_file文件。
int seq_puts(struct seq_file *m, const char *s);

函數seq_puts則用于把一個字符串輸出到seq_file文件。
int seq_escape(struct seq_file *, const char *, const char *);

函數seq_escape類似于seq_puts,只是,它將把第一個字符串參數中出現的包含在第二個字符串參數中的字符按照八進制形式輸出,也即對這些字符進行轉義處理。
int seq_printf(struct seq_file *, const char *, ...)
??????? __attribute__ ((format (printf,2,3)));

函數seq_printf是最常用的輸出函數,它用于把給定參數按照給定的格式輸出到seq_file文件。
int seq_path(struct seq_file *, struct vfsmount *, struct dentry *, char *);

函數seq_path則用于輸出文件名,字符串參數提供需要轉義的文件名字符,它主要供文件系統使用。

在定義了結構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"可以查看系統的所有進程。

回頁首

三、debugfs

內核開發者經常需要向用戶空間應用輸出一些調試信息,在穩定的系統中可能根本不需要這些調試信息,但是在開發過程中,為了搞清楚內核的行為,調試信息非常必要,printk可能是用的最多的,但它并不是最好的,調試信息只是在開發中用于調試,而printk將一直輸出,因此開發完畢后需要清除不必要的printk語句,另外如果開發者希望用戶空間應用能夠改變內核行為時,printk就無法實現。因此,需要一種新的機制,那只有在需要的時候使用,它在需要時通過在一個虛擬文件系統中創建一個或多個文件來向用戶空間應用提供調試信息。

有幾種方式可以實現上述要求:

使用procfs,在/proc創建文件輸出調試信息,但是procfs對于大于一個內存頁(對于x86是4K)的輸出比較麻煩,而且速度慢,有時回出現一些意想不到的問題。

使用sysfs(2.6內核引入的新的虛擬文件系統),在很多情況下,調試信息可以存放在那里,但是sysfs主要用于系統管理,它希望每一個文件對應內核的一個變量,如果使用它輸出復雜的數據結構或調試信息是非常困難的。

使用libfs創建一個新的文件系統,該方法極其靈活,開發者可以為新文件系統設置一些規則,使用libfs使得創建新文件系統更加簡單,但是仍然超出了一個開發者的想象。

為了使得開發者更加容易使用這樣的機制,Greg Kroah-Hartman開發了debugfs(在2.6.11中第一次引入),它是一個虛擬文件系統,專門用于輸出調試信息,該文件系統非常小,很容易使用,可以在配置內核時選擇是否構件到內核中,在不選擇它的情況下,使用它提供的API的內核部分不需要做任何改動。

使用debugfs的開發者首先需要在文件系統中創建一個目錄,下面函數用于在debugfs文件系統下創建一個目錄:
??????? struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);

參數name是要創建的目錄名,參數parent指定創建目錄的父目錄的dentry,如果為NULL,目錄將創建在debugfs文件系統的根目錄下。如果返回為-ENODEV,表示內核沒有把debugfs編譯到其中,如果返回為NULL,表示其他類型的創建失敗,如果創建目錄成功,返回指向該目錄對應的dentry條目的指針。

下面函數用于在debugfs文件系統中創建一個文件:
??????? struct dentry *debugfs_create_file(const char *name, mode_t mode,
?????????????????????????????????????? struct dentry *parent, void *data,
?????????????????????????????????????? struct file_operations *fops);

參數name指定要創建的文件名,參數mode指定該文件的訪問許可,參數parent指向該文件所在目錄,參數data為該文件特定的一些數據,參數fops為實現在該文件上進行文件操作的fiel_operations結構指針,在很多情況下,由seq_file(前面章節已經講過)提供的文件操作實現就足夠了,因此使用debugfs很容易,當然,在一些情況下,開發者可能僅需要使用用戶應用可以控制的變量來調試,debugfs也提供了4個這樣的API方便開發者使用:
??? 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);

參數name和mode指定文件名和訪問許可,參數value為需要讓用戶應用控制的內核變量指針。

當內核模塊卸載時,Debugfs并不會自動清除該模塊創建的目錄或文件,因此對于創建的每一個文件或目錄,開發者必須調用下面函數清除:
??????? void debugfs_remove(struct dentry *dentry);

參數dentry為上面創建文件和目錄的函數返回的dentry指針。

在源代碼包中給出了一個使用debufs的示例模塊debugfs_exam.c,為了保證該模塊正確運行,必須讓內核支持debugfs,debugfs是一個調試功能,因此它位于主菜單Kernel hacking,并且必須選擇Kernel debugging選項才能選擇,它的選項名稱為Debug Filesystem。為了在用戶態使用debugfs,用戶必須mount它,下面是在作者系統上的使用輸出:
$ 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

回頁首

四、relayfs

relayfs是一個快速的轉發(relay)數據的文件系統,它以其功能而得名。它為那些需要從內核空間轉發大量數據到用戶空間的工具和應用提供了快速有效的轉發機制。

Channel是relayfs文件系統定義的一個主要概念,每一個channel由一組內核緩存組成,每一個CPU有一個對應于該channel的內核緩存,每一個內核緩存用一個在relayfs文件系統中的文件文件表示,內核使用relayfs提供的寫函數把需要轉發給用戶空間的數據快速地寫入當前CPU上的channel內核緩存,用戶空間應用通過標準的文件I/O函數在對應的channel文件中可以快速地取得這些被轉發出的數據mmap來。寫入到channel中的數據的格式完全取決于內核中創建channel的模塊或子系統。

relayfs的用戶空間API:

relayfs實現了四個標準的文件I/O函數,open、mmap、poll和close
open(),打開一個channel在某一個CPU上的緩存對應的文件。
mmap(),把打開的channel緩存映射到調用者進程的內存空間。
read(),讀取channel緩存,隨后的讀操作將看不到被該函數消耗的字節,如果channel的操作模式為非覆蓋寫,那么用戶空間應用在有內核模塊寫時仍可以讀取,但是如果channel的操作模式為覆蓋式,那么在讀操作期間如果有內核模塊進行寫,結果將無法預知,因此對于覆蓋式寫的channel,用戶應當在確認在channel的寫完全結束后再進行讀。
poll(),用于通知用戶空間應用轉發數據跨越了子緩存的邊界,支持的輪詢標志有POLLIN、POLLRDNORM和POLLERR。
close(),關閉open函數返回的文件描述符,如果沒有進程或內核模塊打開該channel緩存,close函數將釋放該channel緩存。

注意:用戶態應用在使用上述API時必須保證已經掛載了relayfs文件系統,但內核在創建和使用channel時不需要relayfs已經掛載。下面命令將把relayfs文件系統掛載到/mnt/relay。
??????? mount -t relayfs relayfs /mnt/relay

relayfs內核API:

relayfs提供給內核的API包括四類:channel管理、寫函數、回調函數和輔助函數。

Channel管理函數包括:
relay_open(base_filename, parent, subbuf_size, n_subbufs, overwrite, callbacks)
relay_close(chan)
relay_flush(chan)
relay_reset(chan)
relayfs_create_dir(name, parent)
relayfs_remove_dir(dentry)
relay_commit(buf, reserved, count)
relay_subbufs_consumed(chan, cpu, subbufs_consumed)

寫函數包括:
relay_write(chan, data, length)
__relay_write(chan, data, length)
relay_reserve(chan, length)

回調函數包括:
subbuf_start(buf, subbuf, prev_subbuf_idx, prev_subbuf)
buf_mapped(buf, filp)
buf_unmapped(buf, filp)

輔助函數包括:
relay_buf_full(buf)
subbuf_start_reserve(buf, length)

前面已經講過,每一個channel由一組channel緩存組成,每個CPU對應一個該channel的緩存,每一個緩存又由一個或多個子緩存組成,每一個緩存是子緩存組成的一個環型緩存。

函數relay_open用于創建一個channel并分配對應于每一個CPU的緩存,用戶空間應用通過在relayfs文件系統中對應的文件可以訪問channel緩存,參數base_filename用于指定channel的文件名,relay_open函數將在relayfs文件系統中創建base_filename0..base_filenameN-1,即每一個CPU對應一個channel文件,其中N為CPU數,缺省情況下,這些文件將建立在relayfs文件系統的根目錄下,但如果參數parent非空,該函數將把channel文件創建于parent目錄下,parent目錄使用函數relay_create_dir創建,函數relay_remove_dir用于刪除由函數relay_create_dir創建的目錄,誰創建的目錄,誰就負責在不用時負責刪除。參數subbuf_size用于指定channel緩存中每一個子緩存的大小,參數n_subbufs用于指定channel緩存包含的子緩存數,因此實際的channel緩存大小為(subbuf_size x n_subbufs),參數overwrite用于指定該channel的操作模式,relayfs提供了兩種寫模式,一種是覆蓋式寫,另一種是非覆蓋式寫。使用哪一種模式完全取決于函數subbuf_start的實現,覆蓋寫將在緩存已滿的情況下無條件地繼續從緩存的開始寫數據,而不管這些數據是否已經被用戶應用讀取,因此寫操作決不失敗。在非覆蓋寫模式下,如果緩存滿了,寫將失敗,但內核將在用戶空間應用讀取緩存數據時通過函數relay_subbufs_consumed()通知relayfs。如果用戶空間應用沒來得及消耗緩存中的數據或緩存已滿,兩種模式都將導致數據丟失,唯一的區別是,前者丟失數據在緩存開頭,而后者丟失數據在緩存末尾。一旦內核再次調用函數relay_subbufs_consumed(),已滿的緩存將不再滿,因而可以繼續寫該緩存。當緩存滿了以后,relayfs將調用回調函數buf_full()來通知內核模塊或子系統。當新的數據太大無法寫入當前子緩存剩余的空間時,relayfs將調用回調函數subbuf_start()來通知內核模塊或子系統將需要使用新的子緩存。內核模塊需要在該回調函數中實現下述功能:

初始化新的子緩存;

如果1正確,完成當前子緩存;

如果2正確,返回是否正確完成子緩存切換;

在非覆蓋寫模式下,回調函數subbuf_start()應該如下實現:
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;
}

如果當前緩存滿,即所有的子緩存都沒讀取,該函數返回0,指示子緩存切換沒有成功。當子緩存通過函數relay_subbufs_consumed()被讀取后,讀取者將負責通知relayfs,函數relay_buf_full()在已經有讀者讀取子緩存數據后返回0,在這種情況下,子緩存切換成功進行。

在覆蓋寫模式下, subbuf_start()的實現與非覆蓋模式類似:
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()檢查,因為此模式下,緩存是環行的,可以無條件地寫。因此在此模式下,子緩存切換必定成功,函數relay_subbufs_consumed() 也無須調用。如果channel寫者沒有定義subbuf_start(),缺省的實現將被使用。 可以通過在回調函數subbuf_start()中調用輔助函數subbuf_start_reserve()在子緩存中預留頭空間,預留空間可以保存任何需要的信息,如上面例子中,預留空間用于保存子緩存填充字節數,在subbuf_start()實現中,前一個子緩存的填充值被設置。前一個子緩存的填充值和指向前一個子緩存的指針一道作為subbuf_start()的參數傳遞給subbuf_start(),只有在子緩存完成后,才能知道填充值。subbuf_start()也被在channel創建時分配每一個channel緩存的第一個子緩存時調用,以便預留頭空間,但在這種情況下,前一個子緩存指針為NULL。

內核模塊使用函數relay_write()或__relay_write()往channel緩存中寫需要轉發的數據,它們的區別是前者失效了本地中斷,而后者只搶占失效,因此前者可以在任何內核上下文安全使用,而后者應當在沒有任何中斷上下文將寫channel緩存的情況下使用。這兩個函數沒有返回值,因此用戶不能直接確定寫操作是否失敗,在緩存滿且寫模式為非覆蓋模式時,relayfs將通過回調函數buf_full來通知內核模塊。

函數relay_reserve()用于在channel緩存中預留一段空間以便以后寫入,在那些沒有臨時緩存而直接寫入channel緩存的內核模塊可能需要該函數,使用該函數的內核模塊在實際寫這段預留的空間時可以通過調用relay_commit()來通知relayfs。當所有預留的空間全部寫完并通過relay_commit通知relayfs后,relayfs將調用回調函數deliver()通知內核模塊一個完整的子緩存已經填滿。由于預留空間的操作并不在寫channel的內核模塊完全控制之下,因此relay_reserve()不能很好地保護緩存,因此當內核模塊調用relay_reserve()時必須采取恰當的同步機制。

當內核模塊結束對channel的使用后需要調用relay_close() 來關閉channel,如果沒有任何用戶在引用該channel,它將和對應的緩存全部被釋放。

函數relay_flush()強制在所有的channel緩存上做一個子緩存切換,它在channel被關閉前使用來終止和處理最后的子緩存。

函數relay_reset()用于將一個channel恢復到初始狀態,因而不必釋放現存的內存映射并重新分配新的channel緩存就可以使用channel,但是該調用只有在該channel沒有任何用戶在寫的情況下才可以安全使用。

回調函數buf_mapped() 在channel緩存被映射到用戶空間時被調用。

回調函數buf_unmapped()在釋放該映射時被調用。內核模塊可以通過它們觸發一些內核操作,如開始或結束channel寫操作。

在源代碼包中給出了一個使用relayfs的示例程序relayfs_exam.c,它只包含一個內核模塊,對于復雜的使用,需要應用程序配合。該模塊實現了類似于文章中seq_file示例實現的功能。

當然為了使用relayfs,用戶必須讓內核支持relayfs,并且要mount它,下面是作者系統上的使用該模塊的輸出信息:
$ mkdir -p /relayfs
$ insmod ./relayfs-exam.ko
$ mount -t relayfs relayfs /relayfs
$ cat /relayfs/example0

$

relayfs是一種比較復雜的內核態與用戶態的數據交換方式,本例子程序只提供了一個較簡單的使用方式,對于復雜的使用,請參考relayfs用例頁面http://relayfs.sourceforge.net/examples.html。

回頁首

小結

本文是該系列文章最后一篇,它詳細地講解了其余四種用戶空間與內核空間的數據交換方式,并通過實際例子程序向讀者講解了如何在內核開發中使用這些技術,其中seq_file是單向的,即只能向內核傳遞,而不能從內核獲取,而另外三種方式均可以進行雙向數據交換,即既可以從用戶應用傳遞給內核,又可以從內核傳遞給應用態應用。procfs一般用于向用戶出口少量的數據信息,或用戶通過它設置內核變量從而控制內核行為。seq_file實際上依賴于procfs,因此為了使用seq_file,必須使內核支持procfs。debugfs用于內核開發者調試使用,它比其他集中方式都方便,但是僅用于簡單類型的變量處理。relayfs是一種非常復雜的數據交換方式,要想準確使用并不容易,但是如果使用得當,它遠比procfs和seq_file功能強大。

參考資料
Linux 2.6.13的內核源碼, http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.13.tar.bz2。
Linux 2.6.14的內核源碼, http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.14.tar.bz2。
內核文檔,Documentation/filesystems/proc.txt。
LWN, Driver porting: The seq_file interface, http://lwn.net/Articles/22355/。
LWN, debugfs, http://lwn.net/Articles/115405/。
Linux kernel procfs guide, http://kernelnewbies.org/documents/kdoc/procfs-guide/lkprocfsguide.html。
內核文檔,Documentation/filesystems/relayfs.txt。
Relayfs project homepage, http://relayfs.sourceforge.net/。
relayfs usage examples, http://relayfs.sourceforge.net/examples.html。
relayfs source, http://prdownloads.sourceforge.net/relayfs/old-relayfs-051205-kernel-2.6.11.patch.bz2?download。
relayfs example applications, http://prdownloads.sourceforge.net/relayfs/relay-apps-0.91.tar.gz?download。

關于作者

楊燚,計算機科學碩士,畢業于中科院計算技術研究所,有4年的Linux內核編程經驗,目前從事嵌入式實時Linux的開發與性能測試。您可以通過 yang.y.yi@gmail.com 與作者聯系。

在 Linux 下用戶空間與內核空間數據交換的方式,第 2 部分: procfs、seq_file、debugfs和relayfs


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 亚洲香蕉在线视频 | 97影院理论片手机在线观看 | 2021最新国产成人精品免费 | 色综合天天综合给合国产 | 久久综合九色综合97伊人麻豆 | 亚洲国产99| 欧美高清视频www夜色资源 | 最新国产精品自拍 | 男人天堂a在线 | 日本高清一级做a爱过程免费视频 | 99在线免费观看视频 | 狠狠久久亚洲欧美专区 | 免费精品久久久视频 | 成人性生交大片免费看午夜a | 国产在线视频色综合 | 久久久免费观成人影院 | 免费观看a级完整视频 | 四虎影永久在线观看网址 | 99久久免费国产精品m9 | 一区二区视频在线观看免费的 | 免费中文字幕在线 | 在线手机福利免费福利院 | 日韩高清一区二区三区不卡 | 日本在线观看不卡免费视频 | 一区二区三区欧美日韩国产 | 午夜探花 | 四虎视屏 | 在线观看日本免费视频大片一区 | 奇米777视频二区中文字幕 | 日韩在线观看一区二区三区 | 欧美精品日本一级特黄 | 国产福利91精品一区二区三区 | 激性欧美激情在线播放16页 | 天天插天天射天天干 | 一级毛片成人免费看免费不卡 | 尹人香蕉 | 成人欧美一区二区三区 | 99国产精品一区二区 | 日韩a一级欧美一级在线播放 | 5060午夜一级毛片 | 国产高清a毛片在线看 |