/dev/nullと/dev/zero
Rev.2を表示中。最新版はこちら。
/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 oldmemmemデバイスは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