تفکرات صفر و یکی

نوشته های من در مورد سیستم عامل، درایور نویسی، مهندسی معکوس، امنیت و هر چیز سطح پایین دیگر

تفکرات صفر و یکی

نوشته های من در مورد سیستم عامل، درایور نویسی، مهندسی معکوس، امنیت و هر چیز سطح پایین دیگر

آخرین نظرات

IRQL چیست و چه ارتباطی با IRQ و وقفه ها دارد؟

چهارشنبه, ۱۴ مهر ۱۳۹۵، ۱۲:۰۲ ق.ظ

موضوع اصلی آموزش امروز ما در مورد IRQL است که می توان گفت یکی مهمترین مباحث موجود در دنیای درایور نویسی در ویندوز به حساب می آید. این مبحث کمی با مساله وقفه ها گره خورده است. در واقع این گره و ابهام به خاطر این است که ماکروسافت مستندات روشنی در این زمینه ارائه نکرده و از طرفی تشابه اسمی بین IRQL و IRQ ممکن است سوالاتی از قبیل: آیا این دو یکی هستند؟ آیا مربوط به سخت افزار می شوند؟ آیا مربوط به ویندوز می شوند؟ را به وجود می آورد. حداقل این ها سوالاتی بود که تا مدتی من درگیرشان بودم تا متوجه شدم موضوع از چه قرار است. و از أنجایی که در پست های آینده با اصطلاحاتی مانند PASSIVE/APC/DPC LEVEL روبرو خواهید شد لازم است در این مورد پیش زمینه ای داشته باشید.

 

کنترلر وقفه (Interrupt Controller)، وقفه (Interrupt) و IRQ

قبل از اینکه بخواهیم در مورد IRQL صحبتی کنیم اول لازم است اطلاعاتی در مورد کنترلر وقفه، وقفه و IRQ داشته باشیم چون بدون دانستن این اطلاعات ممکن است دچار ابهام شوید.

یکسری دستگاه روی سیستم وجود دارند مانند کیبورد، ماوس، هارد دیسک و غیره. این دستگاه ها به طریقی باید CPU را مطلع کنند که هر کاری CPU دارد برای لحظه ای کنار بگذارد و درخواست دستگاه مورد نظر را دریافت کند. برای مثال کاربر کلیدی از صفحه کیبرد فشار می دهد یا اطلاعاتی که از هارد دیسک درخواست کرده بودید برای دریافت حاظر است. دستگاه ها چطور CPU را مطلع می کنند؟ با استفاده از وقفه (Interrupt) یا بطور دقیقتر یک وقفه سخت افزاری. وقفه یک سیگنال است که به CPU می گوید هر کاری دارد متوقف کند و کار دستگاه درخواست کننده را انجام دهد. وقتی دستگاهی وقفه تولید می کند این سیگنال مستقیم به CPU نمی رسد. این وسط دستگاه دیگری وجود دارد که درخواست همه دستگاه ها را می گیرد و بر اساس اولویت (priority) آنها را به CPU می دهد. به این دستگاه میانی کنترلر وقفه (Interrupt Controller) گفته می شود. کنترلر وقفه در واقع یک چیپ قابل برنامه ریزی است. در ادامه دو کنترلر 8259A و 82093AA را مورد بررسی قرار خواهیم داد.

کنترلر 8259A

از کنترلر وقفه های معروف که روی کامپیوترهای مبتنی بر پردازنده های معماری x86 استفاده شده می توان به 8259A اشاره کرد به این کنترلرها اصطلاحا PIC (Programmable Interrupt Controller) گفته می شود. این کنترلر مربوط به دروه ای است که 8086 وجود داشت یعنی حدود ۳۸ سال پیش خنده. از آنجایی که یکسری مفاهیم که می خواهیم در موردشان صحبت کنیم از آن دوران تا الان تغییر خاصی نکرده است به نظرم آمد که بد نیست شرح مختصری در مورد این کنترلر بدهم. در تصویر زیر می توانید پایه های (Pins) مربوط به این کنترلر را مشاهده کنید.

 

کنترلر 8259A

