情報学類 オペレーティングシステムII 2007年02月06日 筑波大学システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2006/2007-02-06
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/
アプリケーションからの使い方は、2種類
デバイス・ファイルは、2つの番号で区別される。
デバイス・ファイルは、主に、/dev
というディレクトリ以下に作られ
る(他の場所にも作れる)。
ls -l
で見ると、先頭に b
と表示される。
% ls -l /dev/disk*普通のファイルなら容量が表示される所に、メジャー番号(この例では14)とマ イナー番号(この例では0から10)が表示される。 ブロック型デバイスファイルは、主にファイルシステムをマウントする時に使う。brw-r----- 1 root operator 14, 0 Dec 26 13:57 /dev/disk0 br--r----- 1 root operator 14, 1 Dec 26 13:57 /dev/disk0s1 brw-r----- 1 root operator 14, 2 Dec 26 13:57 /dev/disk0s3 brw-r----- 1 root operator 14, 3 Dec 26 13:57 /dev/disk1 br--r----- 1 root operator 14, 4 Dec 26 13:57 /dev/disk1s1 br--r----- 1 root operator 14, 5 Dec 26 13:57 /dev/disk1s2 br--r----- 1 root operator 14, 6 Dec 26 13:57 /dev/disk1s3 br--r----- 1 root operator 14, 7 Dec 26 13:57 /dev/disk1s5 br--r----- 1 root operator 14, 8 Dec 26 13:57 /dev/disk1s6 br--r----- 1 root operator 14, 9 Dec 26 13:57 /dev/disk1s7 brw-r----- 1 root operator 14, 10 Dec 26 13:57 /dev/disk1s9 %
![]()
int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data);
ls -l
で見ると、先頭に c
と表示される。
% ls -l cu.Bluetooth-*crw-rw-rw- 1 root wheel 9, 3 Dec 26 13:57 cu.Bluetooth-Modem crw-rw-rw- 1 root wheel 9, 1 Dec 26 13:57 cu.Bluetooth-PDA-Sync %
![]()
int open(const char *pathname, int flags); int close(int fildes); ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); int ioctl(int fildes, int request, ... /* arg */);ioctl() の第2引数、第3引数は、デバイス依存。
伝統的な Unix (BSD含む)には、ブロック型デバイスに、対応する文字型デバイ スがある。これを「生のデバイス(raw device)」と呼ぶ。
生のデバイスでは、読み書きすると必ずハードウェアのレベルでも入出力が生 じる。ブロック型デバイスをアクセスすると、キャッシングが行われる。
生のデバイスの利用方法
生のデバイスのファイル名には、先頭に r を付ける習慣がある。
% ls -l /dev/{r,}disk0Linux には、「生のデバイス」の概念が希薄。明示的には存在しない。brw-r----- 1 root operator 14, 0 Dec 26 13:57 /dev/disk0 crw-r----- 1 root operator 14, 0 Dec 26 13:57 /dev/rdisk0 %
(MacOSX での例)
/dev/hd*
, SCSI /dev/sd*
,
/dev/fd*
/dev/cdrom
/dev/nrst0
/dev/tty0
/dev/fb
、キーボード /dev/kbd
、マウス /dev/mouse
/dev/mem
, /dev/kmem
対応するハードウェアが存在しない。Unix のカーネルがソフトウェア的に作り 出したもの。
/dev/ttyp0
と同じ記号と番号の /dev/ptyp0
が
対になっており、
/dev/ttyp0
に書き込まれたシェルなどの出力が、
/dev/ptyp0
を見ている sshd
や
xterm
に読み込まれる。
syslogd
が吸い上げる
/dev/random
, /dev/urandom
デバイス・ドライバ(device driver)は、カーネル内で動作するモジュール。 デバイス(ハードウェア)を操作して入出力を行う。
新しくハードウェアを開発したら、オペレーティング・システムで定めた仕様 に従ったデバイス・ドライバを開発する。
図? デバイス・ドライバとオペレーティング・システム本体のインタフェース
Linux で、文字型のデバイス・ドライバをカーネルに登録するには、 register_chrdev() という関数を使う。struct file_operations にある関数を 定義し、register_chrdev() を呼ぶ。
/* fs/char_dev.c */ int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops); /* linux/fs.h */ struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*dir_notify)(struct file *filp, unsigned long arg); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); };ファイルを開く時に、struct file の f_op というフィールドは、デバイスご とのstruct file_operations へポインタで初期化される。以後、 f->f_op->read(...) のようにアクセスされる。
/* drivers/char/random.c */ struct file_operations urandom_fops = { .read = urandom_read, .write = random_write, .ioctl = random_ioctl, }; static ssize_t urandom_read(struct file * file, char __user * buf, size_t nbytes, loff_t *ppos) { return extract_entropy_user(&nonblocking_pool, buf, nbytes); } static ssize_t random_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) { ... const char __user *p = buffer; size_t c = count; while (c > 0) { bytes = min(c, sizeof(buf)); bytes -= copy_from_user(&buf, p, bytes); if (!bytes) { ret = -EFAULT; break; } c -= bytes; p += bytes; add_entropy_words(&input_pool, buf, (bytes + 3) / 4); } if (p == buffer) { return (ssize_t)ret; } else { ... } } static int random_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { int size, ent_count; int __user *p = (int __user *)arg; int retval; switch (cmd) { case RNDGETENTCNT: ent_count = input_pool.entropy_count; if (put_user(ent_count, p)) return -EFAULT; return 0; case RNDADDTOENTCNT: ... default: return -EINVAL; } }
int register_blkdev(unsigned int major, const char *name) typedef struct request_queue request_queue_t; typedef void (request_fn_proc) (request_queue_t *q); request_queue_t *blk_init_queue(request_fn_proc *, spinlock_t *); struct request_queue { ... request_fn_proc *request_fn; ... };
blk_init_queue() で、request関数を登録する。オペレーティング・システム の上位層は、必要に応じてデバイス・ドライバの request 関数を呼び出す。
void __generic_unplug_device(request_queue_t *q) { ... q->request_fn(q); }入出力の記述には、struct bio を使う。仮想記憶と統合的に使える。
struct net_device { ... int (*init)(struct net_device *dev); // 統計 struct net_device_stats* (*get_stats)(struct net_device *dev); // 送信開始 int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev); void (*destructor)(struct net_device *dev); // ifconfig up int (*open)(struct net_device *dev); // ifconfig down int (*stop)(struct net_device *dev); // ヘッダを作る int (*rebuild_header)(struct sk_buff *skb); // MAC アドレスの変更 int (*set_mac_address)(struct net_device *dev, void *addr); // 送信失敗 void (*tx_timeout) (struct net_device *dev); };受信は、この関数の表にはない。割り込みから起動される。