معالجاتُ المقاطعات
x86_64 وحدَها — قُلْها قبلَ أن تكتب
مُعدِّلُ دالة مقاطعة يُخفَّضُ على x86_64 حصرًا. وقد قِيسَ الرفضُ على الثلاثةِ الأخرى — i686 وaarch64 وriscv64 — كلُّها تُخفِقُ بـSEM046. والسببُ المكتوبُ في التشخيصِ نفسِه: عقدُ i386 مختلفٌ (رمزُ خطأٍ i32 وعودةٌ بـiret). فإن كانت نواتُك ٣٢-بت فهذه الصفحةُ لا تخدمُك بعد.
الهدف: تكتبُ معالجَ مقاطعةٍ بلغةِ ص، فيتولّى المترجّمُ كعبَ الدخولِ والخروجِ كاملًا — حفظُ السجلّاتِ المتطايرة، وتسويةُ المكدَّسِ لرمزِ الخطأ، والعودةُ بـiretq — ثمّ تُسجِّلُه في جدولِ المقاطعات.
العقد
دالة مقاطعة اسم(رقم إطار_المقاطعة)
دالة مقاطعة اسم(رقم إطار_المقاطعة، رقم رمز_الخطأ)- العائدُ فراغ. لا
ارجعبقيمة. - المعاملُ الأوّلُ إلزاميّ: إطارُ المقاطعة.
- المعاملُ الثاني اختياريّ: رمزُ الخطأ — للمصائدِ التي يدفعُه المعالجُ لها (
#PFمثلًا). ولا ثالثَ. - لا يُنادى ولا يُؤخَذُ قيمةً. عنوانُه يُؤخَذُ بـ
عنوان_رمز.
الوصفة
@رمز("idt_ptr") متغير ساكن واصف_الجدول مصفوفة[10]
@رمز("isr_timer") دالة مقاطعة نبضة(رقم إطار)
اكتب_منفذ(0x20، 0x20) # نهايةُ المقاطعةِ إلى المتحكّم
نهاية
@رمز("isr_pf") دالة مقاطعة عطل_صفحة(رقم إطار، رقم رمز_الخطأ)
بينما (صحيح)
أسبت_المعالج()
نهاية
نهاية
@رمز("kmain") دالة لا_ترجع نواة()
هيئ_جدول_مقاطعات()
سجل_معالج_مقاطعة(32، عنوان_رمز("isr_timer"))
سجل_معالج_مقاطعة(14، عنوان_رمز("isr_pf"))
حمل_جدول_مقاطعات(عنوان_رمز("idt_ptr"))
بينما (صحيح)
أسبت_المعالج()
نهاية
نهاية$ sad-build مقاطعات.ص --حرّ --بلا-رئيسية \
--هدف=x86_64-unknown-none-elf --أظهر-llvm -o مقاطعات.llلا مخرَجَ، والرمزُ صفر. وفي الـIR:
@idt_ptr = global [10 x i8] zeroinitializer, align 16
; Function Attrs: noredzone
define x86_intrcc void @isr_timer(ptr byval({ i64, i64, i64, i64, i64 }) %"إطار") #0
define x86_intrcc void @isr_pf(ptr byval({ i64, i64, i64, i64, i64 }) %"إطار",
i64 %"رمز_الخطأ") #0
call void asm sideeffect "lidt ($0)", "r,~{memory}"(i64 ptrtoint (ptr @idt_ptr to i64))أربعةُ شواهدَ في هذا المقتطف:
| الشاهد | ماذا يعني |
|---|---|
x86_intrcc | اتّفاقيّةُ نداءِ المقاطعة — الكعبُ مولَّدٌ، والعودةُ iretq |
byval({ i64 ×5 }) | إطارُ المقاطعةِ يُمرَّرُ بالقيمةِ كما يضعُه المعالجُ على المكدَّس |
noredzone | لا منطقةَ حمراء — المقاطعةُ تدوسُها لو بقيت |
lidt ($0) | حمل_جدول_مقاطعات تُخفَضُ تجميعًا مضمَّنًا لا نداءَ مكتبة |
الأخطاءُ التي ستراها
كلُّ ما يلي مخرَجٌ حقيقيٌّ لا صياغةٌ تقريبيّة.
ناديتَ المعالجَ بدل أخذِ عنوانِه
error: [خطأ نوع] [SEM046] «معالج_عام»: معالجُ المقاطعةِ لا يُنادى — خذ عنوانَه
بـ«عنوان_رمز» وسجّله في جدولِ المقاطعات
→ صرّح المعالج «دالة مقاطعة اسم(رقم إطار_المقاطعة)» بعائد فراغ ومعامل أوّل
إلزاميّ (ورمز خطأ اختياريّ ثانٍ)، وخذ عنوانه بـ«عنوان_رمز» بدل ندائه،
وصرّف للوضع الحرّ على هدف x86_64.أغفلتَ إطارَ المقاطعة
error: [خطأ نوع] [SEM046] «معالج_عام»: معالجُ المقاطعةِ يأخذ إطارَ المقاطعةِ
معاملًا أوّلَ إلزاميًّا، ورمزَ الخطأِ معاملًا ثانيًا اختياريًّا لا أكثر (وُجد 0)بنيتَ لهدفٍ غيرِ x86_64
error: [SEM046] «نبضة»: بوّابةُ المقاطعةِ تُخفَّض على x86_64 حصرًا في المرحلةِ
الأولى — عقدُ i386 مختلفٌ (رمزُ خطأٍ i32 وعودةٌ بـiret) والهدفُ هنا:
i686-unknown-unknown-elfنسيتَ واصفَ الجدولِ في حمل_جدول_مقاطعات
error: [SEM020] حمل_جدول_مقاطعات: يتطلّب مؤشّر واصف الجدول (الحدّ+القاعدة) في
الوضع الحرّالوسيطُ اختياريٌّ مستضافًا وإلزاميٌّ حرًّا — وهو مثالٌ على قاعدةٍ يحرسُها tests/system/lowlevel_freestanding/test_os_core_arity.py في الاتّجاهَين.
ورتبةُ المدمَجاتِ مفروضةٌ في الاتّجاهَين
اكتب_سجل_تحكم(4096) بوسيطٍ ناقصٍ كانت تخرجُ بصفرٍ ولا تُصدِرُ شيئًا: النداءُ يتبخّرُ صامتًا. وبوسيطٍ زائدٍ تُقبَلُ ويُهمَلُ الزائد. وذاك أخطرُ صنفِ عيوبٍ في نواةٍ: سطرُ أمانٍ «يُنفَّذ» وهو غيرُ موجود. اليومَ الناقصُ خطأُ تصريف:
error: [SEM005] الدالة 'اكتب_سجل_تحكم' تتوقّع 2 من المعاملات، لكن مُرِّر 1لماذا وُجِدَ المُعدِّلُ أصلًا
معالجُ المقاطعةِ المكتوبُ بلغةِ ص كان يدخلُ ولا يستطيعُ الرجوع: لا كعبَ دخولٍ ولا خروج، فلا مقاطعةَ مؤقّت، فلا جدولة، فلا نظامَ تشغيل. والمُعدِّلُ يسدُّ ذلك الثقبَ الواحدَ بإحالةِ الكعبِ إلى x86_intrcc في LLVM. تفصيلُ المقيسِ في tests/system/lowlevel_freestanding/test_interrupt_handler_modifier.py (RFC 0059، الفجوة ح٢).
ما يلي
- المؤقّتُ يلزمُه منفذٌ: MMIO والمنافذ.
- تحويلُ الحلقةِ ٣ يلزمُه
حمّل_سجل_المهمة: لهجةُ التجميع. - ولماذا لا
libcتحتَ كلِّ هذا: الوضعُ الحرّ.