割り込み処理

					2012年02月07日
情報科学類 オペレーティングシステム II

                                       筑波大学 システム情報工学研究科 
                                       コンピュータサイエンス専攻, 電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2011/2012-02-07
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/

■今日の大事な話

■割り込み

◆割り込みの必要性

◆割り込みとは

◆x86の割り込みコントローラ

◆Intel 8259 PIC

8259、8259、CPU

図? x86 の Intel 8259

◆APIC

Local APIC、Core、I/O APIC

図? x86 の APIC

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

◆例外

例外(exceptions) とは、ハードウェア・デバイスとは無関係に、 CPU の命令実行の途中で生じる。例 多くの CPU アーキテクチャでは、例外を割り込みと同じように扱う。 x86 では、システム・コールの処理も、割り込みと同じように扱う。

◆割り込みハンドラ

割り込みハンドラ(interrupt handlers)とは、割り込みが生じた時にそれに応 答するために実行される関数。割り込みサービスルーチン(interrupt service routine, ISR)とも呼ばれる。

例:

Linux では、割り込みハンドラは、C言語の関数。普通の関数との違い。

◆割り込み記述子テーブル

割り込みハンドラは、割り込み記述子テーブル(interrupt descriptor tables)や 割り込みベクタテーブル(interrupt vector table)と呼ばれる表に登録されて いる。(CPUの違い、同じCPU x86でもモードの違いで、テーブルの名前が異なる。)

割り込み番号、割り込み記述子テーブル、割り込みハンドラ

図? 割り込み記述子テーブルと割り込みハンドラ

◆割り込みの前半部分と後半部分

次の2つを両立させたい Linux では、これを両立するために、割り込みの処理を2つに分ける。 例: ネットワークカードによるメッセージの受信

◆割り込み番号の共有

割り込み信号線が足りない時に、1つの割り込み番号を、複数のデバイスで共有 することがある。

デバイス5個、OR回路、PIC、CPU

図? PICの線が不足した時の対応

■Linuxにおける割り込みハンドラの登録

Linux で、割り込みの前半部を登録するには、request_irq() を使う。 登録を解除するには、free_irq() を使う。

◆request_irq()

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)
主なフラグ
IRQF_DISABLED
割り込みハンドラの実行中、「全ての」割り込みを禁止する。 このフラグがセットされていなければ、自分自身だけ禁止され、 他の割り込みは許可された状態で動作する。
IRQF_SAMPLE_RANDOM
乱数生成器のために割り込みを利用してもよい。
IRQF_TIMER
システム・タイマのために利用する。
IRQF_SHARED
複数のデバイスで同じ割り込み番号を共有できる。

◆free_irq()

割り込みハンドラが不要になった時には、free_irq() で取り除く。
void free_irq(unsigned int, void *dev)

◆irq_handler_t handler

割り込みハンドラは、次のような関数である。
irqreturn_t irq_handler_t(int irq, void *dev)
IRQ_NONE
割り込みは、自分自身のものではなかった。
IRQ_HANDLED
割り込みをきちんと処理した。
全割り込みハンドラが IRQ_NONE を返せば、なんらかのトラブルを意味する。 複数の割り込みハンドラが IRQ_HANDLED を返すことは正常。

◆/proc/interrupts

/proc/interrupts は、割り込みの回数を保持している。
$ cat /proc/interrupts  [←]
           CPU0       CPU1       
  0:    4208761      38584    IO-APIC-edge  timer
  1:          0          3    IO-APIC-edge  i8042
  7:          0          0    IO-APIC-edge  parport0
  8:          1          2    IO-APIC-edge  rtc
  9:          0          0   IO-APIC-level  acpi
 12:          3          1    IO-APIC-edge  i8042
 50:       5380      86508         PCI-MSI  ahci
 74:        346          0         PCI-MSI  HDA Intel
 98:        294      28232         PCI-MSI  eth1
169:        130      57006   IO-APIC-level  uhci_hcd:usb3
177:          0          0   IO-APIC-level  uhci_hcd:usb4, uhci_hcd:usb7
217:        358     149530   IO-APIC-level  ehci_hcd:usb1, uhci_hcd:usb5
225:          0          0   IO-APIC-level  ehci_hcd:usb2, uhci_hcd:usb6
233:          0          0   IO-APIC-level  uhci_hcd:usb8
NMI:          0          0 
LOC:    4246864    4246863 
ERR:          0
MIS:          0
$ []

◆x86 CMOS Real-Time Clock rtc_interrupt()

x86 CMOS Real-Time Clock での割り込みハンドラの例。
linux-3.1.3/drivers/char/rtc.c

  96:	static unsigned long rtc_port;
  97:	static int rtc_irq;
...
 191:	static unsigned long rtc_status;        /* bitmapped status byte.       */
 192:	static unsigned long rtc_freq;          /* Current periodic IRQ rate    */
 193:	static unsigned long rtc_irq_data;      /* our output to the world      */
...

 953:	static int __init rtc_init(void)
 954:	{
...
1000:	        if (request_irq(rtc_irq, rtc_interrupt, IRQF_SHARED, "rtc",
1001:	                        (void *)&rtc_port)) {
1002:	                rtc_has_irq = 0;
1003:	                printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq);
1004:	                return -EIO;
1005:	        }
...
1137:	}

 239:	static irqreturn_t rtc_interrupt(int irq, void *dev_id)
 240:	{
...
 248:	        spin_lock(&rtc_lock);
 249:	        rtc_irq_data += 0x100;
 250:	        rtc_irq_data &= ~0xff;
...
 259:	                rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
...

 262:	        if (rtc_status & RTC_TIMER_ON)
 263:	                mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
 264:	
 265:	        spin_unlock(&rtc_lock);
 266:	
 267:	        /* Now do the rest of the actions */
 268:	        spin_lock(&rtc_task_lock);
 269:	        if (rtc_callback)
 270:	                rtc_callback->func(rtc_callback->private_data);
 271:	        spin_unlock(&rtc_task_lock);
 272:	        wake_up_interruptible(&rtc_wait);
 273:	
 274:	        kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
 275:	
 276:	        return IRQ_HANDLED;
 277:	}