پایه های IR0-IR7 در واقع IRQ (Interrupt Request) ها می باشند. هر کدام از این پایه ها به یک دستگاه  متصل است و دستگاه از این طریق کنترلر را با خبر می کند که درخواستی دارد. پایه INT هم برای فرستادن سیگنال به CPU است (پایه INT به پایه INTR پردازنده متصل شده). در واقع کنترلر CPU را از به وجود آمدن یک وقفه از سمت دستگاه با خبر می کند. همانطور که مشاهده می کنید ما ۸ پایه IRQ داریم در نتیجه ۸ دستگاه می توانند به این کنترلر متصل شوند. این امکان وجود دارد که تعدادی کنترلر 8259A را به هم متصل کرد تا تعداد بیشتری دستگاه روی سیستم مورد استفاده قرار گیرد. برای مثال در تصوریر زیر دو کنترلر 8259A که یکی به عنوان Master و دیگری به عنوان Slave به هم متصل شده اند.

 

cascade-8259A

به این صورت ما ۸ پایه از کنترلر Slave داریم و ۷ پایه از کنترلر Master در نتیجه ۱۵ دستگاه می توانند روی یک کامپیوتر وجود داشته باشند (از IRQ0-IRQ15)

کنترلر 82093AA

امروزه کنترلرهای APIC (Advanced Programmable Interrupt Controller) ، که یک جایگزین پیشرفته تر به جای کنترلرهای PIC هستند، مورد استفاده قرار می گیرند. APIC مناسب برای سیستم های است که دارای چند پردازنده هستند. APIC خود شامل دو بخش می باشد I/O APIC و LAPIC (Local APIC). در شکل زیر نمایی از کنترل 82093AA I/O APIC را می توانید مشاهده کنید. 

گنترلر I/O APIC 82093AA

کنترلر I/O APIC که در تصویر می بینید مشابه کنترلر 8259A وقفه های خارجی از سمت دستگاه ها را دریافت می کند و بعد پردازنده را از وقفه با خبر می کند و همچنین به هر تعدادی می توان از این کنترلر روی یک سیستم قرار داد. کنترلری که در تصویر می بینید تعداد ۲۴ پایه IRQ دارد (پایه های INTIN0-INTIN23). بالاتر اشاره کردیم که APIC دارای دو بخش است، بخش دوم LAPIC داخل پردازنده قرار دارد. هر پردازنده و هر هسته منطقی دارای یک LAPIC می باشند و بعد از اینکه I/O APIC وقفه ای از دستگاه دریافت کند توسط LAPIC یکی از پردازنده ها/هسته ها که آزاد است دریافت می شود. تصویر پایین یک مثال از یک سیستم ۴ پردازنده ای نشان می دهد.

I/O APIC و Local APIC

Priority, Maskable, Non-maskable

در مورد وقفه ها مفاهیم دیگری است که به عنوان یک درایور نویس باید در موردشان اطلاع داشته باشید. از طرفی این مفاهیم در فهم بهتر موضوع IRQL که جلوتر در مورد آن توضیح خواهم داد نقش مهمی خواهند داشت. 

Priority (اولویت): مساله اولویت مربوط می شود به ترتیب پایه های IRQ. وقتی یک کنترلر داریم با پایه های IRQ0-IRQ7 پایه IRQ بالاتر اولیت دارد به IRQ های پایینتر. مثلا اگر فرض بگیریم یک کنترلر 8259A داریم و دستگاهی روی IRQ4 باشد این دستگاه اولیت بیشتری دارد نسبت به دستگاهی که روی IRQ2 است. خیلی واضح است. این مساله در مورد دو کنترلر که به هم متصل شدند یکم فرق دارد (به این واضحی نیست) چرا؟ چون ترتیب IRQ ها از ۱ تا ۱۵ نیست. در واقع اول IRQ0-IRQ1 بعد IRQ8-IRQ15 و در آخر IRQ2-IRQ7 است که خیلی هم برای ما مهم نیست لبخند. با اینکه نمی خواهم خیلی وارد همه موضوعات و جزئیات بشوم ولی بد نیست در این زمینه جستجویی هم در مورد رجیستر TPR در کنترلرهای APIC کنید.

Maskable: ما یکسری وقفه داریم که اصطلاحا maskable هستند. به این معنی که ما می توانیم مشخص کنیم یکسری از وقفه ها mask (نادیده گرفته) بشوند. داخل کنترلر یک رجیستر وجود دارد که می توان وقفه هایی که می خواهیم mask بشوند را مشخص کنیم.

