LECTURE 5 select / poll システムコール

○ はじめに

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