この記事はぴょこりんクラスタ:Re Advent Calendar 2016 - Adventarのために書いたものです。
サマリ
特に意味もなくLinuxの/proc/interruptsのアルファベット3文字の項目について説明する。
割り込みとは何か
処理を強制的に切り替える信号のようなもの。例えばキーボードのように、 入力に対する処理に即時性が必要だけど、全力でポーリングするのには割に合わないものに使われている。 ドライブやEthernetへのI/Oも普通は割り込みで動くんだけど(常にI/Oするわけじゃないし)、 なお、全力でポーリングすることでI/O性能を最大限出そうというのがSPDKとかDPDKっていうやつね。
割り込みが上がってくると、OSが割り込みの種類に合わせて処理をする。この処理のことを割り込みハンドラと呼ぶ。 割り込みハンドラはベクタと呼ばれるテーブルでOSのカーネルが管理している。 どのIRQ*1で 上がってきたら何番のベクタに登録されてるハンドラを使う、という運用になっている。
Linuxでの割り込みの取り扱い
各割り込みに仮想的なIRQを割り当ててハンドラを管理している。何でこんな複雑なことをするかというと、 多分このへんの理由なんだろうなと思う。あくまで思うだけで正解はわからない・・・
- 割り込みベクタの数が限られているから(たった255しかない)
- 同じベクタで複数のハンドラを動かしたいから(1つのベクタに対しては1つのハンドラしか登録できない)
で、/proc/interruptsとは何ですかね
Linuxにおいて、どういう割り込みがどれだけ上がっているかを調べられる特殊なファイルのこと。
/proc/interruptsの例
Virtualboxに入れたCentOSから見たときの/proc/interruptsはこんな感じ。 先頭列が数字のものとアルファベット3文字のがあるのがわかると思う。 数字はLinuxが管理している仮想的なIRQ番号を示していて、 アルファベット3文字のものはちょっと特別なやつを示している。 特別といってもハードウェア的には割り込みの上げ方に差はなくて、 特別なIRQが割り当てられているとか、特別なベクタが割り当てられているとかです。
/home/bisco/work/mem% cat /proc/interrupts CPU0 0: 127 IO-APIC-edge timer 1: 10 IO-APIC-edge i8042 8: 1 IO-APIC-edge rtc0 9: 0 IO-APIC-fasteoi acpi 12: 155 IO-APIC-edge i8042 14: 0 IO-APIC-edge ata_piix 15: 8285 IO-APIC-edge ata_piix 19: 42955 IO-APIC-fasteoi enp0s3 20: 3 IO-APIC-fasteoi vboxguest 21: 0 IO-APIC-fasteoi snd_intel8x0 23: 0 IO-APIC-fasteoi ohci_hcd:usb1 24: 14746 PCI-MSI-edge 0000:00:1f.2 NMI: 0 Non-maskable interrupts LOC: 281405 Local timer interrupts SPU: 0 Spurious interrupts PMI: 0 Performance monitoring interrupts IWI: 5644 IRQ work interrupts RTR: 0 APIC ICR read retries RES: 0 Rescheduling interrupts CAL: 0 Function call interrupts TLB: 0 TLB shootdowns TRM: 0 Thermal event interrupts THR: 0 Threshold APIC interrupts DFR: 0 Deferred Error APIC interrupts MCE: 0 Machine check exceptions MCP: 28 Machine check polls ERR: 0 MIS: 0 PIN: 0 Posted-interrupt notification event PIW: 0 Posted-interrupt wakeup event
アルファベット3文字のものについてざっくりとした解説
NMI:割り込みの世界では受取拒否のことを「割り込みをマスクする」と言うんだけど、文字通り受取拒否ができないもの。 0除算とかページフォルトとか、そういう緊急事態に上がってくる割り込み。
LOC:タイマ割り込み。それっぽい言葉でtickと言って、OSが時間管理に使う割り込みのこと。タイマで定期的に割り込みを上げてもらって、 時限処理とか、プロセスのスケジューリングをやったりする。割り込みを上げるのはLocal APICとTSCの組み合わせであったり、 HPETやらACPIやらのCPU外のデバイスについてる時計だったりする。昨今はLocal APICとTSCの組み合わせでやることが多い。
SPU:割り込み発生元がわからない割り込みのこと。(たぶん)ハードウェアが壊れたときとか設定を間違えたときに上がる割り込み。
PMI:Intel CPUにはPerformance Counter(PMC)を積んでるものがあって、クロック数やらキャッシュミス回数をカウントしてくれる。 そのカウンタが一定以上になったときに割り込みを上げる機能があって、それのこと。
IWI:割り込みハンドラを動かしているときは、CPUはちょっと特別なモードになっているんだけど、その特別なモードで割り込みじゃない処理を動かしたい ときがある*2。そのときにカウントされるのがこれ。 自分で自分に割り込み*3を打ち込んで動かすようである。
// Linux/arch/x86/kernel/irq_work.c 35 void arch_irq_work_raise(void) 36 { 37 #ifdef CONFIG_X86_LOCAL_APIC 38 if (!arch_irq_work_has_interrupt()) 39 return; 40 41 apic->send_IPI_self(IRQ_WORK_VECTOR); ★これ 42 apic_wait_icr_idle(); 43 #endif 44 }
- RTR:割り込みを上げようとして、Local APIC*4の ICR(Interrupt Command Register)を読んだときにbusyが返ってきたらリトライするんだけど、そのbusyが返ってきた回数をカウントしてる項目。 これ割り込みじゃないっぽいんだよなー。なお、このルートに入るのはNMI指定でIPIを投げつけたとき*5に限る。
// http://lxr.free-electrons.com/source/arch/x86/kernel/apic/apic.c#L250 250 u32 native_safe_apic_wait_icr_idle(void) 251 { 252 u32 send_status; 253 int timeout; 254 255 timeout = 0; 256 do { 257 send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; 258 if (!send_status) 259 break; 260 inc_irq_stat(icr_read_retry_count); ★ここでRTRがカウントされる 261 udelay(100); 262 } while (timeout++ < 1000); 263 264 return send_status; 265 }
RES:他のコアへスケジューリング要求を出すときの割り込み。
CAL:他のコアに何か関数コールしてほしいときに上げる割り込み。
TLB:TLB Shootdown*6のための割り込み。 特別枠になってるけど、実装はCALと同じでFunction Call Interruptsになっている。
TRM:CPUの温度が上がりすぎてるときに上がる割り込み。
THR:コレクタブルなエラー*7が発生したときに上がる割り込み
DFR:何かエラーが発生して、ハードウェアは何とかできなかったけど、ソフトウェアで何かする必要はないときに上がる割り込み。
MCE:Machine Check Exception。とにかくやばい障害。
MCP:定期的にMachine Checkが発生していないかを調べる割り込み。デフォルト5分だったかな。
ERR:Local APIC自体の障害
MIS:IOAPICの障害
PIN/POS:VMならではのもの。VMが割り込みを発生させるだけさせといて、VT-dが処理を遅らせたときに発生するようなやつ。
まとめ
Linuxの/proc/interruptsの、特にアルファベット3文字のものについて調べた。 わからない項目があっても、最悪ソースを読めばわかるというのはオープンソースのありがたいところですね。