探索Linux內(nèi)核空間文件IO實現(xiàn) BY Wang BaoYi Email: wby0322@gmail.com
本文參考《深入理解Linux內(nèi)核》完成,詳情請查閱相關(guān)書籍 意見和建議可以發(fā)送電子郵件至本人郵箱Email。 文章系作者原創(chuàng),轉(zhuǎn)載請注明出處
內(nèi)核空間文件操作函數(shù)注釋內(nèi)核空間讀寫文件的常規(guī)操作步驟同用戶空間一樣
完成上述功能要用的內(nèi)核函數(shù)有: ◆打開文件filp_open() ◆關(guān)閉文件filp_close() ◆讀文件內(nèi)容到內(nèi)存中vfs_read() ◆寫內(nèi)存中的數(shù)據(jù)到文件vfs_write() 函數(shù) filp_open(const char* filename, int open_mode, int mode)函數(shù)功能:在內(nèi)核空間中打開文件 函數(shù)原形: strcut file* filp_open(const char* filename, int open_mode, int mode); 返回值:strcut file*結(jié)構(gòu)指針,供后繼函數(shù)操作使用,該返回值用IS_ERR()來檢驗其有效性。 參數(shù):
示例 struct file *file = NULL; file = filp_open(/root/test.txt,O_RDWR|O_CREAT,0); //以讀寫方式(沒有則創(chuàng)建)打開文件/root/test.txt。并返回test.txt的文件指針給file. 函數(shù) filp_close(struct file*filp, fl_owner_t id)函數(shù)功能:關(guān)閉之前打開文件 函數(shù)原型:int filp_close(struct file*filp, fl_owner_t id); 參數(shù):
示例
函數(shù) vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)函數(shù)功能:讀取已經(jīng)打開的文件到內(nèi)存中 函數(shù)原型: ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { ssize_t ret; if (!(file->f_mode & FMODE_READ)) //判斷文件是否可讀 return -EBADF; if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read)) //是否定義文件讀方法 return -EINVAL; if (unlikely(!access_ok(VERIFY_WRITE, buf, count))) return -EFAULT; ret = rw_verify_area(READ, file, pos, count); //讀校驗 , if (ret >= 0) { count = ret; if (file->f_op->read) ret = file->f_op->read(file, buf, count, pos); //調(diào)用文件讀操作方法 else ret = do_sync_read(file, buf, count, pos); //通用文件模型讀方法 if (ret > 0) { fsnotify_access(file->f_path.dentry); add_rchar(current, ret); } inc_syscr(current); } return ret; } 通過filp_open我們已經(jīng)可以在當前進程的文件描述表中找到了file , 于是我們就可以調(diào)用保存在file中的文件操作方法(file_operation) file->f_op->read(file, buf, count, pos)來具體的操作文件。 上面的代碼實現(xiàn)并不復雜,在做了一些條件判斷以后,如果該文件索引節(jié)點inode定義了文件的讀實現(xiàn)方法的話,就調(diào)用此方法。Linux下特殊文件讀往往是用此方法, 一些偽文件系統(tǒng)如:proc,sysfs等,讀寫文件也是用此方法。而如果沒有定義此方法就會調(diào)用通用文件模型的讀寫方法.它最終就是讀內(nèi)存,或者需要從存儲介質(zhì)中去讀數(shù)據(jù). 參數(shù):
示例 int *buf; loff_t *pos = &(file->f_pos); buf = (int *)kmalloc(fsize+100,GFP_KERNEL); //分配一個文件自身大小+100字節(jié)邊界的內(nèi)存空間,將用來存放打開的文件,內(nèi)存分配方式為kmalloc的flag標志GFP_KERNEL。 vfs_read(file, buf, fsize, pos); //讀文件(指針為file)到內(nèi)存(buf為起始地址)中,讀取字節(jié)數(shù)定為文件自身大小,偏移為自身. 函數(shù) vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)函數(shù)功能:將內(nèi)存中的一段數(shù)據(jù)寫到文件中 函數(shù)原形: ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { ssize_t ret; if (!(file->f_mode & FMODE_WRITE)) return -EBADF; if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write)) return -EINVAL; if (unlikely(!access_ok(VERIFY_READ, buf, count))) return -EFAULT; ret = rw_verify_area(WRITE, file, pos, count); if (ret >= 0) { count = ret; if (file->f_op->write) ret = file->f_op->write(file, buf, count, pos); else ret = do_sync_write(file, buf, count, pos); if (ret > 0) { fsnotify_modify(file->f_path.dentry); add_wchar(current, ret); } inc_syscw(current); } return ret; } 可以看出這個函數(shù)和vfs_read()是差不多的,只是調(diào)用的文件操作方法不同而已(file->f_op->write) ,如果沒有定義file->f_op->write ,同樣也需要do_sync_write()調(diào)用同樣文件寫操作, 首先把數(shù)據(jù)寫到內(nèi)存中,然后在適當?shù)臅r候把數(shù)據(jù)同步到具體的存儲介質(zhì)中去. 參數(shù):
示例 loff_t *pos = &(file->f_pos); vfs_write(file,buf,fsize,pos); 獲取文件的大小我們可以利用文件的inode結(jié)構(gòu)獲得文件的大小,參考代碼如下 struct file *file = NULL; struct inode *inode = NULL; file = filp_open(file_path,O_RDWR|O_CREAT,0); inode = file->f_dentry->d_inode; fsize = inode->i_size; printk(KERN_ALERT "size=%d/n",(int)fsize); 示例代碼此ko模塊代碼在arm架構(gòu)的fpga上已經(jīng)跑通。因為涉及的參數(shù)比較多,為了清楚地重現(xiàn)重要步驟,對每步驟的函數(shù)進行了簡單的封裝。參數(shù)的傳遞只要理解上面的介紹即可區(qū)分清楚。執(zhí)行流程在static int hello_init(void)函數(shù)中 /* * kernel_hello_file.c * * Created on: 2010-11-9 * Author: Wang BaoYi(zats) * Email:wby0322@gmail.com */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/syscalls.h> #include <asm/unistd.h> #include <asm/uaccess.h> #define FILE_PATH_READ "/file_read_test" //打開文件路徑(包括文件名),未來將要讀的 #define FILE_PATH_WRITE "/new_file_test" //打開文件路徑(包括文件名),未來將要寫的 struct file *file = NULL; //保存打開文件的文件指針變量 struct inode *inode = NULL; //為了獲取文件大小用的inode結(jié)構(gòu)變量 int *file_buf; //保存開辟的內(nèi)存空間的地址的指針變量 loff_t fsize; //保存文件大小的變量 mm_segment_t old_fs; //保存內(nèi)存邊界的變量 /* * kernel_file_open封裝了文件打開函數(shù) * 參數(shù)為文件路徑(包含文件名)。 * 操作file類型結(jié)構(gòu)變量。 * 打開方式為讀寫(沒有則創(chuàng)建) */ static int kernel_file_open(char *file_path) { file = filp_open(file_path,O_RDWR|O_CREAT,0); if (IS_ERR(file)) { printk("Open file %s failed./n", file_path); return 0; } } /* * kernel_file_size封裝了獲取文件大小函數(shù) * 參數(shù)為待獲取大小的文件指針。 * 操作inode類型結(jié)構(gòu)變量。 * 返回值為文件大小,單位字節(jié) */ static loff_t kernel_file_size(struct file *file) { inode = file->f_dentry->d_inode; fsize = inode->i_size; printk(KERN_ALERT "size=%d/n",(int)fsize); return fsize; } /* * kernel_addr_limit_expend封裝了內(nèi)存邊界擴展函數(shù) * 參數(shù)無。 */ static int kernel_addr_limit_expend(void) { old_fs = get_fs(); set_fs(KERNEL_DS); return 0; } /* * kernel_addr_limit_resume封裝了內(nèi)存邊界恢復函數(shù) * 參數(shù)無。 */ static int kernel_addr_limit_resume(void) { set_fs(old_fs); } /* * kernel_file_read封裝了讀文件函數(shù) * 參數(shù)為open的文件指針,獲取的文件大小 * 返回值為讀入到內(nèi)存中的首地址。 */ void *kernel_file_read(struct file *file,loff_t fsize) { int *buf; loff_t *pos = &(file->f_pos); buf = (int *)kmalloc(fsize+100,GFP_KERNEL); vfs_read(file, buf, fsize, pos); return buf; } /* * kernel_file_ write封裝了讀文件函數(shù) * 參數(shù)為open的文件指針,數(shù)據(jù)在內(nèi)存中的地址,寫入到文件的字節(jié)數(shù) */ static int kernel_file_write(struct file *file,int *buf,loff_t fsize) { loff_t *pos = &(file->f_pos); vfs_write(file,buf,fsize,pos); } /* * ko的主函數(shù) */ static int hello_init(void) //ko的主函數(shù) { printk(KERN_ALERT "Y(^_^)Y Hello Wang`s file./n"); kernel_file_open(FILE_PATH_READ); //打開文件file_read_test kernel_file_size(file); //獲取file_read_test的大小 /*read file to mem*/ kernel_addr_limit_expend(); //邊界擴展 file_buf = kernel_file_read(file,fsize); //讀操作 filp_close(file, NULL); //關(guān)閉文件file_read_test kernel_addr_limit_resume(); //邊界恢復 /*write mem to file*/ kernel_file_open(FILE_PATH_WRITE); //打開文件new_file_test,沒有則創(chuàng)建 kernel_addr_limit_expend(); //邊界擴展 kernel_file_write(file,file_buf,fsize); //將前面讀到內(nèi)存中的數(shù)據(jù),寫入到文件new_file_test中 filp_close(file, NULL); //關(guān)閉文件 kernel_addr_limit_resume(); //邊界恢復 return 0; } static void hello_exit(void) { printk(KERN_ALERT "BYE BYE file Y(^_^)Y/n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("wby"); MODULE_DESCRIPTION("A simple hello world Module with File"); 簡單的效果圖 上述代碼可以直接復制到你要操作文件的代碼段,也可以把它放到頭文件中使用。 |
|