LECTURE 6 ioctl システムコール

○ はじめに

今回は、ユーザプロセスから発行される ioctl システムコールに対応してみます。ioctl は、read や write で行うことが適当でない、デバイスの設定を変えたりする場合に良く使われます。

ここでは、poll.c を改造して ioctl システムコールに対応させてみましょう。

○ サンプルプログラム

ioctl システムコールに対応したサンプルプログラム ioctl.c chardev_ioct.h です。 はじめに のページから、 全てのサンプルプログラムを 固めた tar ボールをダウンロードすることもできます。

簡単な試験プログラム ioctl_test.c も用意してみました。 ioctl.c をコンパイルして insmod で組み込み、ioctl_test.c の実行プログラムを実行すると

Tell driver to update internal value ...
Query driver internal value ...
Internal value = 0
Tell driver to update internal value via pointer ...
Query driver internal value via pointer ...
Internal value = 1
Reset reference counter ...

と表示されます。

--- ioctl.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/ioctl.h>    /* ioctl */
    6    #include <linux/kernel.h>   /* printk() */
    7    #include <linux/module.h> 
    8    #include <linux/poll.h>     /* POLLIN ... */
    9    #include <linux/wait.h>     /* wait_queue_head_t */
   10    #include <linux/sched.h>    /* wait_event_interruptible, wake_up_interruptible */
   11    
   12    #include "chardev_ioctl.h"
   13    
   14    
   15    MODULE_AUTHOR( "Ryo" );
   16    MODULE_DESCRIPTION( "Sample character device driver." );
   17    MODULE_LICENSE( "GPL" );
   18    
   19    
   20    static int   devmajor = 77;
   21    static char* devname  = "chardev";
   22    static char* msg      = "module [poll.o]";
   23    
   24    
   25    #define MAXBUF 32
   26    static unsigned char devbuf[ MAXBUF ];
   27    static int buf_pos;
   28    
   29    static struct semaphore chardev_sem;
   30    static wait_queue_head_t write_q;
   31    static wait_queue_head_t read_q;
   32    
   33    
   34    /*
   35     * ログ出力フォーマット用関数
   36     */
   37    static void 
   38    log_format( char* body )
   39    {
   40        if ( current != NULL ) {
   41            printk( KERN_INFO "%s : PID = %-5i cmdline = \"%-10s\" : %s\n", 
   42                msg, current->pid, current->comm, body );
   43        }
   44        else {
   45            printk( KERN_INFO "%s : %s\n", msg, body );
   46        }
   47    }
   48    
   49    
   50    /*
   51     * open()
   52     */
   53    static int 
   54    chardev_open( struct inode* inode, struct file* filp )
   55    {
   56        log_format( "open()  called" );
   57        return 0;
   58    }
   59    
   60    
   61    /*
   62     * release()
   63     */
   64    static int 
   65    chardev_release( struct inode* inode, struct file* filp )
   66    {
   67        log_format( "close() called" );
   68        return 0;
   69    }
   70    
   71    
   72    /*
   73     * read()
   74     * ユーザプロセスに対して内部バッファの内容を転送する。
   75     * 転送した内容は内部バッファから消える。
   76     */
   77    static ssize_t 
   78    chardev_read( struct file* filp, char* buf, size_t count, loff_t* pos )
   79    {
   80        int copy_len;
   81        int i;
   82    
   83        log_format( "read()  called" );
   84    
   85        if ( down_interruptible( &chardev_sem ) ) {
   86            log_format( "down_interruptible for read failed" );
   87            return -ERESTARTSYS;
   88        }
   89    
   90        while ( buf_pos == 0 ) {
   91            up( &chardev_sem );
   92    
   93            if ( filp->f_flags & O_NONBLOCK ) {
   94                log_format( "read return because O_NONBLOCK is specified" );
   95                return -EAGAIN;
   96            }
   97    
   98            log_format( "read process going to sleep" );
   99    
  100            if ( wait_event_interruptible( read_q, ( buf_pos != 0 ) ) )
  101                return -ERESTARTSYS;
  102    
  103            if ( down_interruptible( &chardev_sem ) ) {
  104                log_format( "down_interruptible for read failed" );
  105                return -ERESTARTSYS;
  106            }
  107        }
  108    
  109    
  110        log_format( "read process going to read" );
  111    
  112        if ( count > buf_pos )
  113            copy_len = buf_pos;
  114        else
  115            copy_len = count;
  116    
  117        if ( copy_to_user( buf, devbuf, copy_len ) ) {
  118            up( &chardev_sem );
  119            log_format( "copy_to_user failed" );
  120            return -EFAULT;
  121        }
  122    
  123        *pos += copy_len;
  124    
  125        for ( i = copy_len; i < buf_pos; i ++ )
  126            devbuf[ i - copy_len ] = devbuf[i];
  127    
  128        buf_pos -= copy_len;
  129    
  130        up( &chardev_sem );
  131        wake_up_interruptible( &write_q );
  132    
  133        return copy_len;
  134    }
  135    
  136    
  137    /*
  138     * write()
  139     * ユーザプロセスから転送された内容を内部バッファに書き込む。
  140     */
  141    static ssize_t 
  142    chardev_write( struct file* filp, const char* buf, size_t count, loff_t* pos )
  143    {
  144        int copy_len;
  145    
  146        log_format( "write() called" );
  147    
  148        if ( down_interruptible( &chardev_sem ) ) {
  149            log_format( "down_interruptible for write failed" );
  150            return -ERESTARTSYS;
  151        }
  152    
  153        while ( buf_pos == MAXBUF ) {
  154            up( &chardev_sem );
  155    
  156            if ( filp->f_flags & O_NONBLOCK ) {
  157                log_format( "write return because O_NONBLOCK is specified" );
  158                return -EAGAIN;
  159            }
  160    
  161            log_format( "write process going to sleep" );
  162    
  163            if ( wait_event_interruptible( write_q, ( buf_pos != MAXBUF ) ) )
  164                return -ERESTARTSYS;
  165    
  166            if ( down_interruptible( &chardev_sem ) ) {
  167                log_format( "down_interruptible for write failed" );
  168                return -ERESTARTSYS;
  169            }
  170        }
  171    
  172    
  173        log_format( "write process going to write" );
  174    
  175        if ( count > ( MAXBUF - buf_pos ) )
  176            copy_len = MAXBUF - buf_pos;
  177        else
  178            copy_len = count;
  179    
  180        if ( copy_from_user( devbuf + buf_pos, buf, copy_len ) ) {
  181            up( &chardev_sem );
  182            log_format( "copy_from_user failed" );
  183            return -EFAULT;
  184        }
  185    
  186        *pos    += copy_len;
  187        buf_pos += copy_len;
  188    
  189        up( &chardev_sem );
  190        wake_up_interruptible( &read_q );
  191    
  192        return copy_len;
  193    }
  194    
  195    
  196    /*
  197     * poll()
  198     * select / poll システムコール発行時に実行される。
  199     */
  200    static unsigned int 
  201    chardev_poll( struct file* filp, poll_table* wait )
  202    {
  203        unsigned int retmask = 0;
  204    
  205        log_format( "poll()  called" );
  206    
  207        poll_wait( filp, &read_q,  wait );
  208        poll_wait( filp, &write_q, wait );
  209    
  210        if ( buf_pos != 0 ) {
  211            retmask |= ( POLLIN  | POLLRDNORM );
  212            log_format( "POLLIN  | POLLRDNORM" );
  213        }
  214    
  215        if ( buf_pos != MAXBUF ) {
  216            retmask |= ( POLLOUT | POLLWRNORM );
  217            log_format( "POLLOUT | POLLWRNORM" );
  218        }
  219    
  220        /* 
  221           読み込みデバイスが EOF の場合は retmask に POLLHUP を設定
  222           デバイスがエラー状態である場合は POLLERR を設定
  223           out-of-band データが読み出せる場合は POLLPRI を設定
  224        */
  225    
  226        return retmask;
  227    }
  228    
  229    
  230    /* 
  231     * ioctl()
  232     * 複数プロセスが同一変数を操作するため、セマフォやスピンロック等での
  233     * ロックを考慮すべきだが、ioctl の実装を例示したいだけなのでロックは
  234     * 一切使っていない。。。
  235     */
  236    static int 
  237    chardev_ioctl( 
  238        struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg )
  239    {
  240        static int ioctl_value = 0;
  241        int ret = 0;
  242    
  243        log_format( "ioctl()  called" );
  244    
  245        switch( cmd ) {
  246            case CHARDEV_IOCTL_TELL :
  247                log_format( "IOCTL_TELL" );
  248                ioctl_value = arg;
  249                break;
  250    
  251            case CHARDEV_IOCTL_SET :
  252                log_format( "IOCTL_SET" );
  253                if ( get_user( ioctl_value, ( int* )arg ) ) 
  254                    return -EFAULT;
  255                break;
  256    
  257            case CHARDEV_IOCTL_QUERY :
  258                log_format( "IOCTL_QUERY" );
  259                return ioctl_value;
  260    
  261            case CHARDEV_IOCTL_GET :
  262                log_format( "IOCTL_GET" );
  263                if ( put_user( ioctl_value, ( int* )arg ) )
  264                    return -EFAULT;
  265                break;
  266    
  267            case CHARDEV_IOCTL_RESET :
  268                log_format( "IOCTL_RESET" );
  269    
  270                while ( MOD_IN_USE )
  271                    MOD_DEC_USE_COUNT;
  272    
  273                MOD_INC_USE_COUNT;
  274                break;
  275    
  276            default:
  277                return -EINVAL;
  278                /* POSIX 標準では -ENOTTY らしい */
  279        }
  280    
  281        return 0;
  282    }
  283    
  284    
  285    static struct file_operations chardev_fops = 
  286    {
  287        owner   : THIS_MODULE,
  288        read    : chardev_read,
  289        write   : chardev_write,
  290        poll    : chardev_poll,
  291        ioctl   : chardev_ioctl,
  292        open    : chardev_open,
  293        release : chardev_release,
  294    };
  295    
  296    
  297    /*
  298     * モジュールの初期処理
  299     * insmod 時に呼ばれる
  300     */
  301    int 
  302    init_module( void )
  303    {
  304        if ( register_chrdev( devmajor, devname, &chardev_fops ) ) {
  305            log_format( "register_chrdev failed" );
  306            return -EBUSY;
  307        }
  308    
  309        sema_init( &chardev_sem, 1 );
  310        init_waitqueue_head( &write_q );
  311        init_waitqueue_head( &read_q );
  312    
  313        log_format( "loaded  into kernel" );
  314        return 0;
  315    }
  316    
  317    
  318    /*
  319     * モジュールの解放処理
  320     * モジュールの参照数が 0 であれば、rmmod 時に呼ばれる
  321     */
  322    void
  323    cleanup_module( void )
  324    {
  325        if ( unregister_chrdev( devmajor, devname ) ) {
  326            log_format( "unregister_chrdev failed" );
  327            /* 対策無し ... */
  328        }
  329    
  330        log_format( "removed from kernel" );
  331    }
  332    
  333    
  334    /* End of ioctl.c */

