|
○ はじめに
Linux® では、/proc ファイルシステムというファイルシステムがサポートされています。/proc ファイルシステムは、実ファイルが存在するわけではなく、カーネル内のソフトウェアと情報のやりとりをするためのインタフェースです。例えば ps 等は、/proc 以下のファイルから情報を収集して画面に表示しています。
ここでは、/proc 以下にエントリを作成し、ユーザプロセスとやりとりする方法を説明します。
○ サンプルプログラム
/proc/driver/samplproc という /proc インタフェースに対応したサンプルプログラムproc.c です。はじめに のページから、全てのサンプルプログラムを 固めた tar ボールをダウンロードすることもできます。
insmod で組み込むと、/proc/driver/sampleproc というエントリを作成します。このエントリに対して、LECTURE 3 複数プロセスからのアクセス の様に echo 等で書き込んだ内容を cat で読み込むことができます。
--- proc.c ---
1 #include <asm/uaccess.h> /* copy_from_user */ 2 #include <linux/errno.h> 3 #include <linux/module.h> /* module 作成には必須 */ 4 #include <linux/kernel.h> /* printk */ 5 #include <linux/proc_fs.h> 6 #include <linux/stat.h> /* S_IFREG, ... */ 7 8 9 MODULE_LICENSE( "GPL" ); 10 11 static char* msg = "module [proc.o]"; 12 13 #define MAXBUF 32 14 static unsigned char procbuf[ MAXBUF ]; 15 static int buf_pos; 16 17 static struct semaphore proc_sem; 18 19 20 /* 21 * /proc/driver/sampleproc に対して読み出しを行った際に実行される。 22 * 内部バッファの内容を返す。 23 */ 24 static int 25 proc_read( char* page, char** start, off_t offset, int count, int* eof, void* data ) 26 { 27 int len = 0; 28 29 printk( KERN_INFO "%s : read() called\n", msg ); 30 31 if ( down_interruptible( &proc_sem ) ) { 32 printk( KERN_INFO "%s : down_interruptible for read failed\n", msg ); 33 return -ERESTARTSYS; 34 } 35 36 if ( buf_pos != 0 ) { 37 len += sprintf( page, "%s : Internal buffer contents = %s\n", msg, procbuf ); 38 buf_pos = 0; 39 } 40 41 up( &proc_sem ); 42 43 *eof = 1; 44 return len; 45 } 46 47 48 /** 49 * /proc/driver/sampleproc に対して書き込みを行った際に実行される。 50 * 書き込まれた内容を内部バッファに保持する。 51 */ 52 static int 53 proc_write( struct file* filp, const char* buffer, unsigned long count, void* data ) 54 { 55 int copy_len; 56 57 printk( KERN_INFO "%s : write() called\n", msg ); 58 59 if ( down_interruptible( &proc_sem ) ) { 60 printk( KERN_INFO "%s : down_interruptible for write failed\n", msg ); 61 return -ERESTARTSYS; 62 } 63 64 if ( buf_pos == MAXBUF ) { 65 up( &proc_sem ); 66 printk( KERN_INFO "%s : no space left\n", msg ); 67 return -ENOSPC; 68 } 69 70 if ( count > ( MAXBUF - buf_pos ) ) 71 copy_len = MAXBUF - buf_pos; 72 else 73 copy_len = count; 74 75 if ( copy_from_user( procbuf + buf_pos, buffer, copy_len ) ) { 76 up( &proc_sem ); 77 printk( KERN_INFO "%s : copy_from_user failed\n", msg ); 78 return -EFAULT; 79 } 80 81 buf_pos += copy_len; 82 up( &proc_sem ); 83 84 return copy_len; 85 } 86 87 88 /* 89 * モジュールの初期処理 90 * insmod 時に呼ばれる 91 */ 92 int 93 init_module( void ) 94 { 95 struct proc_dir_entry* entry; 96 entry = create_proc_entry( "driver/sampleproc", 97 S_IFREG | S_IRUGO | S_IWUGO, NULL ); 98 99 if ( entry != NULL ) { 100 entry->read_proc = proc_read; 101 entry->write_proc = proc_write; 102 } 103 else { 104 printk( KERN_INFO "%s : create_proc_entry failed\n", msg ); 105 return -EBUSY; 106 } 107 108 sema_init( &proc_sem, 1 ); 109 printk( KERN_INFO "%s : loaded into kernel\n", msg ); 110 111 return 0; 112 } 113 114 115 /* 116 * モジュールの解放処理 117 * モジュールの参照数が 0 であれば、rmmod 時に呼ばれる 118 */ 119 void 120 cleanup_module( void ) 121 { 122 remove_proc_entry( "driver/sampleproc", NULL ); 123 printk( KERN_INFO "%s : removed from kernel\n", msg ); 124 } 125 126 127 /* End of proc.c */
○ サンプルプログラムの説明
では、サンプルプログラムについて説明していきます。実際の読み込み処理や書き込み処理については、LECTURE 3 複数プロセスからのアクセス とほぼ同様なので、そちらの説明を参照してください。ここでは、/proc ファイルシステム特有の内容について説明します。
- 1 〜 6 行目
必要なヘッダファイルをインクルードしています。/proc にエントリを 作成する際に、ファイルの種類や読み込み/書込み権の設定を行いますが、 その際に linux/stat.h で定義されている定数を使用します。
- 20 〜 45 行目 ( proc_read() )
/proc 以下のエントリに対して読み出しを行った際に実行される関数です。 読みだし用の関数は、以下の形式で作成します。
int read_proc( char* page, char** start, off_t off, int count, int* eof, void* data );
page で指定されたバッファに対して、返したい内容を書き込みます。 page が示すバッファは、カーネルが 1 ページ( x86 では 4KB ) のバッファを 割り当てます。よって、1 ページを越える大きさのデータを書き込んではいけません ( count を越えなければ大丈夫です )。
1 ページを越える大きさのデータを返す場合は start をうまく使う必要があるのですが、 1 ページ未満の場合は start を無視します。proc.c では、ごく小さなデータを返すだけですので、 start を無視しています。筆者はまだ start の使い方がよく分かっていないので、 ここでは触れません m(__)m 分かったら説明を付け加えようと思います。
off は現在アクセスしているファイルの位置で、count は読み出しを要求している データのバイト数です。ファイルの位置に意味があるデータを扱う場合は、 off 以降のデータを書き込むことになります。
eof は、返すデータが無い場合に内部で 1 に設定します。これにより、呼出側で EOF と判断してくれます。また、data は /proc にエントリを作成する際に設定した ポインタがそのまま渡されます。
- 48 〜 85 行目 ( proc_write )
/proc 以下のエントリに対して書き込みを行った際に実行される関数です。 書き込み用の関数は、以下の形式で作成します。
int write_proc( struct file* filp, const char* buffer, unsigned long count, void* data );
filp、buffer、count については、通常のキャラクタデバイスドライバの write 関数と同様の意味です。data は、/proc にエントリを作成する際に設定したポインタ がそのまま渡されます。実際に行っている内容も、通常のキャラクタデバイスドライバ と変わりません。
- 88 〜 112 行目 ( init_module )
create_proc_entry 関数で、/proc の下にエントリを作成しています。
#include <linux/proc_fs.h> struct proc_dir_entry* create_proc_entry( const char* name, mode_t mode, struct proc_dir_entry* parent );
name には、/proc の下に作成するエントリ名称を指定します。既に存在する エントリの下に作成する場合は、proc.c の "driver/sampleproc" の様に階層込みで 指定することもできます。
mode には、作成するエントリのファイル種別や読み書き権を指定します。 これらは、linux/stat.h 内の定数を使用して設定します。定数の意味は man stat とすると大体分かります。例えば通常のファイルで、所有者 ( root ) のみの 読み書き権とするには、mode に S_IFREG | S_IRUSR | S_IWUSR を指定します。
linux/stat.h には、__KERNEL__ マクロを定義した時のみ有効になる定数があり、 proc.c ではそれを使用しています。S_IRUGO は S_IRUSR | S_IRGRP | S_IROTH と同じ意味で、S_IWUGO は S_IWUSR | S_IWGRP | S_IWOTH と同じ意味です。
create_proc_entry は、成功すると struct proc_dir_entry へのポインタを返します。 多階層のエントリを作成したい場合は、親から順にエントリを作成し、create_proc_entry の最後の引数に親のエントリを設定します。
create_proc_entry で NULL 以外のポインタが返って来たら、 読み出し/書き込みアクセスハンドラを struct proc_dir_entry の read_proc / write_proc メンバに設定します。これにより、/proc 以下のエントリに 対するアクセスが転送されてきます。
読み出し/書き込みアクセスハンドラに渡したいデータがある場合は、 ここで struct proc_dir_entry の data メンバにポインタを設定しておくと ハンドラ実行時に引数として渡されます。
#include <linux/proc_fs.h> struct proc_dir_entry* entry; int value; entry->read_proc = read_function; entry->write_proc = write_function; entry->data = ( void* )( &value );
- 115 〜 124 行目 ( cleanup_module )
cleanup_module では、作成した /proc エントリを remove_proc_entry によって 削除しています。
#include <linux/proc_fs.h> void remove_proc_entry( const char* name, struct proc_dir_entry* parent );
以上で proc.c の解説は終わりです。/proc ファイルシステムは、デバッグ用にちょっとした内部情報を表示させたり、外部から簡単にパラメータを設定したりするのに使えてとても便利だと思います。