levelX

Level6で作ったヌル・デバイスドライバを変更して、動くキャラクタデバイスド ライバを作成せよ。

デバイスドライバを作成

open/close/read/writeシステムコールに対応したキャラクタデバイスを 作成します。 chardev.c

chardev.cプログラム
/*必要なヘッダファイルをインクルードしています。*/
#include     /* copy_from_user, copy_to_user */
#include 
#include        /* inode, file, file_operations */
#include    /* printk */
#include 
#include 

...

/*今回はメジャー番号を242番とします。*/
static int   devmajor = 242;

...

/*ユーザプロセスから書き込まれたデータを保存し、ユーザプロセスから読み込
める*/
/*様にするための内部バッファ32バイトを宣言しています。*/
#define MAXBUF 32
static unsigned char devbuf[ MAXBUF ];
static int buf_pos;

...

/*
 * open()
 * 1ユーザプロセスのみがデバイスファイルの open に成功します。
 *
 * ユーザプロセスからのopenに対応する関数です。linux/fs.h内の
 * struct file_operations のメンバであるopen関数ポインタの形式で
 * 定義する必要があります。成功時には0を、何らかのエラー終了時
 * にはlinux/errno.h内の適切なエラー番号を選び、負の数値に変換して
 * 返却しています。
 */
static int
chardev_open( struct inode* inode, struct file* filp )
{
        printk( KERN_INFO "%s : open()  called\n", msg );

        spin_lock( chardev_spin_lock );

        if ( access_num ) {
                spin_unlock( chardev_spin_lock );
                return -EBUSY;
        }

        access_num ++;
        spin_unlock( chardev_spin_lock );

        return 0;
}

/*
 * release()
 * 使用ユーザプロセス数をクリアします。
 *
 *ユーザプロセスとデバイスドライバの関連付けが解放される際に
 *に実行されます。一般的にはユーザプロセスがcloseを実行した際
 * に実行されますが、ユーザプロセスがエラー終了してしまった際
 * などにも実行されることで、資源が解放されます.
 */
static int
chardev_release( struct inode* inode, struct file* filp )
{
        printk( KERN_INFO "%s : close() called\n", msg );

        spin_lock( chardev_spin_lock );
        access_num --;
        spin_unlock( chardev_spin_lock );

        return 0;
}

/*
 * read()
 * ユーザプロセスに対して内部バッファの内容を転送します。
 * 転送した内容は内部バッファから消えます。
 */

static ssize_t
chardev_read( struct file* filp, char* buf, size_t count, loff_t* pos )
{
        int copy_len;
        int i;

        printk( KERN_INFO "%s : read()  called\n", msg );

        if ( count > buf_pos )
                copy_len = buf_pos;
        else
                copy_len = count;

/*bufにコピー先アドレス、devbufにコピー元アドレスが指定されています。*/
/*戻り値はコピーされていないバイト数であるため、正常に終了した場合は0*/
/*が帰ります.*/
        if ( copy_to_user( buf, devbuf, copy_len ) ) {
                printk( KERN_INFO "%s : copy_to_user failed\n", msg );
                return -EFAULT;
        }

/*コピーした後は、コピーしたデータ長に応じて内部バッファ内の*/
/*データを先頭につめています。*/

        *pos += copy_len;

        for ( i = copy_len; i < buf_pos; i ++ )
                devbuf[ i - copy_len ] = devbuf[i];

        buf_pos -= copy_len;
        printk( KERN_INFO "%s : buf_pos = %d\n", msg, buf_pos );

        return copy_len;
}

/*
 * write()
 * ユーザプロセスから転送された内容を内部バッファに書き込みます。
 */
static ssize_t
chardev_write( struct file* filp, const char* buf, size_t count, loff_t*
pos )
{
        int copy_len;

        printk( KERN_INFO "%s : write() called\n", msg );

        if ( buf_pos == MAXBUF ) {
                printk( KERN_INFO "%s : no space left\n", msg );
                return -ENOSPC;
        }

        if ( count > ( MAXBUF - buf_pos ) )
                copy_len = MAXBUF - buf_pos;
        else
                copy_len = count;

/*ここでは、単にbufからコピーするわけには行きませんので、           */
/*copy_from_userという関数を使います。
*/
/*devbuf + buf_posにコピー先アドレス、bufにコピー元アドレスを    */
/*指定します。また、copy_lenにコピーするバイト数を指定します。*/
        if ( copy_from_user( devbuf + buf_pos, buf, copy_len ) ) {
                printk( KERN_INFO "%s : copy_from_user failed\n", msg );
                return -EFAULT;
        }

        *pos    += copy_len;
        buf_pos += copy_len;

        printk( KERN_INFO "%s : buf_pos = %d\n", msg, buf_pos );

        return copy_len;
}