--- chardev_ioctl.h ---

    1    #ifndef ___chardev_ioctl_h_included_
    2    #define ___chardev_ioctl_h_included_
    3    
    4    #include <linux/ioctl.h>
    5    
    6    /* 
    7     * /dev/chardev に対する ioctl() コマンドに使うマジックナンバー
    8     *
    9     * /usr/src/linux/Documentation/ioctl-number.txt に記述されている 
   10     * Code とぶつからないものを選ぶ
   11     */
   12    #define CHARDEV_IOCTL_MAGIC  0xAE
   13    
   14    
   15    #define CHARDEV_IOCTL_TELL   _IOC( _IOC_NONE,  CHARDEV_IOCTL_MAGIC, 0, 0 )
   16    #define CHARDEV_IOCTL_SET    _IOC( _IOC_WRITE, CHARDEV_IOCTL_MAGIC, 1, sizeof( int ) )
   17    #define CHARDEV_IOCTL_QUERY  _IOC( _IOC_NONE,  CHARDEV_IOCTL_MAGIC, 2, 0 ) 
   18    #define CHARDEV_IOCTL_GET    _IOC( _IOC_READ,  CHARDEV_IOCTL_MAGIC, 3, sizeof( int ) )
   19    #define CHARDEV_IOCTL_RESET  _IOC( _IOC_NONE,  CHARDEV_IOCTL_MAGIC, 4, 0 )
   20    
   21    
   22    #endif
   23    
   24    
   25    /* End of chardev_ioctl.h */

