page 參數(shù)是這些數(shù)據(jù)寫(xiě)入到的位置,其中 count 定義了可以寫(xiě)入的最大字符數(shù)。在返回多頁(yè)數(shù)據(jù)(通常一頁(yè)是 4KB)時(shí),我們需要使用
start 和 off 參數(shù)。當(dāng)所有數(shù)據(jù)全部寫(xiě)入之后,就需要設(shè)置 eof(文件結(jié)束參數(shù))。與 write 類似,data
表示的也是私有數(shù)據(jù)。此處提供的 page 緩沖區(qū)在內(nèi)核空間中。因此,我們可以直接寫(xiě)入,而不用調(diào)用 copy_to_user。
其他有用的函數(shù)
我們還可以使用 proc_mkdir、symlinks 以及 proc_symlink 在 /proc
文件系統(tǒng)中創(chuàng)建目錄。對(duì)于只需要一個(gè) read 函數(shù)的簡(jiǎn)單 /proc 項(xiàng)來(lái)說(shuō),可以使用
create_proc_read_entry,這會(huì)創(chuàng)建一個(gè) /proc 項(xiàng),并在一個(gè)調(diào)用中對(duì) read_proc
函數(shù)進(jìn)行初始化。這些函數(shù)的原型如清單 8 所示。
清單 8. 其他有用的 /proc 函數(shù)
代碼: |
/* Create a directory in the proc filesystem */
struct proc_dir_entry *proc_mkdir( const char *name,
struct proc_dir_entry *parent );
/* Create a symlink in the proc filesystem */
struct proc_dir_entry *proc_symlink( const char *name,
struct proc_dir_entry *parent,
const char *dest );
/* Create a proc_dir_entry with a read_proc_t in one call */
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 );
/* Copy buffer to user-space from kernel-space */
unsigned long copy_to_user( void __user *to,
const void *from,
unsigned long n );
/* Copy buffer to kernel-space from user-space */
unsigned long copy_from_user( void *to,
const void __user
*from,
unsigned long n );
/* Allocate a ‘virtually‘ contiguous block of memory */
void *vmalloc( unsigned long size );
/* Free a vmalloc‘d block of memory */
void vfree( void *addr );
/* Export a symbol to the kernel (make it visible to the kernel) */
EXPORT_SYMBOL( symbol );
/* Export all symbols in a file to the kernel (declare before module.h) */
EXPORT_SYMTAB
|
通過(guò) /proc 文件系統(tǒng)實(shí)現(xiàn)財(cái)富分發(fā)
下面是一個(gè)可以支持讀寫(xiě)的 LKM。這個(gè)簡(jiǎn)單的程序提供了一個(gè)財(cái)富甜點(diǎn)分發(fā)。在加載這個(gè)模塊之后,用戶就可以使用 echo 命令向其中導(dǎo)入文本財(cái)富,然后再使用 cat 命令逐一讀出。
清單 9 給出了基本的模塊函數(shù)和變量。init 函數(shù)(init_fortune_module)負(fù)責(zé)使用 vmalloc
來(lái)為這個(gè)點(diǎn)心罐分配空間,然后使用 memset 將其全部清零。使用所分配并已經(jīng)清空的 cookie_pot 內(nèi)存,我們?cè)?/proc
中創(chuàng)建了一個(gè) proc_dir_entry 項(xiàng),并將其稱為 fortune。當(dāng) proc_entry 成功創(chuàng)建之后,對(duì)自己的本地變量和
proc_entry 結(jié)構(gòu)進(jìn)行了初始化。我們加載了 /proc read 和 write 函數(shù)(如清單 9 和清單 10
所示),并確定這個(gè)模塊的所有者。cleanup 函數(shù)簡(jiǎn)單地從 /proc 文件系統(tǒng)中刪除這一項(xiàng),然后釋放 cookie_pot 所占據(jù)的內(nèi)存。
cookie_pot 是一個(gè)固定大小(4KB)的頁(yè),它使用兩個(gè)索引進(jìn)行管理。第一個(gè)是 cookie_index,標(biāo)識(shí)了要將下一個(gè)
cookie 寫(xiě)到哪里去。變量 next_fortune 標(biāo)識(shí)了下一個(gè) cookie 應(yīng)該從哪里讀取以便進(jìn)行輸出。在所有的 fortune
項(xiàng)都讀取之后,我們簡(jiǎn)單地回到了 next_fortune。
清單 9. 模塊的 init/cleanup 和變量
代碼: |
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Fortune Cookie Kernel Module");
MODULE_AUTHOR("M. Tim Jones");
#define MAX_COOKIE_LENGTH PAGE_SIZE
static struct proc_dir_entry *proc_entry;
static char *cookie_pot; // Space for fortune strings
static int cookie_index; // Index to write next fortune
static int next_fortune; // Index to read next fortune
int init_fortune_module( void )
{
int ret = 0;
cookie_pot = (char *)vmalloc( MAX_COOKIE_LENGTH );
if (!cookie_pot) {
ret = -ENOMEM;
} else {
memset( cookie_pot, 0, MAX_COOKIE_LENGTH );
proc_entry = create_proc_entry( "fortune", 0644, NULL );
if (proc_entry == NULL) {
ret = -ENOMEM;
vfree(cookie_pot);
printk(KERN_INFO "fortune: Couldn‘t create proc entry\n");
} else {
cookie_index = 0;
next_fortune = 0;
proc_entry->read_proc = fortune_read;
proc_entry->write_proc = fortune_write;
proc_entry->owner = THIS_MODULE;
printk(KERN_INFO "fortune: Module loaded.\n");
}
}
return ret;
}
void cleanup_fortune_module( void )
{
remove_proc_entry("fortune", &proc_root);
vfree(cookie_pot);
printk(KERN_INFO "fortune: Module unloaded.\n");
}
module_init( init_fortune_module );
module_exit( cleanup_fortune_module ); |
向這個(gè)罐中新寫(xiě)入一個(gè) cookie 非常簡(jiǎn)單(如清單 10 所示)。使用這個(gè)寫(xiě)入 cookie
的長(zhǎng)度,我們可以檢查是否有這么多空間可用。如果沒(méi)有,就返回 -ENOSPC,它會(huì)返回給用戶空間。否則,就說(shuō)明空間存在,我們使用
copy_from_user 將用戶緩沖區(qū)中的數(shù)據(jù)直接拷貝到 cookie_pot 中。然后增大
cookie_index(基于用戶緩沖區(qū)的長(zhǎng)度)并使用 NULL 來(lái)結(jié)束這個(gè)字符串。最后,返回實(shí)際寫(xiě)入 cookie_pot
的字符的個(gè)數(shù),它會(huì)返回到用戶進(jìn)程。
清單 10. 對(duì) fortune 進(jìn)行寫(xiě)入操作所使用的函數(shù)
對(duì) fortune 進(jìn)行讀取也非常簡(jiǎn)單,如清單 11
所示。由于我們剛才寫(xiě)入數(shù)據(jù)的緩沖區(qū)(page)已經(jīng)在內(nèi)核空間中了,因此可以直接對(duì)其進(jìn)行操作,并使用 sprintf 來(lái)寫(xiě)入下一個(gè)
fortune。如果 next_fortune 索引大于 cookie_index(要寫(xiě)入的下一個(gè)位置),那么我們就將
next_fortune 返回為 0,這是第一個(gè) fortune 的索引。在將這個(gè) fortune 寫(xiě)入用戶緩沖區(qū)之后,在
next_fortune 索引上增加剛才寫(xiě)入的 fortune 的長(zhǎng)度。這樣就變成了下一個(gè)可用 fortune 的索引。這個(gè) fortune
的長(zhǎng)度會(huì)被返回并傳遞給用戶。
清單 11. 對(duì) fortune 進(jìn)行讀取操作所使用的函數(shù)
代碼: |
int fortune_read( char *page, char **start, off_t off,
int count, int *eof, void *data )
{
int len;
if (off > 0) {
*eof = 1;
return 0;
}
/* Wrap-around */
if (next_fortune >= cookie_index) next_fortune = 0;
len = sprintf(page, "%s\n", &cookie_pot[next_fortune]);
next_fortune += len;
return len;
} |
從這個(gè)簡(jiǎn)單的例子中,我們可以看出通過(guò) /proc 文件系統(tǒng)與內(nèi)核進(jìn)行通信實(shí)際上是件非常簡(jiǎn)單的事情?,F(xiàn)在讓我們來(lái)看一下這個(gè) fortune 模塊的用法(參見(jiàn)清單 12)。
清單 12. 展示 fortune cookie LKM 的用法
代碼: |
[root@plato]# insmod fortune.ko
[root@plato]# echo "Success is an individual proposition. Thomas Watson" > /proc/fortune
[root@plato]# echo "If a man does his best, what else is there? Gen. Patton" > /proc/fortune
[root@plato]# echo "Cats: All your base are belong to us. Zero Wing" > /proc/fortune
[root@plato]# cat /proc/fortune
Success is an individual proposition. Thomas Watson
[root@plato]# cat /proc/fortune
If a man does his best, what else is there? General Patton
[root@plato]# |
/proc 虛擬文件系統(tǒng)可以廣泛地用來(lái)報(bào)告內(nèi)核的信息,也可以用來(lái)進(jìn)行動(dòng)態(tài)配置。我們會(huì)發(fā)現(xiàn)它對(duì)于驅(qū)動(dòng)程序和模塊編程來(lái)說(shuō)都是非常完整的。在下面的 參考資料 中,我們可以學(xué)習(xí)到更多相關(guān)知識(shí)。
|