Linuxカーネルスペースファイル操作機能



Linux Kernel Space File Operation Function



カーネル内のファイルを操作するための関数はユーザースペースとは異なり、カーネルスペース専用の関数セット(主にfilp_open()、filp​​_close()、vfs_read()、vsf_write()、set_fs())を使用する必要があります。 、get_fs()など。関数は、ヘッダーファイルlinux / fsおよびasm / uaccess.hで宣言されています。

1.カーネル空間のファイル構造

カーネル内のファイル構造構造体ファイルは、ファイル操作でよく使用される構造体です。構造体のプロトタイプは次のように定義されます。ここで、f_opはファイルを操作する構造体であり、f_posはファイルの現在のポインターです。



struct file_operations構造体で定義されている関数は、いくつかのカーネルファイル操作です。構造のプロトタイプは次のように定義されます。たとえば、そのread()メンバー関数はファイルからデータを読み取り、write()メンバー関数はファイルにデータを書き込みます。



2.カーネル空間でのファイル作成操作

カーネルでファイルを開くと、ユーザースペースでライブラリ関数を呼び出すことができず、カーネルスペースとユーザースペースでファイルを開く機能が異なります。カーネル空間でファイルを開く関数はfilp_open()です(file_open()ではないことに注意してください)。そのプロトタイプは次のとおりです。

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



filp_openは、パスfilenameの下のファイルを開くために使用され、構造体構造体ファイルへのポインターを返します。次の関数は、このポインターを使用してファイルを操作します。戻り値は、IS_ERR()関数を使用してその有効性を確認する必要があります。

パラメータの説明は次のとおりです。

  • filename:Lujinを含む、開くまたは作成するファイルの名前。カーネルでファイルを開く機能を使用すると、ファイルを開くタイミングが間違っているとエラーが発生しやすくなります。たとえば、この関数が呼び出されたとき、ファイルドライバがロードされていないか、開いているファイルがシステムにマウントされていません。
  • open_mode:ファイルを開くモードを設定します。この値は、開いているユーザースペースに対応するパラメーターに類似しており、値O_CREAT、O_RDWR、O_RDONLYおよびその他の値を取ることができます。
  • mode:このパラメーターは、ファイルを作成するときにのみ使用されます。これは、ファイルを作成するための読み取りおよび書き込み権限を設定するために使用されます。ファイルを作成しない他の条件は無視して0に設定できます。A
#define DBGPRINT printk //The kernel program uses file_open to open the file struct file *SIPFW_OpenFile(const char *filename, int flags, int mode) { struct file *f = NULL DBGPRINT('==>SIPFW_OpenFile ') /* */ f = filp_open(filename, flags, 0) if (!f || IS_ERR(f)) { f = NULL } DBGPRINT('<==SIPFW_OpenFile ') return f }

3.カーネルスペースファイルの読み取りおよび書き込み操作

Linuxカーネルでファイルを読み書きするための関数は、vfs_read()関数とvfs_write()関数です。これら2つの関数のプロトタイプは次のとおりです。関数の基本的な意味は、基本的にユーザースペースと同じです。

//Read the file ssize_t vfs_read(struct file *filp, char __user *buffer, size_t len, loff_t *pos) //Write file ssize_t vfs_write(struct file *filp, const char __user *buffer, size_t len, loff_t *pos)

これら2つの関数のパラメーターの意味は次のとおりです。

  • filp:ファイルポインタ。filp_open()関数によって返されます。
  • buffer:バッファ、ファイルから読み取られたデータはこのバッファに入れられ、ファイルに書き込まれたデータもこのバッファにあります。
  • len:ファイルから読み取られたデータまたはファイルに書き込まれたデータの長さ。
  • pos:ファイルポインタの位置、つまりファイルデータの操作を開始する場所。