■Linuxにおける割り込みハンドラの実行

ハードウェア依存のコードからハードウェア独立のコードを呼び出す。

◆x86 Interrupt Descriptor Table (IDT)

x86 IDT は、ハードウェア・レベルの割り込みハンドラの一覧表を保持する。 その先頭番地は、IDTR レジスタに保存される。
linux-3.1.3/arch/x86/include/asm/irq_vectors.h
arch/x86/include/asm/irq_vectors.h
 133:	#define NR_VECTORS                       256

linux-3.1.3/arch/x86/kernel/traps.c
  78:	gate_desc idt_table[NR_VECTORS] __page_aligned_data = { { { { 0, 0 } } }, };

 824:	void __init trap_init(void)
 825:	{
...
 836:	        set_intr_gate(0, &divide_error);
 837:	        set_intr_gate_ist(2, &nmi, NMI_STACK);
...
 871:	        set_system_trap_gate(SYSCALL_VECTOR, &system_call);
...

linux-3.1.3/arch/x86/include/asm/irq_vectors.h
  51:	# define SYSCALL_VECTOR                 0x80

◆do_IRQ

x86 Linux では、Interrupt Descriptor Table に従い、do_IRQ() が呼ばれる。 引数には、x86 のレジスタを表現した構造体へのポインタが渡される。これに より、割り込みが発生した時のレジスタの値がわかる。割り込みハンドラが有 効で、割り込み番号に登録されていれば、最終的には、handle_IRQ_event() と いう、CPU とは独立の割り込みハンドラが呼ばれる。

◆handle_IRQ_event()

linux-3.1.3/kernel/irq/handle.c

 167:	irqreturn_t handle_irq_event(struct irq_desc *desc)
 168:	{
 169:	        struct irqaction *action = desc->action;
 170:	        irqreturn_t ret;
 171:	
 172:	        desc->istate &= ~IRQS_PENDING;
 173:	        irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
 174:	        raw_spin_unlock(&desc->lock);
 175:	
 176:	        ret = handle_irq_event_percpu(desc, action);
 177:	
 178:	        raw_spin_lock(&desc->lock);
 179:	        irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
 180:	        return ret;
 181:	}
linux-3.1.3/kernel/irq/handle.c
 116:	irqreturn_t
 117:	handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
 118:	{
 119:	        irqreturn_t retval = IRQ_NONE;
 120:	        unsigned int random = 0, irq = desc->irq_data.irq;
 121:	
 122:	        do {
 123:	                irqreturn_t res;
...
 126:	                res = action->handler(irq, action->dev_id);
...
 133:	                switch (res) {
...
 147:	                case IRQ_HANDLED:
 148:	                        random |= action->flags;
 149:	                        break;
 150:	
 151:	                default:
 152:	                        break;
 153:	                }
 154:	
 155:	                retval |= res;
 156:	                action = action->next;
 157:	        } while (action);
...
 164:	        return retval;
 165:	}

■割り込みハンドラのプログラミングの注意点

◆割り込みコンテキスト

割り込みハンドラが実行されるのは、「割り込みコンテキスト」。 普通のシステム・コールの処理が実行される「プロセス・コンテキスト」とは 異なる。

プロセス・コンテキストでできること。

割り込みコンテキストでは、このうようなことはできない。 速やかに終了すべきである。busy loop はできるが、あまり やらない方がよい。他の割り込みは、実行される可能性もある。

◆割り込みの許可・禁止

マクロをつかって保存したり回復したりする。

unsigned long flags;

local_irq_save(flags); /* 割り込み禁止 */
...
local_irq_restore(flags); /* 割り込み許可 (save の時の状態にもどる) */

単一CPUの x86 では、cli() と sti() で割り込みの禁止と許可を設定する方法 があった。それそれ同名の CPU の命令を実行して、全ての割り込みを禁止/許 可する。マルチプロセッサ(マルチコア含む)では、1つのCPU で割り込みを禁止 しても、他の CPU では許可されていることがあるので、cli()/sti() の方法は 使えない。

特定の割り込み番号の割り込みを禁止する方法もある。

void disable_irq(unsigned ing irq);
    // 全CPUの割り込みを禁止する

void disable_irq_nosync(unsigned ing irq);
    // 同上。ただし、割り込みハンドラの終了を待たない。

void enable_irq(unsigned ing irq);
    // 割り込みを許可する。

void synchronize_irq(unsigned ing irq);
    // 割り込みハンドラの終了を待つ。

■クイズ7 割り込み処理

★問題(701) 割り込みの利用

キーボード、マウス、ネットワーク・カード等のデバイスからの入力では割り 込みが多く使われている。割り込みを使う方法と割り込みを使わない方法を対 比して、割り込みを使う方法の利点を説明しなさい。

★問題(702) x86 CMOS Real-Time Clock の割り込みハンドラ

x86 CMOS Real-Time Clock の割り込みハンドラを関数名で答えなさい。 その関数の引数と結果を、簡単に説明しなさい。

★問題(703) x86 CMOS Real-Time Clock の割り込みハンドラの呼び出し

今日の資料の中で、x86 CMOS Real-Time Clock の割り込みハンドラを呼び出し ていると思われる関数と行数を答えなさい。
Last updated: 2012/02/07 18:01:58
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>