INTERRUPT Nedir ?
~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~
Interrupt kelimesinin Türkçe karşılığı bilgisayar
terminolojisinde kesmeye karşılık gelmektedir.
Interrupt mekanizmasına ihtiyaç duymamızın sebeplerinden birisi sahip
olduğumuz çevresel cihazlar. Çevresel cihazlara örnek ; Klavye,Mouse,Printer.
Çevresel cihazlardan klavyeyi ele alalım. Klavyeden bir tuşa
basılıp basılmadığını işletim sistemi nasıl anlayabilir ? Bunun için 2 yöntem
var.
İşletim
sistemi , klavyeyi belirli aralıklarla dinleyerek, durumunu sorgulayarak bir
tuşa basılıp basılmadığını anlayabilir. Bu yönteme polling denilmektedir.Bu
yöntemin dezavantajlarından birisi belirli aralıklarla çevresel cihazların
dinlenmesidir ve ayrıca bu yöntem hardware tabanlı bir çözüm değil software tabanlı
bir çözümdür.
Klavyeden
herhangi bir tuşa basıldığı takdirde
işlemciye “klavyeden bir tuşa basıldı”
uyarısı gönderilebilir. Bu yöntem sayesinde klavyenin belirli
aralıklarla dinlenmesi gerekmez. Bu yönteme interrupt/kesme yöntemi
denilmektedir.Bu çözüm hardware tabanlı bir çözümdür. Hardware tabanlı çözümden
kastımız işlemciye sinyal gönderilmesidir.Bu
sinyal sayesinden dış cihaz tarafından kesme gerçekleştirilir. Bir
Interrupt gerçekleştiği takdirde işlemci uğraşmış olduğu işi bırakarak kesmeye
hizmet etmelidir. Örneğin, Bir proses
çalışırken kesme gerçekleştiği takdirde prosesi kaldığı yerden tekrar
çalıştırabilmek için prosese ilişkin bilgilerin korunması gereklidir.
Interrupt gerçekleştiği takdirde kesmeyi gerçekleştiren
olaya(yalnızca cihazlar değil başka sebeplerden dolayı da interruptlar
gerçekleşebilir. Bu yüzden olay demek sanırım daha doğru olacaktır. Çok uzun
bir parantez içi oldu farkındayım ) ilişkin programın çalıştırılması gerekir.
Bu programlara Interrupt Service Routine
veya Interrupt handler denilmektedir.
INTERRUPT ve EXCEPTION
~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~
Interruptlar genel
olarak 2 kısma ayrılmaktadır. Senkron Interruptlar ve Asenkron Interruptlar.
Senkron
Interruptlar işlemcinin kendisi tarafından bazı emirler çalıştırılırken oluşan
anormal durumlarda(sıfıra bölme,page faults ) meydana gelmektedir. Senkron interruptlar
intel terminolojisinde Exception olarak
bilinmektedir.
Harici cihazlar
tarafından gerçekleştirilen ve kesmenin rastgele bir zaman içerisinde
gelebileceği interruptlara Asenkron Interruptlar denilmekedir.
İntel terminolojisinde Senkron Interruptlar Exception ve Asenkron
Interruptlar ise Interrupt olarak adlandırılmaktadır. Interrupt ve Exceptionlar da kategorilere ayrılmaktadır.
Interruptlar, Maskable ve Non-Maskable olmak üzere ikiye
ayrılır.
Maskable
Interrupt , Maskelenebilir Interruptlar CPU tarafından reddedilebilir. EFLAGS
registerı içerisindeki IF(Interrupt
Flag) Flagi STI emiri ile setlenir. Bu durumda maskelenebilir interruptlar
etkindir. CLI instructionı ile IF(Interrupt Flag) flagi clear edilir. Bu
durumda maskalenebilir interruptlar disable edilmiştir ve maskelenebilir
interruptlar kesme gerçekleştirirse CPU tarafından kesmeler reddedilir.
Non-Maskable interruptlar hiçbir şekilde CPU tarafından
reddedilemez eğer non-masksable bir interrupt gerçekleştirildiyse sistemin buna
hizmet etmesi gerekir.
Exception olarak sık kullanılan iki exception durumunu
açıklayalım. Fault ve Trap.
Eğer exception olarak fault meydana geldiyse bu durumda
ilgili fault handler çalışır ve fault handler işini bitirdikten sonra faultlamaya sebep olan instructiona döner.
Örnek olarak bir programın henüz memoryde bulunmayan bir sayfasına erişmek
istediğini düşünelim bu durumda page fault exception meydana gelir . Page fault
handler ilgili page eğer programa ait ise bu durumda memorye yükler ve fault
handler faultlamaya sebep olan instructiona döner ve program tekrardan aynı
emiri çalıştırarak akışına buradan devam eder.
Eğer exception olarak trap meydana geldiyse bu durumda
ilgili trap handler çalışır fakat trap handlerdan dönüş olarak traplemeye sebep
olan instructionı değil bir sonraki adrese döner. Fault exceptiondan
farkı budur.
Basit Model
~~~~~~~~~~~~~~~~~~
Hardware tarafında bu işlerin nasıl gerçekleştirildiğini anlamak için basit model adını verdiğimiz bu başlık altında ilk x86 işlemcilerde interrupt mekanizması nasıl kullanılıyordu buna göz atacağız.
~~~~~~~~~~~~~~~~~~
Hardware tarafında bu işlerin nasıl gerçekleştirildiğini anlamak için basit model adını verdiğimiz bu başlık altında ilk x86 işlemcilerde interrupt mekanizması nasıl kullanılıyordu buna göz atacağız.
İşlemcilerde INTR ve NMI adında iki pin-bacak bulunmaktadır.
Kesmenin gerçekleştirilmesi için bu 2 bacaktan birine sinyal gönderilir.
INTR
~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~
INTR pini
, kesme bu bacak üzerinden gerçekleştirildiği zaman işlemci tarafından bu
kesmeye ya cevap verilir ya da verilmez.
EFLAGS registerındaki IF flagı setlenerek veya clear edilerek kesmelere
ya cevap verilebilir ya da kesmeye cevap verilmez. x86 mimaride IF(Interrupt Flag) flaginin
setlenmesi ve clear edilmesine ilişkin STI ve CLI emirleri mevcuttur.
Kritik işlerle ilgilenildiği zaman gelen interruptlar CPU
tarafından reddedilebilir.
NMI(Non-Maskable Interrupt)
~~~~~~~~~~~~~~~~~
Bu pin üzerinden kesme gerçekleşirse işlemci tarafından interrupt reddedilemez bu yüzden non-maskable interrupt olarak bilinir.
~~~~~~~~~~~~~~~~~
Bu pin üzerinden kesme gerçekleşirse işlemci tarafından interrupt reddedilemez bu yüzden non-maskable interrupt olarak bilinir.
INTEL 8259A-PIC(Programmable Interrupt Controller)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Intel 8259A Programlanabilir Kesme Denetleyicisini intel x86
Uniprocessor işlemcilerinde
kullanılmıştır.
8259A kesme denetleyicisinin 8 adet IRQ(Interrupt ReQuest)
bacağı vardır. Kesmeyi gerçekleştirecek olan cihazlar bu bacaklara bağlıdır.
Herhangi bir kesme gerçekleştirmek istediklerinde bağlı bulundukları IRQ
hattında bir sinyal yaratırlar.Eğer birden fazla kesme aynı anda
gerçekleştirildiyse böyle bir durumda kesme denetleyici önceliğe göre kesmelere
teker teker cevap verir. Kesme denetleyicisi tarafından seçim gerçekleştirildikten sonra interrupt için kesme denetleyicisinin
INT bacağı aktif hale gelir. Bu bacak işlemcinin INTR bacağına bağlıdır.Eğer
CPU bu kesmeyi onaylarsa Kesme denetleyicisinin INTA bacağına sinyal gönderir.
Kesme denetleyici kendisine gelen INTA
sinyali ile Interruptın onaylandığını anlar ve bunun ardından işlemci bir INTA
sinyali daha gönderir. Bu sinyalle birlikte 8259A ilgili interruptın servis rutin adresini elde etmesi için
işlemciye data bus üzerinden bir type number(8-bit) gönderir. Bu type number
Interrupt Vektör Tablosuna göz atılırken kullanılan bir numaradır. Interrupt
Vektör tablosu daha önceden hazırlanmış bir tablodur ve içerisinde
interruptlara ilişkin servis programların adresini barındırır. Type Number ise
bu tablo için bir index numarası olarak kullanılmaktadır.Bu tablodan ilgili
interruptın adresi type number aracılığı ile elde edilir.
Burada
Interrupt gerçekleştiğinde kesilmiş olan belirli bir iş olduğundan her servis
rutin programına başlanmadan önce tüm cpu registerları korunur ve servis rutin
programının sonunda tekrardan korunmuş olan
cpu registerları yüklenir ve interruptlama gerçekleştiğinde ana program
nereden kesildiyse o adresden tekrar devam eder.
Yukarıdaki resim anlattığımız olay döngüsünü güzel bir
şekilde açıklamakta. 2.INTA sinyali gösterilmese de yine de güzel bir çizim. :
) ( 2.INTA sinyalinin varlığına inanmanız için bu linke göz atabilirsiniz. https://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf
)
(*) Resim Mohammed Amer Arafah adlı bir kişinin çizimi ve
oldukça güzel bir çizim.
Buraya kadar x86 işlemcilerde ilk kullanılan interrupt
mekanizmasından ve genel kavramlardan bahsettik. Interrupt mekanizmasının
hardware tarafında uygulanış biçimi ile interruptlar
hakkında fikir sahibi olduk. Yazının bundan sonraki kısmı ise modern interrupt
mekanizmasını anlatacaktır.
Local APIC , I/O
APIC
~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~
Bu başlıkta PIC
anlatımındaki gibi belirli bir modeli(8259A) ele alarak anlatım
yapmayacağız. Genel bir anlatım gerçekleştirilecektir.
8259A
kesme denetleyicisi yalnızca uniprocessor(tek procesoor ve tek core)
sistemlerde kesme isteklerini karşılayabilmektedir. Gelişmiş multiprocessor
sistemler için gelişmiş kesme denetleyici gerektiğinden intel APIC( Advanced
Programmable Interrupt Controller) denetleyicisini geliştirdi. APIC genel olarak bir I/O APIC denetleyicisi
ve her core veya processorün kendisine entegre edilmiş Local APIC
denetleyicilerinden meydana gelmektedir.
I/O APIC Harici cihazların interruptlarını karşılamaktadır. I/O APIC gelen bu interruptları sistemdeki
processorlere/corelara dağıtmaktadır. I/O APIC ve LAPIC arasındaki iletişim
yukarıdaki resimde gördüğünüz gibi kimi
processorler için ya System bus
üzerinden ya da APIC bus üzerinden gerçekleşmektedir. Günümüzde haberleşme
system bus üzerinden gerçekleşmektedir.
APIC ile interrupt
handlerın adresi nasıl elde ediyor sorusundan önce Interrupt Descriptor Table
anlatmamız gerekiyor.
IDT-[Interrupt
Descriptor Table]
~~~~~~~~~~~~~~~~~
İşlemci bir kesme gerçekleştiğinde ilgili kesmenin Interrupt
handlerına ulaşmak için Interrupt Descriptor Tablosunu kullanmaktadır. Real
Modda bu tabloya Interrupt Vector
Tablosu denilmektedir. x86 mimaride tablodaki her kayıt 8 bayt ve x64 mimaride 16 bayt uzunluğundadır.(Bu yazıda anlatılanlar x86
mimari ve x64 mimari için geçerlidir fakat kısmi değişikliler olabilir tıpkı
boyut farklılığı gibi) .
x86 İşlemcilerde bu tabloya erişim için emirler ve bir adet
register bulunmaktadır.
IDTR registerı,
Interrupt Descriptor Tablosunun base adresini(bellekteki adresi) ve Limit değerlerini tutar. ( Base Adress +
Limit ) değeri interrup descriptor
tablosundaki son kayıtın adresini işaret eder.
LIDT(Load Interrupt Descriptor Table Register ) emiri ile
IDTR registerının içeriği
setlenirken, SIDT emiri ile IDTR
registerının içeriği elde edilir.
IDT içerisindeki kayıtlar Gate Descriptor olarak adlandırılmaktadır.
Gate descriptorlar type field ile birbirlerinden ayrılırlar.
Üç tür gate descriptor vardır. Bunlar ; Task
Gate Descriptor, Interrupt Gate Descriptor , Trap Gate Descriptor.
Genel olarak Exceptionlar
için Trap Gateler ve Interruptlar için de Interrupt Gateler kullanılmaktadır(
Örnek ; Linux). Eğer kesme IDT
içerisinde Interrupt Gate olarak tanımlandıysa, EFLAGS registerı içerisindeki
IF flagi clear edilir(0 değerine setlenir) . Bu sayede bir kesmeye hizmet
edilirken diğer Maskelenebilir Interruptlar kesme gerçekleştiremez. Trap
Gateler ise IF flagini değiştirmezler.
Şimdi APIC ile Interrupt Vektörünün nasıl elde edileceğini
anlatabiliriz.
I/O APIC kendi içerisinde Redirection Tablosunu barındırır. Bu tablolarda tutulan kayıtlara ilişkin format ise şu şekildedir.
Redirection Tablosunun kayıtları System bus üzerinden Local Apiclere gönderilecek olan Interrupt
mesajının içeriğini belirler.
Buraya kadar anlatılanlar bir interrupt handlerın adresinin
nasıl elde edildiğini anlamamız için yeterli. O halde bir interruptlama
gerçekleşirken ilgili kesme için interrupt handler nasıl koşturuluyor buna göz
atalım.
Harici cihazlar kesme gerçekleştirmek istedikleri zaman I/O
APIC denetleyicisinin IRQ bacaklarına (INITIN bacağı da denilebilir ama biz I/O
APIC denetleyicinin bacaklarına IRQ bacakları diyebiliriz : ) tıpkı PIC deki
gibi ) sinyal gönderirler.
Bu sinyal örneğin IRQ8 bacağına gelsin. Bu durumda I/O APIC
içerisinde bulunan Redirection Tablosunda 8. Kayıt dikkate alınır. I/O APIC bu kayıt
içerisindeki destinationı dikkate alarak
system bus üzerinden Local APIC denetleyicisine Interrupt mesajını gönderir. Bu
interrupt mesajı Interrupt Vektörünü barındırmaktadır. Interrupt vektörü,
interrupt descriptor tablosunda ilgili kesmenin kayıtına ulaşmak için
kullanılmaktadır. Bu değer 0-255 arasında bir değer olabilir.
Bu arada Redirection tablosundaki kayıtların formatına
dikkat edecek olursak yalnızca Destination bitleri kullanılarak(extended destination
bitlerini dikkate almasak dahi) I/O
APIC tarafından bir interrupt mesajı 255
işlemciye veya çekirdeğe gönderilebilir.
Local APIC sorunsuz bir şekilde interrupt mesajını aldıktan
sonra ilgili core veya işlemci artık interrupt handlerı koşturmak için gerekli tüm bilgilere sahip.
Yukarıdaki resimde bir interrupt handlerın(intel buna
interrupt procedure demiş ) nasıl
çağırılacağı görülmektedir. Bu resmi
özeteleyecek olursa ilgili kesmenin
interrupt vektör numarası IDTR içerisindeki IDT tablosunun adress ile toplanır. Buradaki
Gate descriptor dan segment selector( bunun için koruma mekanizması adlı yazıya
göz atabilirsiniz ) elde edilir . Bu segment selector bir segment descriptorı
işaret eder. Koruma mekanizması adlı yazımızda da belirttiğimiz gibi i386 ve sonrası ve x64
mimari buradaki base addres değerini 0 olarak ele alıyorlardı. İşlemcinin
burada segment descriptora göz atmasının nedeni yine koruma amaçlı
kontrollerdir. Bizi burada ilgilendiren Gate descriptorlardaki offset değeridir
.Bu değer ile base adressin ( 0 değerinde ) toplanması sonucu Interrupt
handlerın bellekteki adresi bulunur ve ilgili handler çalıştırılmaya başlanır.
Linux x86 -[INT 0x80]-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INT 0x80, Linux x86 işletim sisteminde user-space den kernel-space tarafına geçiş için
kullanılan yazılımsal kesmedir.Bu geçişin sebebi sistem çağrılarıdır. Sistem
çağrıları ile User mod tarafındaki uygulamalar
işletim sisteminin kendisi ile irtibata geçerler.Örneğin ; Disk üzerindeki
dosyaya yazma gerçekleştirmek istediğinizi varsayalım.Böyle bir işi ancak işletim
sistemi yapabilir ve siz kernel space
tarafındaki dosyaya yazma gerçekleştiren
kodu tetiklemek için sistem çağrılarını kullanırsınız.
Burada
fark edebileceğiniz gibi seviye değişiminin gerçekleşmesi gerekmektedir.
İşletim sistemleri 0.seviyede çalıştıklarından, 3.seviyede bulunan uygulamalar
0.seviyedeki koda dallanma gerçekleştiremez. O halde önce seviye değişimi nasıl
gerçekleşiyor buna göz atalım.
1.Call Gate ile Seviye Değişimi
2.Interrupt Gate ile Seviye Değişimi
2.Interrupt Gate ile Seviye Değişimi
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yukarıda Code-Segment
Descriptorının formatı görülmektedir. Burada Type field içerisindeki C bitinin
setlenmesi durumunda bu segment
Conforming Code Segment olarak adlandırılmaktadır. Aksi durumda Nonconforming
code segment olarak adlandırılmaktadır.
Call Gate ile seviye değişiminde 4 değere bağlı olarak bir
seviye değişim kontrolü gerçekleştirilmektedir.
Burada CPL değeri o anki seviyeyi, RPL ise dallanmak istenilen seviyeyi , Call Gate Descriptorındaki DPL ise Call
Gate’in seviyesini , Destination Code-Segment Descriptordaki DPL ise dallanmayı istediğimiz kodun seviyesini
belirtmektedir.
İşlemci tarafından uygulanan kontrol ise şu şekildedir ;
Call Gate DPL >= CPL ve Call Gate DPL >= RPL eğer bu 2 şart sağlanırsa işlemci olağan
akışı bozmaz ve ardından hedef Code Segment Descriptorın Conforming bitini
sorgular ve ardından son olarak Destination nonconforming / conforming code
segment DPL <=C PL şartına bakılır
eğer bu durumda sağlanırsa code
segmentin nonconforming veya conforming olmasına bağlı olarak işlemler
gerçekleştirilir.
Eğer nonconforming code segmente erişiliceksek bu durumda
işlemci tarafından CPL değeri Destination nonconforming code segmentin DPL
değerine setlenir ve ardından stack switch meydana gelir.
Eğer burada
dallanmayı gerçekleştirmek istediğimiz seviyedeki kod conforming code
segment olsaydı bu durumda stack switch meydana gelmeyecek ve CPL değeri değiştirilmeyecektir.
Linux nonconforming code segmenti kullanmaktadır.
Yukarıdaki resim yukarıda bahsettiğimiz seviye değişimi için
ilgili güvenlik kontrollerin
yapılmasının ardından kodun icrası için
ilgili adresin nasıl üretildiğini göstermektedir.
Interrupt Gate ile Seviye Değişimi
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Call gate ile seviye değişimini anlatmamızın sebebi, interrupt gate ile seviye değişiminin , call gate ile seviye değişiminden çok farklı olmamasıdır.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Call gate ile seviye değişimini anlatmamızın sebebi, interrupt gate ile seviye değişiminin , call gate ile seviye değişiminden çok farklı olmamasıdır.
Interrupt Gate ile seviye değişimidnde RPL kontrolü gerçekleştirilmemekte.
Bunun sebepi Interrupt veya exception
vektörleri herhangi bir RPL değerine sahip değillerdir.
Ayrıca Interrupt Gate ile seviye değişiminde sadece INT n , INT3 ve INTO instructionları ile üretilen
exceptionlar veya interruptlar için DPL değerinin kontrolü gerçekleştirilir.
Evet yazının buraya kadar olan kısmına kadar INT 0x80
kesmesinden bahsetmedik. Bilmemiz gereken tüm detayları öğrendiğimize göre INT
0x80 kesmesinin basit olarak nasıl gerçekleştiğini bakalım.
İşlemci Instructionları işlerken bir yandan da herhangi bir
kesme gerçekleşmiş mi bunu kontrol eder.
0x80 vektörü kesme gerçekleştirdiyse öncelikle APIC
konusunda anlattığımız gibi vektör numarasını elde eder.
Seviye değişimi gerçekleşeceğinden dolayı kernel stack
geçişinin yapılması gerekir. Linux işletim sistemi bunun için Task State
Segmentinden yararlanır. Linux işletim sisteminde yalnızca bir tane TSS vardır
ve switchlemede bu TSS den yararlanılır.
Task State Segment Yapısı ;
TR registerı Linux x86 işletim sisteminde 0x80 değerine
sahiptir. 0x80 değerini incelediğimizde TR Segment Selectorünün index değerinin
16 olduğu görülmektedir.Bu değer GDT için index değeri olarak kullanılır ve GDT
içerisindeki 16.kayıt TSS segmentinin memorydeki yerini belirten bilgiyi
içerir.
İşlemci tarafından Kernel stackine geçiş yapılırken Task
State Segmentinden SS0 ve ESP0 değerleri elde edilir. Burada SS0 ve ESP0 ile kernel stacke geçiş yapılır.
Ardından
eski SS ve ESP değerlerini bu stack üzerine iter. EFLAGS, CS ve EIP değerlerini yine bu stack üzerine
iter.
Burada Interrupted Procedure's stackini User Mode Stack ve Handler's Stackini Kernel Mode Stack olarak düşünebilirsiniz.
Int 0x80 user space tarafından gerçekleşirken CPL
değerimizin 3 olduğunu biliyoruz.
Aşağıda Int 0x80 ilişkin Gate Descriptorını görmektesiniz.
INT 0x80
kesmesinin DPL değerinin 3 olduğu
görülmektedir. Bu durumda INT 0x80 kesmesini gerçekleştirdiğimizde CPL <= Gate DPL olduğundan işlemci
herhangi bir exception fırlatmayacaktır.
CS registerını Gate Descriptor içerisinde 0x60 kernel code
segment selectorüne ve EIP registerını ise 0xd58115e8 adresine setleyecektir.
Bu şekilde Interrupt Handler çalışmaya başlayacaktır.
x64 mimari privilege level değiştirmek için syscall & sysret emirlerini tanıtmıştır.
ayrıca yazının yukarı kısmından bahsetmeyi unuttuğumuz bir şey var . x86 veya x64 bazı interruptları önceden tanımlamıştır.x86 mimari 0 ile 31 ve arasındaki vektörleri kendisi için tanımlamıştır.
Örneğin ; 14 numaralı vektör page fault exception için önceden tanımlanmıştır. Page fault exception meydana geldiğinden 14 numaralı vektördeki interrupt handler çalıştırılmaya başlanacaktır. Bu yüzden işletim sistemi geliştiricisi Interrupt Descriptor Tablosundaki 14 numaralı vektör için mecburi olarak page fault handlerına ilişkin bilgileri yerleştirmelidir.
Umarım Faydalı olmuştur.
Kaynaklar :
Understanding The Linux Kernel
Intel® 64 and IA-32 Architectures Software Developer’s Manual
EOF
Hiç yorum yok:
Yorum Gönder