デバイスドライバ、割り込み

					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/

■今日の大事な話

■デバイス・ドライバ

デバイス・ドライバとは、 オペレーティング・システム・カーネル内で動作するモジュールで、ディスク やキーボード等のデバイス(周辺装置)を管理し、入出力を制御する。

◆Unix系OSのデバイス・ドライバの種類

◆メジャー番号とマイナー番号

ブロック型と文字型のデバイス・ドライバは、メジャー番号とマイナー番号の 2つの番号で区別される。利用者プログラムからそれらをアクセスする時には、 /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 構造体とその内部の関数群を定義する。その構 造体を、struct cdev に設定する。最後に、struct cdev を、cdev_add() で登 録する。
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);

◆file_operations構造体

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: };
主な手続きの意味

◆inode 構造体

Linux カーネル内で、1つのファイルを表現する構造体。

デバイス・ファイルでは、メジャー番号とマイナー番号を取り出すために使わ れる。

◆file構造体

開いたファイルを表現するために使われる。
    int fd1 = open("file1");
    int fd2 = open("file1");
ファイル名 "file1" で表現されるファイルの inode 構造体は、1 個でも、 file 構造体は、2 個割り当てられる。

◆random-get.c

/dev/random は、カーネル・レベルの乱数生成器に対応した文字型デバイスの ファイルである。これを開いて読み出すと乱数が得られる。
   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
% []

◆mem.c

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: }

◆random.cのrandom_read()

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: }

◆copy_from_user()とcopy_to_user()

デバイス・ドライバが動作するカーネル空間とユーザ空間では、「基本的に」 異なるアドレス空間を持っている。たとえば、同じ 1000 番地でも、別の内容 が含まれていることがある。

カーネル空間とユーザ空間でデータをコピーする時には、次のような特殊な関 数を使う必要がある。

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 PIC

8259、8259、CPU

図? x86 の Intel 8259

◆APIC

Local APIC、Core、I/O APIC

図? x86 の APIC

APIC は、次のような割り込み信号を受け取る。

◆/proc/interrupts

/proc/interrupts は、割り込みの回数を保持している。
% 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
% []

◆x86 Interrupt Descriptor Table (IDT)

x86 IDT は、ハードウェア・レベルの割り込みハンドラの一覧表を保持する。 その先頭番地は、IDTR レジスタに保存される。
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, &divide_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

◆init_8259A()

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

◆Linux の割り込みハンドラ

Linux は、アーキテクチャから独立した割り込み処理の仕組みを持つ。 x86 では、do_IRQ(), handle_irq() が、ハードウェア依存。その後、ハードウェ アから独立した struct irq_desc に含まれている手続きが呼ばれる。

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)

■クイズ9 デバイスドライバ、割り込み

★問題(901) random_read()

random.cのrandom_read()は、 random-get.cのどの部分が実行された 時に呼ばれるか。また、random_read()の引数と、 random-get.c の変数で対応するものを示しなさい。

★問題(902) copy_to_user()

random.cのrandom_read()は、下請け関数 extract_entropy_user() の中でcopy_to_user() を呼び出している。これは正 しいプログラムであるが、もしも copy_to_user() を memcpy() やポインタを 操作して直接ユーザ空間のメモリにアクセスすると問題がある。895 行目から 898 行目を、memcpy() 、または、直接ポインタを操作することで、「間違った プログラム」に書き換えなさい。以下の「...」の部分を適当に埋めなさい。
     ...
     ... memcpy( ..., ..., .... );
     ...
なお、memcpy() のインタフェースは、次のようになっている。
void * memcpy(void *destination, const void *source, size_t len);
なお、memcpy() を使わなくて、ポインタや配列を操作して独自にコピーしても よい。
Last updated: 2010/02/16 12:22:18
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>