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() を使わなくて、ポインタや配列を操作して独自にコピーしても よい。