2010年02月16日 情報科学類 オペレーティングシステム II 筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/literacy-2009/2010-02-16
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/
/dev
以下のファイルをアクセスする。
% df /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/cciss/c0d0p2 30470176 6222136 22675280 22% /
% ls -l /dev/cciss/c0d0p2
brw-r----- 1 root disk 104, 2 Jan 8 21:25 /dev/cciss/c0d0p2
% ls -l /dev/random
crw-rw-rw- 1 root root 1, 8 Jan 8 21:24 /dev/random
%
ls -l で見ると、ブロック型は、b
、文字型は、c
で始まる。メ
ジャー番号は、デバイスの種類、マイナー番号は、同じ種類で、細かい違い
(上の例では、パーティション)等を意味する。
メジャー番号は、静的に決めうちにすることもあるが、
alloc_chrdev_region() を呼び、動的に割り当てられることもできる。使われ
ているメジャー番号は、/proc/devices
に現れる。
% cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
...
253 hpilo
254 pcmcia
Block devices:
1 ramdisk
8 sd
9 md
65 sd
...
104 cciss0
...
253 device-mapper
254 mdp
%
/dev/ の下にあるブロック型と文字型のファイルは、mknod コマンド (make
node) で作ることができる。
% mknod b /dev/ファイル名 メジャー番号 マイナー番号
% mknod c /dev/ファイル名 メジャー番号 マイナー番号
struct file_operations my_fops = { .... }; struct cdev *my_cdevp = cdev_alloc(); my_cdev->ops = &my_fops; my_cdev->owner = &my_fops;
struct file_operations my_fops = { .... }; struct cdev my_cdev ; cdev_init(&my_cdev,&my_fops); cdev_add(&my_cdev,num, count)register_chrdev() という関数で登録することもできる。以前はこの方法が主。
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
include/linux/fs.h 1486: struct file_operations { 1487: struct module *owner; 1488: loff_t (*llseek) (struct file *, loff_t, int); 1489: ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 1490: ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 1491: ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); 1492: ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); 1493: int (*readdir) (struct file *, void *, filldir_t); 1494: unsigned int (*poll) (struct file *, struct poll_table_struct *); 1495: int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 1496: long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); 1497: long (*compat_ioctl) (struct file *, unsigned int, unsigned long); 1498: int (*mmap) (struct file *, struct vm_area_struct *); 1499: int (*open) (struct inode *, struct file *); 1500: int (*flush) (struct file *, fl_owner_t id); 1501: int (*release) (struct inode *, struct file *); 1502: int (*fsync) (struct file *, struct dentry *, int datasync); 1503: int (*aio_fsync) (struct kiocb *, int datasync); 1504: int (*fasync) (int, struct file *, int); 1505: int (*lock) (struct file *, int, struct file_lock *); 1506: ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); 1507: unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); 1508: int (*check_flags)(int); 1509: int (*flock) (struct file *, int, struct file_lock *); 1510: ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); 1511: ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); 1512: int (*setlease)(struct file *, long, struct file_lock **); 1513: };主な手続きの意味
デバイス・ファイルでは、メジャー番号とマイナー番号を取り出すために使わ れる。
int fd1 = open("file1"); int fd2 = open("file1");ファイル名 "file1" で表現されるファイルの inode 構造体は、1 個でも、 file 構造体は、2 個割り当てられる。
1: 2: /* 3: random-get.c -- open /dev/random and read a random number. 4: ~yas/syspro/file/random-get.c 5: Created on: 2010/02/15 17:44:07 6: */ 7: 8: #include <stdio.h> 9: #include <sys/types.h> /* open() */ 10: #include <sys/stat.h> /* open() */ 11: #include <fcntl.h> /* open() */ 12: #include <unistd.h> /* read() */ 13: 14: main() 15: { 16: long int rand; 17: int fd; 18: if( (fd = open("/dev/random",O_RDONLY)) < 0 ) 19: { 20: perror("/dev/random"); 21: exit( 1 ); 22: } 23: if( read( fd,&rand,sizeof(rand) ) != sizeof(rand) ) 24: { 25: perror("/dev/random"); 26: exit( 1 ); 27: } 28: printf("%x\n", rand ); 29: }実行例
% make random-get
cc -g random-get.c -o random-get
% ls -l /dev/random
crw-rw-rw- 1 root root 1, 8 Jan 8 21:24 /dev/random
% ./random-get
3cfa18eb
% ./random-get
c32e41ea
% ./random-get
8409ee78
% ./random-get
96a9e4b3
%
drivers/char/mem.c 866: static const struct { 867: unsigned int minor; 868: char *name; 869: umode_t mode; 870: const struct file_operations *fops; 871: struct backing_dev_info *dev_info; 872: } devlist[] = { /* list of minor devices */ ... 885: {8, "random", S_IRUGO | S_IWUSR, &random_fops, NULL}, ... 891: }; ... 922: static const struct file_operations memory_fops = { 923: .open = memory_open, /* just a selector for the real open */ 924: }; ... 893: static int memory_open(struct inode *inode, struct file *filp) 894: { 895: int ret = 0; 896: int i; ... 900: for (i = 0; i < ARRAY_SIZE(devlist); i++) { 901: if (devlist[i].minor == iminor(inode)) { 902: filp->f_op = devlist[i].fops; ... 908: break; 909: } 910: } ... 915: if (filp->f_op && filp->f_op->open) 916: ret = filp->f_op->open(inode, filp); ... 919: return ret; 920: } ... 928: static int __init chr_dev_init(void) 929: { ... 937: if (register_chrdev(MEM_MAJOR,"mem",&memory_fops)) ... 947: }
drivers/char/random.c 1161: const struct file_operations random_fops = { 1162: .read = random_read, 1163: .write = random_write, 1164: .poll = random_poll, 1165: .unlocked_ioctl = random_ioctl, 1166: .fasync = random_fasync, 1167: }; 987: static ssize_t 988: random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) 989: { 990: ssize_t n, retval = 0, count = 0; 991: 992: if (nbytes == 0) 993: return 0; 994: 995: while (nbytes > 0) { 996: n = nbytes; ... 1002: n = extract_entropy_user(&blocking_pool, buf, n); ... 1007: if (n == 0) { ... 1015: wait_event_interruptible(random_read_wait, 1016: input_pool.entropy_count >= 1017: random_read_wakeup_thresh); ... 1026: continue; 1027: } 1028: 1029: if (n < 0) { 1030: retval = n; 1031: break; 1032: } 1033: count += n; 1034: buf += n; 1035: nbytes -= n; 1036: break; /* This break makes the device work */ 1037: /* like a named pipe */ 1038: } ... 1046: return (count ? count : retval); 1047: } 874: static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, 875: size_t nbytes) 876: { 883: while (nbytes) { ... 893: extract_buf(r, tmp); 894: i = min_t(int, nbytes, EXTRACT_SIZE); 895: if (copy_to_user(buf, tmp, i)) { 896: ret = -EFAULT; 897: break; 898: } 899: 900: nbytes -= i; 901: buf += i; 902: ret += i; 903: } ... 908: return ret; 909: }
カーネル空間とユーザ空間でデータをコピーする時には、次のような特殊な関 数を使う必要がある。
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)これらの関数は、コピーの途中でページフォールトが発生した時にもうまくコ ピーできる(ページインの処理でプロセスがスリープすることがある)。また、 引数の番地が有効かどうかをチェックする。
Linux では、x86 アーキテクチャでは、カーネル空間とユーザ空間が一部重なっ ていることがある。この場合、カーネルでmemcpy() を使ったり、直接ポインタ を操作してもユーザ空間がアクセスできてしまうが、それは誤りである。
図? x86 の Intel 8259
図? x86 の APIC
APIC は、次のような割り込み信号を受け取る。
% cat /proc/interrupts
CPU0
0: 3599685028 local-APIC-edge timer
1: 27624 IO-APIC-edge i8042
2: 0 XT-PIC cascade
8: 42 IO-APIC-edge rtc
12: 546018 IO-APIC-edge i8042
14: 32380268 IO-APIC-edge ide0
145: 0 IO-APIC-level i915@pci:0000:00:02.0
153: 19131822 IO-APIC-level eth0
161: 0 IO-APIC-level uhci_hcd
169: 2012268 IO-APIC-level libata
177: 0 IO-APIC-level ehci_hcd, uhci_hcd
185: 0 IO-APIC-level Intel ICH, uhci_hcd
193: 0 IO-APIC-level uhci_hcd
NMI: 0
LOC: 3599732928
ERR: 0
MIS: 0
%
arch/x86/kernel/traps.c 79: gate_desc idt_table[256] 80: __attribute__((__section__(".data.idt"))) = { { { { 0, 0 } } }, }; ... 911: void __init trap_init(void) 912: { 923: set_intr_gate(0, ÷_error); 924: set_intr_gate_ist(1, &debug, DEBUG_STACK); 925: set_intr_gate_ist(2, &nmi, NMI_STACK); ... 943: set_intr_gate(14, &page_fault); ... 974: set_system_trap_gate(SYSCALL_VECTOR, &system_call); ... 986: } arch/x86/include/asm/irq_vectors.h: 37:# define SYSCALL_VECTOR 0x80
arch/x86/kernel/i8259.c 308: void init_8259A(int auto_eoi) 309: { .... 322: outb_pic(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */ 324: /* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 on x86-64, 325: to 0x20-0x27 on i386 */ 326: outb_pic(IRQ0_VECTOR, PIC_MASTER_IMR); ... 360: } arch/x86/include/asm/irq_vectors.h 34:#define FIRST_EXTERNAL_VECTOR 0x20 52:#define IRQ0_VECTOR (FIRST_EXTERNAL_VECTOR + 0x10) arch/x86/include/asm/i8259.h: 14:#define PIC_MASTER_IMR 0x21
CPUアーキテクチャに独立した形で割り込みハンドラを登録するには、 request_irq() を用いる。
include/linux/interrupt.h typedef irqreturn_t (*irq_handler_t)(int, void *); request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
... ... memcpy( ..., ..., .... ); ...なお、memcpy() のインタフェースは、次のようになっている。
void * memcpy(void *destination, const void *source, size_t len);なお、memcpy() を使わなくて、ポインタや配列を操作して独自にコピーしても よい。