/*モジュール内の各関数とユーザプロセスからの操作を関連づけています。*/
/*今回は、readとwrite、open、releaseの4つを使っていますので4つ関連付け*/
/*をしています。*/

static struct file_operations chardev_fops =
{
        owner   : THIS_MODULE,
        read    : chardev_read,
        write   : chardev_write,
        open    : chardev_open,
        release : chardev_release,
};

...
...

/* End of chardev.c */

カーネルに登録

上で少し書きましたが、モジュール内の各関数とユーザプロセスからの操作を 関連づけるためには、
linux/fs.h内のstruct file_operations型構造体を作成し、 カーネルに登録する必要があります。
カーネル2.4.27のlinux/fs.hでは以下の様に定義されています。
884:struct file_operations {
885-    struct module *owner;
886-    loff_t (*llseek) (struct file *, loff_t, int);
887-    ssize_t (*read) (struct file *, char *, size_t, loff_t *);
888-    ssize_t (*write) (struct file *, const char *, size_t, loff_t
*);
889-    int (*readdir) (struct file *, void *, filldir_t);
890-    unsigned int (*poll) (struct file *, struct poll_table_struct
*);
891-    int (*ioctl) (struct inode *, struct file *, unsigned int,
unsigned long);
892-    int (*mmap) (struct file *, struct vm_area_struct *);
893-    int (*open) (struct inode *, struct file *);
894-    int (*flush) (struct file *);
895-    int (*release) (struct inode *, struct file *);
896-    int (*fsync) (struct file *, struct dentry *, int datasync);
897-    int (*fasync) (int, struct file *, int);
898-    int (*lock) (struct file *, int, struct file_lock *);
899-    ssize_t (*readv) (struct file *, const struct iovec *, unsigned
long, loff_t *);
900-    ssize_t (*writev) (struct file *, const struct iovec *, unsigned
long, loff_t *);
901-    ssize_t (*sendpage) (struct file *, struct page *, int, size_t,
loff_t *, int);
902-    unsigned long (*get_unmapped_area)(struct file *, unsigned long,
unsigned long, unsigned long, unsigned long);
903-};
追加方法はLevel6で書きましたので省略します。
追加された証に、
[root@pw022 j03018]% cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 ttyS
  5 cua
  7 vcs
 10 misc
 14 sound
128 ptm
136 pts
162 raw
180 usb
226 drm
241 newnull2
242 chardev

Block devices:
  2 fd
  3 ide0
 22 ide1
となっています。

実行結果

作成したキャラクタデバイスを動かしてみます。
[root@pw022 ~]% echo "HELLO WORLD" > /dev/chardev
として、次にchardevを見ると、
[root@pw022 ~]% cat /dev/chardev
HELLO WORLD

となります。なお、この/dev/chardevは、同時に一つのユーザプロセスしか 使えない様になっています。

最後に、Level6とLevelXは、新しく構築したLinux-2.7.27でのみ動きます。
よって、実験が終了次第元のバージョンを元に戻しましたので、新しく追加した キャラクタデバイスは今のpw022では使えません。


考察

今回のキャラクタデバイスは、同時アクセス可能プロセス数を1にしています。
これを複数にすると、同時にアクセスされると内部バッファの内容に不整合が生じる と思われます。
そうならないためには、内部バッファ操作の前後でロックを取得/解放する 必要があると思われます。


参考リンク

The LinuxKernel
http://www.linux.or.jp/JF/JFdocs/The-Linux-Kernel.html#toc9
Linux Kernel Module programming ( Kernel 2.4)
http://homepage3.nifty.com/rio_i/lab/driver24/

デバイスドライバを作成
カーネルに登録
実行結果
考察
参考リンク

level6 top