--- ioctl_test.c ---

    1    #include <fcntl.h>
    2    #include <stdlib.h>
    3    #include <sys/ioctl.h>
    4    
    5    #include "chardev_ioctl.h"
    6    
    7    
    8    int 
    9    main( void )
   10    {
   11        int  setval = 0;
   12        int  retval;
   13        int  fd;
   14    
   15        if ( ( fd = open( "/dev/chardev", O_RDWR ) ) < 0 ) {
   16            perror( "open" );
   17            exit( 1 );
   18        }
   19    
   20    
   21        /*
   22         * IOCTL_TELL
   23         */
   24        printf( "Tell driver to update internal value ...\n" );
   25    
   26        if ( ioctl( fd, CHARDEV_IOCTL_TELL, setval ) == -1 )
   27            perror( "ioctl( CHARDEV_IOCTL_TELL )" );
   28    
   29    
   30        /*
   31         * IOCTL_QUERY
   32         */
   33        printf( "Query driver internal value ...\n" );
   34    
   35        if ( ( retval = ioctl( fd, CHARDEV_IOCTL_QUERY ) ) == -1 )
   36            perror( "ioctl( CHARDEV_IOCTL_QUERY )" );
   37    
   38        printf( "Internal value = %d\n", retval );
   39        setval = retval + 1;
   40    
   41    
   42        /*
   43         * IOCTL_SET
   44         */
   45        printf( "Tell driver to update internal value via pointer ...\n" );
   46    
   47        if ( ioctl( fd, CHARDEV_IOCTL_SET, &setval ) == -1 )
   48            perror( "ioctl( CHARDEV_IOCTL_SET )" );
   49    
   50    
   51        /*
   52         * IOCTL_GET
   53         */
   54        printf( "Query driver internal value via pointer ...\n" );
   55    
   56        if ( ioctl( fd, CHARDEV_IOCTL_GET, &retval ) == -1 )
   57            perror( "ioctl( CHARDEV_IOCTL_GET )" );
   58    
   59        printf( "Internal value = %d\n", retval );
   60    
   61    
   62        /*
   63         * IOCTL_RESET
   64         */
   65        printf( "Reset reference counter ...\n" );
   66    
   67        if ( ioctl( fd, CHARDEV_IOCTL_RESET ) == -1 )
   68            perror( "ioctl( CHARDEV_IOCTL_RESET )" );
   69    
   70    
   71        close( fd );
   72        return 0;
   73    }
   74    
   75    
   76    /* End of ioctl_test.c */

