/proc/interruptsに関するメモ

この記事はぴょこりんクラスタ: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 APICTSCの組み合わせであったり、 HPETやらACPIやらのCPU外のデバイスについてる時計だったりする。昨今はLocal APICTSCの組み合わせでやることが多い。

  • 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文字のものについて調べた。 わからない項目があっても、最悪ソースを読めばわかるというのはオープンソースのありがたいところですね。

*1:Interrupt ReQuest。どのハードウェアが割り込みを上げてきたかを識別するためのIDのようなもので、マザーボードごとに固有のものが振られている。

*2:らしい。具体的にはわからないが・・・

*3:IPI:Inter Processor Interrupt。コア間で飛ばしあう割り込みのこと

*4:割り込みコントローラのこと。各コアに1つずつついてる

*5:立ち上げのときとか障害発生したとき

*6:論物アドレス変換のキャッシュを捨てる

*7:CMCI:Correctable Machine Check Interrupt。エラー発生したけどなんとかなったやつ。