注:2つの関数vfs_read()およびvfs_write()の2番目のパラメーターには、バッファーの前に__user修飾子があります。これには、バッファー・ポインターがユーザーのスペース・アドレスを指している必要があります。カーネルで上記の2つの関数を使用してファイル操作を直接実行する場合、カーネルスペースのポインターが渡されると、関数は失敗したEFAULTを返します。しかし、Linuxカーネルでは、一般にユーザースペースポインタを生成するのは簡単ではありません。または、ユーザースペースメモリを個別に使用するのは便利ではありません。これらの2つの関数が正しく機能するためには、カーネル空間でアドレスを処理できる必要があります。

set_fs()関数を使用すると、上記の2つの関数を指定して、バッファーアドレスを処理できます。プロトタイプは次のとおりです。

void set_fs(mm_segment_t fs)

この関数は、カーネルがメモリチェックを処理する方法を変更し、メモリアドレスチェック方法をユーザーが指定した方法に設定します。パラメータfsには、USER_DSとKERNEL_DSの2つの値があります。それぞれユーザースペースとカーネルスペースを表します。

デフォルトでは、カーネルのアドレスチェック方法はUSER_DSです。これは、ユーザースペースに応じたアドレスチェックと、ユーザーアドレススペースからカーネルアドレススペースへの変換を意味します。関数でカーネルアドレス空間を使用する場合は、set_fs(KERNEL_DS)関数を使用して設定する必要があります。 set_fs()関数に対応して、get_fs()関数は現在の設定を取得します。 set_fs()を使用する前に、get_fs()関数を呼び出して、以前の設定を取得します。ファイルを操作した後、set_fs()関数を使用して以前の設定を復元します。

カーネルスペースファイルを継続的に書き込むためのフレームワークは次のとおりです。

mm_segmen_t old_fs old_fs = get_fs() set_fs(KERNEL_DS) ... set_fs(old_fs)

注:vfs_read()およびvfs_write()を使用する場合は、最後のパラメーターloff_t * posに注意してください。posが指す値は初期化する必要があり、ファイルの読み取りと書き込みを示します。このパラメーターを使用して、継続ファイルの場所を設定します。これにより、ユーザースペースでlseek()関数の関数を完了することができます。

以下は、カーネル空間のファイル読み取り機能を使用してファイルからデータを読み取る例です。

ssize_t ReadFile(struct file *filp, char __user *buffer, size_t len, loff_t *pos) { ssize_t count = 0 oldfs = get_fs() set_fs(KERNEL_DS) count = file->f_op->read(filp, buf, len, &file->f_pos) set_fs(oldfs) return count }

次のサンプルプログラムで発生する問題であるため、ここでいくつかの単語を無駄にします。