○ サンプルプログラムの説明

では、サンプルプログラムについて説明していきます。

ioctl.c は、前回の poll.c を基本として、ioctl システムコール対応用の処理を追加しています。ここでは変更点について説明します。

  • 5、12 行目

    ioctl 対応処理の実装に必要なヘッダファイルをインクルードしています。 12 行目では、ioctl コマンド定数を定義した自作のヘッダファイルをインクルードしています。

    ユーザプロセスで使用する ioctl システムコールの関数プロトタイプは

    #include <sys/ioctl.h>
    int ioctl( int fd, int request, ... );
         

    の様になります。第 2 引数にコマンドを指定する定数を、第 3 引数に コマンドの引数を直接値として、あるいはポインタを介してデータを渡します ( 引数が無くても良い )。

    chardev_ioctl.h では以下の ioctl コマンド定数を定義しています。

    定数名 ユーザプロセスから見た入出力方向 ioctl 第3引数の型
    CHARDEV_IOCTL_TELL 出力
    CHARDEV_IOCTL_SET 出力 ポインタ
    CHARDEV_IOCTL_QUERY 入力
    CHARDEV_IOCTL_GET 入力 ポインタ
    CHARDEV_IOCTL_RESET 出力 無し

    CHARDEV_IOCTL_TELL / CHARDEV_IOCTL_SET はどちらも、デバイスドライバ に対して値を設定するコマンドです。TELL は直接値を通知し、SET はポインタ を第 3 引数に設定する点が異なります。

    CHARDEV_IOCTL_QUERY / CHARDEV_IOCTL_GET はどりたも、デバイスドライバ から値を取得するコマンドです。QUERY は ioctl の返り値として値を取得し、 GET は第 3 引数に設定したポインタの参照先に値を格納する点が異なります。

    CHARDEV_IOCTL_RESET はデバッグ用のコマンドで、カーネルモジュールの 参照カウンタ ( ユーザプロセスや他のカーネルモジュール等から参照されている 数を管理するカウンタ ) を、強制的に 1 にしてしまいます。これを実行した直後に ファイル記述子を閉じてしまえば参照カウンタは 0 になるため、他に参照している ユーザプロセス等があっても強制的に rmmod してしまうことが出来ます。

    ユーザプロセスにおけるこれらのコマンドの詳しい使い方は、 ioctl_test.c を参照して下さい。

    ところで各々のコマンド定数は、システム全体で唯一になることが求められます。 そのためにはまず、カーネルソースツリー内の Documentation/ioctl-number.txt を参照して、使われていないマジックナンバーを決定します。 chardev_ioctl.h では、0xAE を 使っています。この値は、使っているシステムに応じて決定して下さい。

    次に、_IOC マクロを使って定数を作り出します。

    #include <linux/ioctl.h>
    _IOC( direction, magic, number, size )
         

    direction はユーザプロセスとのデータ転送の方向を、以下の定数で指定します。

    定数名 ユーザプロセスから見たデータ転送の方向
    _IOC_NONE 無し ( ポインタではなく直接値をやりとりする場合は、これを使う )
    _IOC_READ 入力
    _IOC_WRITE 出力
    _IOC_READ | _IOC_WRITE 入出力

    magic は、先程決定したマジックナンバーを指定します。number は、 コマンドを識別するための通番を指定します。ビット幅が 8 ビットなので、 0 - 255 を指定します。

    size は、データ転送を伴う場合の転送サイズを設定します。ただし、 コマンドから転送サイズをデコードして使うのでなければ、別に何を設定 しても良いみたいです。

  • 230 〜 282 行目 ( chardev_ioctl )

    今回追加した関数です。ioctl システムコールが発行された際に実行されます。 switch 文でコマンドに応じた処理を行っているだけです。chardev_ioctl の第 3 引数 cmd としてコマンド定数が、第 4 引数 arg としてユーザプロセスで ioctl システムコールの第 3 引数に設定した変数が渡されます。

    コマンド 処理内容
    CHARDEV_IOCTL_TELL arg を直接の設定値としているので、内部変数に設定します。
    CHARDEV_IOCTL_SET arg は整数変数へのポインタとして扱います。このポインタの参照先から 内部変数にデータを転送するのですが、read / write の実装同様、直接代入 することはできません。ここでは、1 / 2 / 4 バイトのデータ転送を copy_from_user よりも早く行ってくれる get_user 関数を使っています。 get_user は成功すると 0、失敗すると -EFAULT を返します。
    CHARDEV_IOCTL_QUERY ioctl の戻り値を返却値としているので、内部変数の値を return します。
    CHARDEV_IOCTL_GET arg は整数変換へのポインタとして扱います。このポインタの参照先に対して、 1 / 2 / 4 バイトのデータ転送を copy_to_user よりも早く行ってくれる put_user 関数を使っています。put_user は成功すると 0 を、失敗すると -EFAULT を返します。
    CHARDEV_IOCTL_RESET カーネルモジュールの参照カウンタを、強制的に 1 にします。これにより、 実行後にファイル記述子を閉じれば参照カウンタが 0 となり、他ユーザプロセス が参照中であっても rmmod することができます。本当の目的は、バグ等で 参照カウンタが狂ってしまい rmmod できなくなってしまった時に使いたいだけです。 MOD_IN_USE マクロは、参照カウンタが 0 で無い場合に真と評価されます。 MOD_DEC_USE_COUNT マクロは参照カウンタを 1 減算し、 MOD_INC_USE_COUNT マクロは参照カウンタを 1 増加します。 これらを使い、参照カウンタが 0 になるまで MOD_DEC_USE_COUNT で減算し、 最後に MOD_INC_USE_COUNT で 1 にしています。

  • 285 〜 294 行目

    struct file_opearations テーブルの ioctl タグに対して、新たに chardev_ioctl への関数ポインタを設定しています。これにより、ioctl システムコールが発行された際に chardev_ioctl 関数が実行されます。

以上で ioctl.c の解説は終わりです。キャラクタデバイスドライバとしての内容はひとまず終了して、次回からは他の話題を取り上げたいと思います。