|
|
○ はじめに
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 システムコールに対応させます。