|
○ はじめに
block.c ではブロック I/O を実装してみました。この場合、read や write でブロックしない様にするためには、O_NONBLOCK 指定でデバイスファイルを開く必要があります。
ところで多くのユーザプロセスでは、I/O 時にブロックしてしまわない様に select や poll といったシステムコールを使って、ブロックせずに I/O を実行することができるかどうか調べてから I/O 処理を行っています。
ここでは、block.c を改造して select / poll システムコールに対応させてみましょう。
○ サンプルプログラム
select / poll システムコールに対応したサンプルプログラム poll.c です。 はじめに のページから、 全てのサンプルプログラムを 固めた tar ボールをダウンロードすることもできます。
簡単な試験プログラム poll_test.c も用意してみました。 poll.c をコンパイルして insmod で組み込み、/dev/chardev に何も書き込まずに poll_test.c の実行プログラムを実行すると、
Calling select() ...
と表示したまま停止します。その状態で別のコンソール等から
echo "HELLO WORLD" > /dev/chardev
の様にして書き込みを行うと、停止していたプロセスが動作を再開して
Now possible to read()
と表示します。その後また Calling select() ... の表示に戻って停止します(この流れを 5 回繰り返します)。
--- poll.c ---
1 #include <asm/semaphore.h> 2 #include <asm/uaccess.h> /* copy_from_user, copy_to_user */ 3 #include <linux/errno.h> 4 #include <linux/fs.h> /* inode, file, file_operations */ 5 #include <linux/kernel.h> /* printk() */ 6 #include <linux/module.h> 7 #include <linux/poll.h> /* POLLIN ... */ 8 #include <linux/wait.h> /* wait_queue_head_t */ 9 #include <linux/sched.h> /* wait_event_interruptible, wake_up_interruptible */ 10 11 12 MODULE_AUTHOR( "Ryo" ); 13 MODULE_DESCRIPTION( "Sample character device driver." ); 14 MODULE_LICENSE( "GPL" ); 15 16 17 static int devmajor = 77; 18 static char* devname = "chardev"; 19 static char* msg = "module [poll.o]"; 20 21 22 #define MAXBUF 32 23 static unsigned char devbuf[ MAXBUF ]; 24 static int buf_pos; 25 26 static struct semaphore chardev_sem; 27 static wait_queue_head_t write_q; 28 static wait_queue_head_t read_q; 29 30 31 /* 32 * ログ出力フォーマット用関数 33 */ 34 static void 35 log_format( char* body ) 36 { 37 if ( current != NULL ) { 38 printk( KERN_INFO "%s : PID = %-5i cmdline = \"%-10s\" : %s\n", 39 msg, current->pid, current->comm, body ); 40 } 41 else { 42 printk( KERN_INFO "%s : %s\n", msg, body ); 43 } 44 } 45 46 47 /* 48 * open() 49 */ 50 static int 51 chardev_open( struct inode* inode, struct file* filp ) 52 { 53 log_format( "open() called" ); 54 return 0; 55 } 56 57 58 /* 59 * release() 60 */ 61 static int 62 chardev_release( struct inode* inode, struct file* filp ) 63 { 64 log_format( "close() called" ); 65 return 0; 66 } 67 68 69 /* 70 * read() 71 * ユーザプロセスに対して内部バッファの内容を転送する。 72 * 転送した内容は内部バッファから消える。 73 */ 74 static ssize_t 75 chardev_read( struct file* filp, char* buf, size_t count, loff_t* pos ) 76 { 77 int copy_len; 78 int i; 79 80 log_format( "read() called" ); 81 82 if ( down_interruptible( &chardev_sem ) ) { 83 log_format( "down_interruptible for read failed" ); 84 return -ERESTARTSYS; 85 } 86 87 while ( buf_pos == 0 ) { 88 up( &chardev_sem ); 89 90 if ( filp->f_flags & O_NONBLOCK ) { 91 log_format( "read return because O_NONBLOCK is specified" ); 92 return -EAGAIN; 93 } 94 95 log_format( "read process going to sleep" ); 96 97 if ( wait_event_interruptible( read_q, ( buf_pos != 0 ) ) ) 98 return -ERESTARTSYS; 99 100 if ( down_interruptible( &chardev_sem ) ) { 101 log_format( "down_interruptible for read failed" ); 102 return -ERESTARTSYS; 103 } 104 } 105 106 107 log_format( "read process going to read" ); 108 109 if ( count > buf_pos ) 110 copy_len = buf_pos; 111 else 112 copy_len = count; 113 114 if ( copy_to_user( buf, devbuf, copy_len ) ) { 115 up( &chardev_sem ); 116 log_format( "copy_to_user failed" ); 117 return -EFAULT; 118 } 119 120 *pos += copy_len; 121 122 for ( i = copy_len; i < buf_pos; i ++ ) 123 devbuf[ i - copy_len ] = devbuf[i]; 124 125 buf_pos -= copy_len; 126 127 up( &chardev_sem ); 128 wake_up_interruptible( &write_q ); 129 130 return copy_len; 131 } 132 133 134 /* 135 * write() 136 * ユーザプロセスから転送された内容を内部バッファに書き込む。 137 */ 138 static ssize_t 139 chardev_write( struct file* filp, const char* buf, size_t count, loff_t* pos ) 140 { 141 int copy_len; 142 143 log_format( "write() called" ); 144 145 if ( down_interruptible( &chardev_sem ) ) { 146 log_format( "down_interruptible for write failed" ); 147 return -ERESTARTSYS; 148 } 149 150 while ( buf_pos == MAXBUF ) { 151 up( &chardev_sem ); 152 153 if ( filp->f_flags & O_NONBLOCK ) { 154 log_format( "write return because O_NONBLOCK is specified" ); 155 return -EAGAIN; 156 } 157 158 log_format( "write process going to sleep" ); 159 160 if ( wait_event_interruptible( write_q, ( buf_pos != MAXBUF ) ) ) 161 return -ERESTARTSYS; 162 163 if ( down_interruptible( &chardev_sem ) ) { 164 log_format( "down_interruptible for write failed" ); 165 return -ERESTARTSYS; 166 } 167 } 168 169 170 log_format( "write process going to write" ); 171 172 if ( count > ( MAXBUF - buf_pos ) ) 173 copy_len = MAXBUF - buf_pos; 174 else 175 copy_len = count; 176 177 if ( copy_from_user( devbuf + buf_pos, buf, copy_len ) ) { 178 up( &chardev_sem ); 179 log_format( "copy_from_user failed" ); 180 return -EFAULT; 181 } 182 183 *pos += copy_len; 184 buf_pos += copy_len; 185 186 up( &chardev_sem ); 187 wake_up_interruptible( &read_q ); 188 189 return copy_len; 190 } 191 192 193 /* 194 * poll() 195 * select / poll システムコール発行時に実行される。 196 */ 197 static unsigned int 198 chardev_poll( struct file* filp, poll_table* wait ) 199 { 200 unsigned int retmask = 0; 201 202 log_format( "poll() called" ); 203 204 poll_wait( filp, &read_q, wait ); 205 poll_wait( filp, &write_q, wait ); 206 207 if ( buf_pos != 0 ) { 208 retmask |= ( POLLIN | POLLRDNORM ); 209 log_format( "POLLIN | POLLRDNORM" ); 210 } 211 212 if ( buf_pos != MAXBUF ) { 213 retmask |= ( POLLOUT | POLLWRNORM ); 214 log_format( "POLLOUT | POLLWRNORM" ); 215 } 216 217 /* 218 読み込みデバイスが EOF の場合は retmask に POLLHUP を設定 219 デバイスがエラー状態である場合は POLLERR を設定 220 out-of-band データが読み出せる場合は POLLPRI を設定 221 */ 222 223 return retmask; 224 } 225 226 227 static struct file_operations chardev_fops = 228 { 229 owner : THIS_MODULE, 230 read : chardev_read, 231 write : chardev_write, 232 poll : chardev_poll, 233 open : chardev_open, 234 release : chardev_release, 235 }; 236 237 238 /* 239 * モジュールの初期処理 240 * insmod 時に呼ばれる 241 */ 242 int 243 init_module( void ) 244 { 245 if ( register_chrdev( devmajor, devname, &chardev_fops ) ) { 246 log_format( "register_chrdev failed" ); 247 return -EBUSY; 248 } 249 250 sema_init( &chardev_sem, 1 ); 251 252 init_waitqueue_head( &write_q ); 253 init_waitqueue_head( &read_q ); 254 255 log_format( "loaded into kernel" ); 256 return 0; 257 } 258 259 260 /* 261 * モジュールの解放処理 262 * モジュールの参照数が 0 であれば、rmmod 時に呼ばれる 263 */ 264 void 265 cleanup_module( void ) 266 { 267 if ( unregister_chrdev( devmajor, devname ) ) { 268 log_format( "unregister_chrdev failed" ); 269 /* 対策無し ... */ 270 } 271 272 log_format( "removed from kernel" ); 273 } 274 275 276 /* End of poll.c */
--- poll_test.c ---
1 #include <fcntl.h> 2 #include <stdio.h> 3 #include <sys/time.h> 4 #include <sys/types.h> 5 #include <unistd.h> 6 7 8 int 9 main( void ) 10 { 11 int cnt = 0; 12 char buf[32]; 13 int fd; 14 fd_set org_set, read_set; 15 16 17 if ( ( fd = open( "/dev/chardev", O_RDWR ) ) < 0 ) { 18 perror( "open" ); 19 exit( 1 ); 20 } 21 22 23 FD_ZERO( &org_set ); 24 FD_SET( fd, &org_set ); 25 26 while ( cnt < 5 ) { 27 read_set = org_set; 28 29 printf( "Calling select() ...\n" ); 30 31 if ( select( fd + 1, &read_set, NULL, NULL, NULL ) < 0 ) { 32 perror( "select" ); 33 close( fd ); 34 exit( 1 ); 35 } 36 37 printf( "Now possible to read()\n" ); 38 read( fd, buf, sizeof( buf ) ); 39 40 cnt ++; 41 } 42 43 close( fd ); 44 return 0; 45 } 46 47 48 /* End of poll_test.c */ 49
○ サンプルプログラムの説明
では、サンプルプログラムについて説明していきます。
poll.c は、前回の block.c を基本として、select / poll システムコール対応用の処理を追加しています。ここでは変更点について説明します。
- 7 行目
select / poll 対応処理の実装に必要なヘッダファイルをインクルードしています。
- 193 〜 224 行目 ( chardev_poll )
今回追加した関数です。select / poll システムコールが発行された際に実行され、 現在のデバイスの状態を返します。実際には、現在のデバイスの状態に応じて linux/poll.h に定義されている以下のマクロ定義を使ったビットマスクを unsigned int 型の変数に設定して返します。
マクロ 説明 POLLIN ブロックせずに読み出し可能 POLLRDNORM ( ソケット通信における out-of-band データに対して ) 通常のデータが読み出し可能 POLLPRI ソケット通信における out-of-band データがブロックせずに読み出し可能 POLLHUP EOF である POLLERR デバイス上でエラーが発生している POLLOUT ブロックせずに書き込み可能 POLLWRNORM ( ソケット通信における out-of-band データに対して ) 通常のデータが書き込み可能 POLLWRBAND ソケット通信における out-of-band データがブロックせずに書き込み可能 ところで select / poll システムコールは、いずれかのファイル記述子が ブロックせずに処理できる様になるまで、システムコール自体をブロックさせる ことができます(すぐに返る様にすることもできます)。システムコールが 全ファイル記述子の poll を実行した結果眠った場合、何らかのタイミング で起きて再度ファイル記述子の状態をチェックする必要があります。
そのための仕組みを設定しているのが、198、199 行目で実行している poll_wait です。
#include <linux/poll.h> void poll_wait( struct file* file, wait_queue_head_t* queue_p, poll_table* wait );
file と wait は chardev_poll の引数をそのまま渡します。queue_p には、 プロセスを起こすための待ち列を設定します。poll.c の場合、read / write 実行時に wake_up_interruptible を実行する read_q および write_q に対して poll_wait を実行しています。これにより、状態変更が 生じ得る read / write 実行のタイミングで select / poll システムコール が動作を再開し、再度状態をチェックしてくれる様になります。
- 227 〜 235 行目
struct file_opearations テーブルの poll タグに対して、新たに chardev_poll への関数ポインタを設定しています。これにより、select / poll システムコールが発行された際に chardev_poll 関数が実行されます。
以上で poll.c の解説は終わりです。次回はこの poll.c を更に改造し、ioctl システムコールに対応させます。