Non-maskable: از طرفی یکسری وقفه داریم که غیر قابل چشم پوشی هستند. و ما هیچ راهی برای غیر فعال کردن آنها نداریم. این وقفه ها خطاهایی هستند که در سیستم به وجود می آیند و لازم است بدون تاخیر به پردازنده اطلاع داده شوند.

IRQL چیست؟

 تا این مرحله ما در مورد وقفه های سخت افزاری، پایه های IRQ، کنترلر وقفه و همچنین موضوعات دیگری مثل اولویت و maskable بودن وقفه ها توضیحاتی دادیم. در این قسمت میرسیم به موضوع اصلی آموزشمان یعنی IRQL.

IRQL (Interrupt Request Level) در واقع یکسری عدد است که توسط ماکروسافت تعریف شده. این اعداد در سیستم های بر پایه x86 از 0 تا 31 و در x64 و IA64 از 0 تا 15 می باشد. در شکل زیر میتوانید ببینید که در یک سیستم x86 هر IRQL چه عنوانی دارد.

سطح IRQL ها

ماکروسافت یک سیستم نام گذاری داخلی برای تمام وقفه های سخت افزاری و هم چنین یکسری از وقفه های نرم افزاری تعریف کرده است. به این طریق درایور نویس وابسته به تنوع و جزئیات سخت افزارها نیست و همیشه با یکسری مفاهایم ثابت سروکار خواهد داشت. و باید این نکته را در نظر بگیرید که IRQL وقفه های سخت افزاری را نیز در خود دارد ولی لزوما ترتیب این اعداد با ترتیب IRQ های کنترلر وقفه یکی نیستند. ماکروسافت با استفاده از ماژول HAL که داخل کرنل ویندوز است این اعداد را به هم دیگر تبدیل میکند و همانطور که گفتم ما اصلا درگیر این مسائل نمی شویم. 

حالا هدف از تعریف این اعداد چه بوده؟ هدف بطور کلی مربوط به اولیت بندی کدی است که در ویندوز اجرا می شود. این قسمت دقیقا مثل مفهوم اولیت در IRQ مربوط به کنترلر است یعنی مثلا اگر کدی در سطح DPC (IRQL2) اجرا می شود یک کد دیگر در سطح Passive (IRQL0) اجرا نخواهد شد تا زمانی که کار کد سطح DPC تمام شود. در واقع بطور دقیق تر وقتی یک کد در سطح X اجرا می شود هیچ کدی با سطحی برابر یا کوچکتر از X اجازه اجرا شدن ندارد. این ویژگی IRQL استفاده زیادی در مفاهیم مربوط به Synchronization در سیستم عامل ویندوز دارد.

ممکنه بپرسید ماکروسافت چطور IRQL را پیاده کرده؟ یک بخشی از این پیاده سازی مربوط به نگه داری عدد IRQL داخل یکسری ساختارهای کرنل ویندوز می شود و بخش دیگر در واقع به کمک Priority و Maskable بودن وقفه ها در کنتلر وقفه. در وقع ویندوز رجیستر های IMR و TPR (در پردازنده های IA 64 رجیستر CR8 که مربوط به پردازنده است نیز مورد استفاده قرار می گیرد) را با توجه به نوع کنترلر یعنی PIC یا APIC و عدد IRQL (البته این عدد اول توسط HAL.dll تبدیل می شود) تغییر می دهد.

ما به عنوان یک درایور نویس چه احتیاجی به دانستن IRQL داریم؟ هر کدی که در ویندوز اجرا می شود در یک سطح مشخص IRQL اجرا می شود. اگر مستندات ماکروسافت در مورد درایور نویسی را بخوانید هر تابعی در کرنل ویندوز در یک سطح IRQL تعریف شده ای قابل اجرا است و استفاده اشتباه توابع در سطوح IRQL نادرست باعث کرش سیستم می شود. این نکته را گفتم که در استفاده از هر تابعی حتما سطح IRQL آن را بررسی کنید. بگذارید یک مثال بزنم،‌ در آموزش مربوط به "روش های دسترسی به بافر داده" گفته بودم که I/O Manager یک حافظه سیستمی از نوع Nonpaged می گیرد و همچنین در ادامه در بخشی توضیح دادم که ما یک جاهایی مجبوریم از حافظه Nonpaged استفاده کنیم. این نوع حافظه در واقع این اطمینان را می دهد که page های تخصیص گرفته شده همیشه در حافظه اصلی باقی بمانند و به دیسک منتقل نشوند. در عمل "خواندن" I/O Manager باید داده خوانده شده توسط درایور را در این حافظه کپی کند که بعد به برنامه سطح کاربر برسد. یک درایور می تواند Completion Routine برای خود تعریف کند که وقتی عمل خواندن به پایان رسید از این موضوع باخبر شود (در آموزش های آینده در مورد این موضوع توضیح خواهم داد). طبق مستندات ماکروسافت این تابع در سطحی برابر با DPC Level یا کوچکتر اجرا می شود. از طرفی باید بدانیم که در سطح DPC ما نمی توانیم هیچ wait ای داشته باشیم. در نتیجه اگر حافظه از نوع Nonpaged نباشد و ما در سطح DPC باشیم و page های حافظه به دیسک منتقل شده باشند. سیستم عامل باید منتظر بماند تا page های مربوطه از دیسک به حافظه برگردند و این قسمتی است که در سطح DPC نمی توانیم انجام دهیم. نتیجه سیستم با مشکل روبرو خواهد شد و در نهایت کرش خواهد کرد.