閲覧した記事へのリンク:( http://blog.chinaunix.net/uid-15141543-id-2775960.html )。

上記のコードは非常に簡単に実装できます。いくつかの条件付き判断を行った後、ファイルインデックスノードのiノードがファイル読み取りの実装メソッドを定義している場合、このメソッドが呼び出されます。 Linuxでの特別なファイル読み取りでは、この方法がよく使用されます。proc、sysfsなどの一部の疑似ファイルシステムでは、この方法はファイルの読み取りと書き込みにも使用されます。このメソッドが定義されていない場合、一般的なファイルモデルの読み取りおよび書き込みメソッドが呼び出されます(次の例では、このメソッドが定義されていない、つまりf-> f_op-> read == NULLであることがわかります。したがって、最終的にはvfs_readメソッドが直接呼び出されてファイルが読み取られます)。最終的には読み取りメモリであるか、記憶媒体からデータを読み取る必要があります。

4.カーネル空間でのファイルクローズ操作

カーネル内のファイルが使用されていない場合は、リソースを解放するためにファイルを閉じる必要があります。 Linuxカーネルでファイルを閉じる関数はfilp_close()であり、そのプロトタイプは次のとおりです。

int filp_close(struct file *filp, fl_owner_t id)

この関数は、以前に開いたファイルを閉じるために使用されます。関数の最初のパラメーターはfilp_open()によって返されるポインターであり、2番目のパラメーターはPOSIXスレッドIDです。 LinuxカーネルではファイルポインタをNULLで渡すか、current-> filesを使用して現在のモジュールファイルポインタを渡すことができます。

void CloseFile(void) { if (myfile) filp_close(myfiles, current->files) }

5.例

ファイル:

ソースコード:

main_file.c

//kernel #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //user #define DBGPRINT printk //The kernel program uses file_open to open the file struct file *SIPFW_OpenFile(const char *filename, int flags, int mode) { struct file *f = NULL DBGPRINT('==>SIPFW_OpenFile ') f = filp_open(filename, flags, 0) if (!f || IS_ERR(f)) { f = NULL } DBGPRINT('SIPFW_ReadLine ') if (!f || IS_ERR(f) || !buf || len f_inode) { goto out_error } inode = f->f_inode if (!(f->f_mode & FMODE_READ)) { goto out_error } if (f->f_op /*&& f->f_op->read*/) { oldfs = get_fs() set_fs(KERNEL_DS) count = 0 if (vfs_read(f, buf, 1, &f->f_pos) <= 0) { DBGPRINT('file read failure ') goto out } if (*buf == EOF) { DBGPRINT('file EOF ') goto out } count = 1 while (*buf != EOF && *buf != '' && *buf != ' ' && *buf != ' ' && count f_pos i_size) { buf += 1 count += 1 if (vfs_read(f, buf, 1, &f->f_pos) <= 0) { count -= 1 break } } } else { DBGPRINT('goto out_error ') goto out_error } if (*buf == ' '|| *buf ==' ' ||*buf == EOF ) { *buf = '' count -= 1 } else { buf += 1 *buf = '' } out: set_fs(oldfs) out_error: //DBGPRINT('SIPFW_WriteLine ') // if (!f || IS_ERR(f) || !buf || len f_dentry || !f->f_dentry->d_inode) // { // goto out_error // } // inode = f->f_dentry->d_inode // if (!(f->f_mode & FMODE_WRITE) || !(f->f_mode & FMODE_READ) ) // { // goto out_error // } // if (f->f_op && f->f_op->read && f->f_op->write) // { // //f->f_pos = f->f_count // oldfs = get_fs() // set_fs(KERNEL_DS) // count = 0 // count = f->f_op->write(f, buf, len, &f->f_pos) // if (count == -1) // { // goto out // } // } // else // { // goto out_error // } // out: // set_fs(oldfs) // out_error: // DBGPRINT('SIPFW_CloseFile ') if(!f) return filp_close(f, current->files) DBGPRINT('SIPFW_HandleConf ') // Create an /etc/sipfw.conf file in advance f = SIPFW_OpenFile('/etc/sipfw.conf', O_RDWR, 0) if(f == NULL) { retval = -1 DBGPRINT('SIPFW_OpenFile called failure ') goto EXITSIPFW_HandleConf } while((count = SIPFW_ReadLine(f, line, 256)) > 0) { pos = line DBGPRINT('line = %d, data: %s ', l, line) l++ memset(line, 0, sizeof(line)) } SIPFW_CloseFile(f) EXITSIPFW_HandleConf: DBGPRINT('SIPFW_Init ') ret = SIPFW_HandleConf() DBGPRINT('SIPFW_Exit ') DBGPRINT('<==SIPFW_Exit ') } module_init(SIPFW_Init) module_exit(SIPFW_Exit) MODULE_LICENSE('GPL/BSD')

Makefile:

MODULE_NAME :=main_file obj-m :=$(MODULE_NAME).o KERNELDIR = /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

テストで読み取られたファイル:(/ etc / sipfw.conf)

実行:

注:このプロジェクトの動作環境:

OK!それが役に立てば幸い!