/dev/nullと/dev/zero


Rev.1を表示中。最新版はこちら

/dev/null、/dev/zeroは、メジャー番号1のマイナ番号がそれぞれ3、5の、デバイス名をmemとするキャラクタデバイスです。
[root@localhost kitamura]# cat /proc/devices | grep mem
 1 mem

crw-r----- 1 root kmem      1,   1  7月  2 01:15 mem
crw-rw-rw- 1 root root      1,   3  7月  2 01:15 null
crw-r----- 1 root kmem      1,   4  7月  2 01:15 port
crw-rw-rw- 1 root root      1,   5  7月  2 01:15 zero
crw-rw-rw- 1 root root      1,   7  7月  2 01:15 full
crw-rw-rw- 1 root root      1,   8  7月  2 01:15 random
crw-rw-rw- 1 root root      1,   9  7月  2 01:15 urandom
crw------- 1 root root      1,  11  7月  2 01:15 kmsg
crw------- 1 root root      1,  12  7月  2 01:15 oldmem
memデバイスはmemモジュールのchr_dev_init()のregister_chrdev()で、メジャー番号MEM_MAJOR=1、デバイス名をmem、file_operationをmemory_fopsとして登録し、device_create()でマイナー番号にもとずくデバイスに対し、devlist[]に対応して、それぞれのデバイスを設定しています。
static int __init chr_dev_init(void)
{
       int minor;
       int err;

       err = bdi_init(&zero_bdi);
       if (err)
               return err;

       if (register_chrdev(MEM_MAJOR, "mem", &memory_fops))
               printk("unable to get major %d for memory devs\n", MEM_MAJOR);

       mem_class = class_create(THIS_MODULE, "mem");
       if (IS_ERR(mem_class))
               return PTR_ERR(mem_class);

       mem_class->devnode = mem_devnode;
       for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) {
               if (!devlist[minor].name)
                       continue;
               device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
                             NULL, devlist[minor].name);
       }

       return tty_init();
}

static const struct memdev {
       const char *name;
       umode_t mode;
       const struct file_operations *fops;
       struct backing_dev_info *dev_info;
} devlist[] = {
        [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
#ifdef CONFIG_DEVKMEM
        [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
#endif
        [3] = { "null", 0666, &null_fops, NULL },
#ifdef CONFIG_DEVPORT
        [4] = { "port", 0, &port_fops, NULL },
#endif
        [5] = { "zero", 0666, &zero_fops, &zero_bdi },
        [7] = { "full", 0666, &full_fops, NULL },
        [8] = { "random", 0666, &random_fops, NULL },
        [9] = { "urandom", 0666, &urandom_fops, NULL },
       [11] = { "kmsg", 0, &kmsg_fops, NULL },
#ifdef CONFIG_CRASH_DUMP
       [12] = { "oldmem", 0, &oldmem_fops, NULL },
#endif
};
/dev/nullのfile_operationコールバックは、null_fopsとなります。
static const struct file_operations null_fops = {
       .llseek         = null_lseek,
       .read           = read_null,
       .write          = write_null,
       .splice_write   = splice_write_null,
};
/dev/nullの読み込みはread_null()です。これは返値として0を返すだけです。この返り値は、読み込んだバイト数となり、従って読み込むデータが無かったことになります。ユーザプロセスとしてはEOFとなるわけです。
static ssize_t read_null(struct file *file, char __user *buf,
                        size_t count, loff_t *ppos)
{
       return 0;
}
書き込み処理においては、書き込むバイト数を返しているだけです。ユーザプロセスとしては正常に書き込まれたことになるわけで、従って単に、その出力を捨てることになるわけです。
static ssize_t write_null(struct file *file, const char __user *buf,
                         size_t count, loff_t *ppos)
{
       return count;
}
/dev/zeroのfile_operationコールバックは、zero_fopsとなります。
static const struct file_operations zero_fops = {
       .llseek         = zero_lseek,
       .read           = read_zero,
       .write          = write_zero,
       .mmap           = mmap_zero,
};
/dev/zeroの読み込みはread_zero()です。ここでの処理は__clear_user()で、ユーザプロセスで指定されたbufを0クリアし、クリアしたサイズ(通常、引数で渡されたcount)を返します。なおクリア処理においてページ単位でクリアし、その都度signal_pending()でシグナル受信チェックの処理を行っています。ddコマンドでディスク全体をクリアするような巨大な領域を処理する場合等で、途中で処理を中断するケースを想定してのこと思われます。
static ssize_t read_zero(struct file *file, char __user *buf,
                        size_t count, loff_t *ppos)
{
       size_t written;

       if (!count)
               return 0;

       if (!access_ok(VERIFY_WRITE, buf, count))
               return -EFAULT;

       written = 0;
       while (count) {
               unsigned long unwritten;
               size_t chunk = count;

               if (chunk > PAGE_SIZE)
                       chunk = PAGE_SIZE;      /* Just for latency reasons */
               unwritten = __clear_user(buf, chunk);
               written += chunk - unwritten;
               if (unwritten)
                       break;
               if (signal_pending(current))
                       return written ? written : -ERESTARTSYS;
               buf += chunk;
               count -= chunk;
               cond_resched();
       }
       return written ? written : -EFAULT;
}
書き込みは、write_zero()ですが、#defineでwrite_null()に差し替えています。従って/dev/zeroと/dev/nullの書き込み処理は同じとなるわけです。
#define write_zero write_null

補足

/dev/nullの読み込みのは、EOF時の動作をチェックするためでないかと思いますが・・・

最終更新 2012/07/01 17:32:54 - north
(2012/07/01 17:32:54 作成)


検索

アクセス数
3585450
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。