توضیح در مورد هر کدام از این سطح ها مساوی از با آوردن مثال هایی مشابه مثال بالا. به نظرم بهتر است بجای اینکه این پست را پر کنم از این قبیل مثال ها، در هر پستی اگر موضوع مرتبط به IRQL بود توضیخ بیشتر بدهم.


مثل همیشه اگر با ایراد یا ابهامی در آموزش مواجه شدید یا اگر سوالی داشتید حتما مطرح کنید.

نظرات (۵)

مرسیییییییییییییی موتورت روشن شده ها، ایول :x
راستی منم شروع کردم منتها پلتفرمم لینوکس هست فعلا دارم کتاب Beginning Linux Programming میخونم تا برسم به Advanced Linux programming بعدش به Linux Device Drivers

:D
فدایی داری

سلام. روز بخیر ممنون از پست خوبت.

به مشکلی برخورد کردم که شاید بی ارتباط با این بحث  IRQL نباشد.

در حال توسعه minifilter درایوری هستم برای تشخیص دسترسی Rename که در ارتباطی با سطح کاربر اجازه چنین دسترسی بررسی میشود و سپس ادامه مییابد. طبق بررسیهای من در دیباگ تنها callback که فراخوانی میشود setInformation هست و سایرین مثل precreate یا PreRead اصلا در این فرآیند صدا زده نمیشوند. بنابراین من در فانکشن PreSetInformation برای ارتباط با سطح کاربر contextی از فایل نخواهم داشت.

برای بررسی دسترسی در سطح کاربر به محتوای فایل نیاز هست. آیا فراخوانی توابع FltCreate و FltRead در فانکشن PreSetInformation صحیح است. ضمن اینکه در حال حاضر در بدست آوردن بافر داده فایل سیستم دچار هنگینگ میشود.

مثال را به صورت خصوصی ارسال میکنم دوست عزیزم. ممنون میشم راهنمایی بفرمایید.



سلام ممنون از پست خوبتون . این پست رو با ذکر منبع منتشر میکنم تا بچه هایی که به این مفاهیم نیاز دارن استفاده کنن و بهتر براشون جا بیافته. برای من که خوب مفهوم بود. دستت درد نکنه.

پاسخ:
خوشحالم مطلب بدرد خورده

مشکلی نیست استفاده کنید از مطالب

سوالی داشتم در خصوص این که در Minifilter درایورها چطوری  میشه از همزمانی در ارسال بافر به مد یوزر جلوگیری کرد ؟ از Spinlock استفاده میکنم کرش میکنه که طبیعی هست چون سطح دسترسی های مختلفی در Minifilter دیده میشن ولی وقتی از FastMutex هم استفاده میکنم هنگ میکنه سیستم و دلیل این هنگ رو نمیدونم چی هست . 

پاسخ:
خیلی مشخص نمی تونم بگم مشکلی چیه ولی اگه بخش های مختلفی مستفیم دارن به یوزر داده می فرستن میشه از یک صف این وسط استفاده کرد.

سلام خیلی ممنون از مطلب شما یه خطای صفحه آبی هست که درواقع متن خطاش IRQL_NOT_LESS_OR_EQUAL میخواستم بدونم این خطا چه ارتباطی به موضوع گفته شده داره و چه ارتباطی به رم داره؟

 

 

 

 

 

 

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی