Linux內核態文件讀寫相關函數API


1、前言

Linux系統中的文件系統由兩層結構進行構建:第一層為虛擬文件系統(VFS),第二層則是各種不同的具體的文件系統。VFS則是將各種具體的文件系統的公共部分抽取出來,從而形成一個抽象層,是Linux系統內核的一部分,它位於用戶程序和具體的文件系統之間,對用戶提供了標准的文件系統調用接口,對於具體的文件系統,通過一系列的對不同文件系統公用的函數指針來實際調用具體文件系統的函數,完成實際的各種差異操作。對於用戶,對文件的讀寫操作時,可以使用函數open()、read()、write()和close()等,當在Linux內核中,肯定是沒有這些函數可以使用,這個時候,可以使用內核的一些函數filp_open()、vfs_read()、vfs_write()、和filp_close()等去完成文件的讀寫。

 

2、常用API接口

接下來,簡單介紹相關的內核API接口是如何使用的,對於filp_open()、filp_close()、vfs_read()、vfs_write()函數的聲名在文件linux/fs.h中。

(1)文件打開filp_open

函數原型如下:

struct file *filp_open(const char *filename, int flags, umode_t mode);

參數說明:

filename:要打開或創建文件的字符串名稱,包括路徑部分;

flags:文件的打開方式,該取值與open()函數類似,可以取O_CREATE、O_RDWR、O_RDONLY;

mode:創建文件時使用該參數,設置文件的讀寫權限,其它情況可以設置為0。

返回值:

文件打開成功返回正確的struct file *指針,失敗返回錯誤的指針。

注意:

函數filp_open()將返回struct file *結構指針,將會提供給后繼的函數進行使用,需要使用IS_ERR()來校驗指針的有效性。

(2)文件關閉filp_close

函數的原型如下:

int filp_close(struct file *filp, fl_owner_t id);

參數說明:

filp:使用filp_opne()函數返回的struct file *結構指針;

id:一般設置為NULL。

返回值:

文件成功關閉時返回0,失敗時返回負的錯誤號。 

(3)文件讀取vfs_read

 函數的原型如下:

ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos);

參數說明:

file:函數filp_open()調用后返回的struct file *結構指針;

buf:buf緩沖區,用來存儲讀取到的數據,該參數具有__user進行修飾,表明buf指向用戶空間地址,如果傳入內核空間地址時,就會出錯,並返回-EFAULT;

count:要讀取的數據字節個數;

pos:返回數據讀取后的文件指針。

返回值:

文件讀取成功時,返回讀取到字節數,如果失敗,返回負的錯誤號。

(4)文件寫入vfs_write

 函數的原型如下:

ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)

參數說明:

file:函數filp_open()調用后返回的struct file *結構指針;

buf:buf緩沖區,用來存儲要寫入文件中的數據,該參數具有__user進行修飾,表明buf指向用戶空間地址,如果傳入內核空間地址時,就會出錯,並返回-EFAULT;

count:要寫入的數據字節個數;

pos:返回數據寫入后的文件指針。

返回值:

文件寫入成功時,返回寫入到的字節個數,如果失敗,返回負的錯誤號。

注意:

對於vfs_read()和vfs_write()函數,參數buf是使用__user進行修飾的,表明buf指向用戶空間地址,由於函數是在內核態中使用,因此,需要使用函數改變kernel對內存地址檢查的處理方式,可以使用下面的函數:

static inline void set_fs(mm_segment_t fs)

對於set_fs()函數中的參數fs,具有兩個取值,分別是USER_DS和KERNEL_DS,分別代表了用戶空間和內核空間,在默認情況下,內核取值為USER_FS,也就是對用戶空間地址檢查並變換,因此,需要使用

set_fs(KERNEL_DS)

進行轉換,改變kernel對內核空間地址檢查,防止函數調用時出錯,該函數的一般用法如下:

filp_open();
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
...
...   /* 內存相關的操作 */
... 
set_fs(old_fs);

以上就是,在內核態進行文件操作的相關操作API接口,對於函數的具體實現,可以查看內核源碼。

 

3、驅動模塊實例

接下來,將進行一個簡單的實例進行說明上面介紹的函數API接口使用,編寫一個簡單的內核模塊filp_open,在模塊加載的時候,在內核態中完成一個test,cfg文件的創建和讀寫操作,新建filp_open.c文件,代碼如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/dcache.h>
#include <asm/fcntl.h>
#include <asm/processor.h>
#include <asm/uaccess.h>

static int __init hello_init(void)
{
    int ret;
    struct file *fp;
    mm_segment_t old_fs;
    loff_t pos;
    char string1[15] = "hello world,";
    char string2[15] = "kernel file.";
    char buf[30];
    int len;

    printk(KERN_INFO "=====hello_init=====\n");
    fp = filp_open("/home/hly/test.cfg", O_RDWR | O_CREAT, 0644);
    if (IS_ERR(fp)) {
        ret = PTR_ERR(fp);
        printk(KERN_INFO "/hoem/hly/test.cfg open failed,err = %d\n", ret);
        return ret;
    }

    old_fs = get_fs();
    set_fs(KERNEL_DS);

    pos = fp->f_pos;
    vfs_write(fp, string1, strlen(string1), &pos);
    fp->f_pos = pos;

    pos = fp->f_pos;
    vfs_write(fp, string2, strlen(string2), &pos);
    fp->f_pos = pos;

    memset(buf, 0, sizeof(buf));
    len = strlen(string1) + strlen(string2);
    pos = 0;
    ret = vfs_read(fp, buf, len, &pos);
    if (ret < len) {
        ret = -EINVAL;
        printk(KERN_INFO "vfs read failed\n");
        return ret;
    }
    printk(KERN_INFO "f_pos = %lld,pos = %lld,buf = %s\n",
        fp->f_pos, pos, buf);

    set_fs(old_fs);
    filp_close(fp, NULL);

    return 0;
}

static void __exit hello_exit(void)
{
    printk(KERN_INFO "hello_exit\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_AUTHOR("HLY");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("A simple filp_open() test Module.");

模塊比較簡單,不介紹了,接下來,為該模塊編寫Makefile文件,如下:

# Makefile for driver

obj-m += filp_open.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

使用命令進行編譯,並將模塊加載到內核中運行:

$ make
$ sudo insmod filp_open.ko

模塊加載后,會創建/home/hly/test.cfg文件,並在文件中寫入字符串"hello world,kernel file.",將該字符串讀取出來並打印效果如下:

 到此,關於該測試模塊的相關介紹完畢。

 

4、小結

本文主要簡單介紹了Linux內核態中對文件操作的filp_open()、filp_close()、vfs_read()和vfs_write()的相關API接口使用,並給出了一個簡單的測試模塊。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM