ورود/ایجاد حساب کاربری
   منوی اصلی
· خانه
· لیست کاربران
· جستجو
· آمار مشاهدات
· آرشیو مقالات


- شرح
· راهنمای نویسندگان
· درباره ما

   همکاری با نشریه
در صورتی که مایل به همکاری با نشریه هستید، می‌توانید در لیست پستی نشریه عضو شده و در جریان امور قرار گیرید. برای اطلاعات بیشتر، اینجا کلیک کنید.

   کاربران
سردبیر
هیچ مدیر کمکی حاضر
همکاران
هیچ مدیر کمکی حاضر
اعضا:
جدیدترین:جدید امروز:0
جدیدترین:جدید دیروز:0
جدیدترین:مجموع:2471
جدیدترین:جدیدترین:
ufumenarayu
اعضا:حاضر
اعضا:اعضا:0
مهمان‌ها:مهمان‌ها:39
مجموع:مجموع:39
کاربران حاضر
هیچ کاربر حاضری وجود ندارد

   ورود کاربران




 


 برای ورود مشکل دارید؟
 ثبت نام کاربران جدید

ماژول نویسی برای کرنل قسمت ۱۳

(1884 مجموع کلمات موجود در متن)
(7367 بار مطالعه شده است)  نسخه چاپی

ماژول نویسی برای هسته لینوکس (قسمت سیزدهم)


در این قسمت به بررسی دو موضوع نسبتا جدا از هم در ماژول نویسی هسته لینوکس خواهیم پرداخت. در بخش اول با عنوان توابع سیستمی با نحوه نوشتن و تغییر توابع سیستمی هسته لینوکس اشنا خواهیم شد و در بخش دوم با عنوان متوقف کردن پروسه ها نحوه مدیریت پروسه های در حال اجرا و پروسه های در حال انتظار را فرا خواهیم گرفت.


توابع سیستمی ( System Calls )


انچه تاکنون در ماژول نویسی هسته لینوکس انجام دادیم استفاده از مکانیزم های دقیق هسته مانند ثبت فایل در proc/ و راه انداز فایل ها و ... بود. همه چیز مرتب است اگر شما بخواهید کاری انجام دهید که توسعه دهندگان هسته ان را پیش بینی کرده و مکانیزمی در هسته برای کار شما در هسته ایجاد کرده باشند مانند نوشتن راه انداز دستگاه. اما اگر شما بخواهید کار غیر معمولی انجام دهید که توسعه دهندگان هسته راه کاری برای ان ایجاد نکرده اند چطور ؟ در اغلب این موارد مسئولیت همه چیز با خودتان است.

اینجا جایی است که برنامه سازی هسته خطرناک می شود. با نوشتن ماژول هسته مثال شما یکی از این کارهای خطرناک را انجام خواهید داد. کد این مثال را از اینجا [۱]می توانید بدست اورید. در این مثال شما تابع سیستمی open را تعویض خواهید نمود. این بدان معنی است که هیچ کس در سیستم دیگر نمی تواند فایلی را باز کند ( هیچ کس نمی تواند برنامه ای اجرا کند و حتی کامچیوتر را خاموش نماید ). تنها راه راه اندازی سخت افزاری سیستم می باشد. خوشبختانه هیچ فایلی از بین نخواهد رفت. برای حصول اطمینان از خراب نشدن فایل ها قبل از هر insmod و هر rmmod یک بار دستور sync را اجرا کنید.


فایل های proc/ و فایل های دستگاه ها در dev/ را فراموش کنید. پروسه اصلی در مکانیزم ارتباط با هسته ( که توسط تمام پروسه ها استفاده می شود ) استفاده از توابع سیستمی می باشد. هنگامی که یک پروسه سرویسی را از هسته درخواست می نماید ( مانند باز کردن یک فایل , ایجاد یک پروسه جدید یا درخواست حافظه بیشتر ) از این مکانیزم استفاده می شود. اگر شما می خواهید رفتار هسته را به دلخواه خود تغییر دهید اینجا جایی است که می توانید تغییرات خود را اعمال نمایید. برای دانستن توابع سیستمی که در برنامه ها به کار می روند می توانید از دستور strace به صورت زیر استفاده نمایید.

$strace programname


به طور کلی یک پروسه نمی تواند به هسته دسترسی داشته باشد. بدان معنی که نمی تواند به حافطه هسته دسترسی داشته باشد و نمی تواند توابع هسته را صدا نماید. ( این سیستم ها protected mode نامیده می شوند) . توابع سیستمی استثنای این قاعده کلی هستند. اتفاقی که می افتد این است که پروسه رجیسترهای پردازنده را با مقادیر مناسب پر کرده و دستور خاصی را صدا کرده که باعث پرش به محل از پیش تعیین شده ای از هسته می شود. ( که البته ان محل از حافظه توسط پروسه ها قابل خواندن است ولی قابل نوشتن نیست ). در پردازنده های اینتل این رویه با استفاده از وقفه ( interrupt ) شماره 0x80 انجام می گیرد.


CPU به عنوان سخت افزار کامپیوتر می داند هنگامی که شما به این نقطه پرش می کنید نمی خواهید که در حالت محدود شده ادامه کار دهید و به عنوان کدی از هسته سیستم عامل به شما اجازه هر کاری که بخواهید را می دهد. مکانی در هسته که پروسه می تواند به ان پرش نماید system_call نامیده می شود. رویه در این موقعیت از هسته به این صورت است که شماره تابع سیستمی که بیانگر سرویسی است که پروسه از هسته می خواهد چک شده و در جدول توابع سیستمی ( sys_call_table ) تابع موردنظر برای صدا زدن جستجو خواهد شد و سپس تابع موردنظر صدا زده می شود و پس از بازگشت تابع و انجام چندین چک از طرف سیستم به پروسه بازگشت خواهد شد ( یا اگر زمان پروسه تمام شده باشد به پروسه دیگری ارجاع خواهد شد )


برای مشاهده کد اسمبلی این رویه به ادرس Linux Source Code/arch/<$architecture$>/kernel/entry.S از کد هسته لینوکس بعد از خط (ENTRY(system_call مراجعه کنید.


بنابراین اگر بخواهیم عملکرد تابع سیستمی ای را تغییر دهیم کافیست تابع خودمان را بنویسیم و اشاره گر به تابع اصلی در sys_call_table را با اشاره گر به تابع خودمان جایگزین نماییم تا به تابع ما اشاره کند. فقط بایستی یادمان باشد که در تابع cleanup_module تمام تغییراتی که در این جدول اعمال کرده ایم را به حالت اولیه بازگردانیم تا سیستم

در حالت ناپایدار باقی نماند.


کد مثالی که از اینجا [1] می توانید دریافت کنید کد یک ماژول کرنل می باشد. ما می خواهیم جاسوسی یکی از کاربران را انجام دهیم به صورتی که هرگاه کاربر موردنظر ما فایلی را باز کرد یک پیغام به وسیله ()printk در فایل log هسته چاپ کنیم. برای این کار تابع سیستمی ()open را با تابع خودمان به نام our_sys_open جایگزین می نماییم. این تابع user's id ) uid ) پروسه جاری را چک کرده و اگر برابر با uid کاربر موردنظر ما بود پیغام موردنظر ما را چاپ می کند و در نهایت فایل موردنظر کاربر را به وسیله تابع اصلی ()open باز می نماید.


در تابع init_module اشاره گر موردنظر در جدول sys_call_table جایگزین شده و مقدار اصلی این اشاره گر در یک متغیر ذخیره می شود. تابع cleanup_module از این متغیر استفاده کرده و همه چی را به حالت عادی باز می گرداند. /P

این روش , روشی خطرناک است چون ممکن است که دو ماژول هسته قصد تعویض یک تابع سیستمی را داشته باشند. فرض کنید که دو ماژول کرنل A و B داشته باشیم. تابع ()open جایگزین شونده در ماژول A_open , A و در ماژول B_open , B است. ماژول A در هسته وارد می شود بنابراین تابع سیستمی ()open با A_open جایگزین می شود. حال ماژول B در هسته وارد می شود که باعث جایگزینی A_open با B_open می شود.در مورد خروج ماژول ها از هسته اگر ماژولB و سپس ماژول A از هسته خارج شوند همه چیز درست خواهد بود اما اگر برعکس خارج شوند چطور ؟ خودتان می توانید حدس بزنید که چه اتفاقی خواهد افتاد . اگر A ابتدا از هسته خارج شود مقدار جاری اشاره گر به تابع سیستمی که به B_open اشاره می کند را با open اصلی تعویض خواهد کرد و اگر در این لحظه B از هسته خارج شود مقدار جاری اشاره گر که به open اصلی اشاره می کند را با مقذار ذخیره کرده خود که همان A_open است جایگزین می نماید و چون A_open در هسته وجود ندارد سیستم دچار crash خواهد شد. و موارد زیاد دیگری که می تواند سیستم را دچار crash کند.


مسائلی از این قبیل اجازه کار با توابع سیستمی را برای کارهای تولیدی که به استفاده عموم می رسد را غیر ممکن می سازد. برای جلوگیری از انجام کارهای خطرناک توسط افراد , دیگر متغیر sys_call_table که به جدول توابع سیستمی اشاره می کند در فضای هسته قرار داده نشده است. بنابراین اگر شما می خواهید که ماژول مثال مورد بحث را بتوانید در هسته وارد کنید بایستی هسته جاری خود را patch کرده و دوباره کامپایل نمایید تا متغیر sys_call_table را در حافظه هسته داشته باشید. این patch را می توانید در دایرکتوری مثال پیدا کنید. ( شاید نیاز داشته باشید که برای نسخه هسته خود کمی کد نیز به صورت دستی اعمال کنید )


متوقف کردن پروسه ها ( Blocking Processes )


هنگامی که کسی از شما چیزی می خواهد که شما نی توانید انجام دهید چه می کنید ؟ اگر شما یک انسان باشید و کسی که از شما درخواست کرده نیز یک انسان باشد به او می گویید : “الان نه , سرم شلوغه” . اما اگر شما یک ماژول هسته باشید که یک پروسه از شما درخواستی کرده باشد امکان دیگری نیز خواهید داشت . شما می توانید پروسه را به حالت خواب ببرید تا زمانی که بتوانید به ان سرویس دهید .

ماژول هسته کد مثال که از اینجا [2] می‌توانید دریافت کنید مثالی از این کار است. در این مثال فایل proc/sleep/ توسط فقط یک پروسه در هر لحظه می تواند باز شود. اگر فایل در حال حاضر باز باشد ماژول هسته تابع wait_event_interruptible را صدا زده که باعث تغییر وضعیت کار ( task ) ( task ساختار داده ای در هسته است که اطلاعاتی در مورد پروسه و تابع سیستمی ای که پروسه در ان است نگه می دارد ) به حالت TASK_INTERRUPTIBLE می شود بدین معنا که کار تا زمانی که کسی ان پروسه را بیدار نکند ادامه نمی یابد و ان را به صفی به نام WaitQ که پروسه های منتظر دسترسی به فایل proc/sleep/ هستند اضافه می کند و به scheduler سیستم عامل دستور context switch را می دهد که CPU را به دست پروسه دیگری بدهد.

هنگامی که یک پروسه کار خود را با فایل به اتمام رساند ان را می بندد که باعث به صدا درامدن تابع module_close در ماژول هسته می شود. این تابع تمام پروسه های موجود در صف WaitQ را بیدار می کند و پروسه ای که بتواند فایل را تصاحب کند به کار خود ادامه می دهد. این پروسه کار خود را از تابع module_interruptible_sleep_on اغاز کرده و متغیری عمومی را تنظیم می کند تا به پروسه های دیگر نشان دهد که فایل هم اکنون باز شده است. پروسه های دیگر به دیدن وضعیت این متغیر به حالت خوابیده باز می گردند.

در این مثال ما با استفاده از دستور tail -f فایل مورد نظرمان را در پس زمینه باز نگه می داریم و با استفاده از فایل اجرایی cat_nonblock که با استفاده از دستور زیر ایجاد می شود فایل را دوباره باز می کنیم.


$gcc cat_nonblock.c -o cat_non_block


اگر با استفاده از دستور kill %1 اولین پروسه پس زمینه را بکشیم پروسه دوم که به حالت خوابیده رفته بود به کار خود ادامه داده و نهایتا پایان می یابد.

درباره مثال قسمت دوم : کد مثال را از اینجا [2] می‌توانید دریافت کنید. ماژول مثال را که در فایل sleep.c پیاده سازی شده است را با استفاده از دستور make کامپایل کنید و کد cat_nonblock.c را همانطور که در بالا اشاره شد با دستور gcc کامپایل کنید.

debian:~/lkmpg13/blockproc# insmod sleep.ko

debian:~/lkmpg13/blockproc# cat_noblock /proc/sleep

Last input:

debian:~/lkmpg13/blockproc# tail −f /proc/sleep &

Last input:

Last input:

Last input:

Last input:

Last input:

Last input:

Last input:

tail: /proc/sleep: file truncated

[1] 6540

debian:~/lkmpg13/blockproc# cat_noblock /proc/sleep

Open would block

debian:~/lkmpg13/blockproc# kill %1

[1]+ Terminated tail −f /proc/sleep

debian:~/lkmpg13/blockproc# cat_noblock /proc/sleep

Last input:

debian:~/lkmpg13/blockproc#


جزییات مثال در کد به صورت comment کامل توضیح داده شده است.

در اینجا این قسمت به پایان می رسد. در قسمت آینده چندین مطلب پیشرفته دیگر در ماژول نویسی هسته لینوکس را مورد بررسی قرار خواهیم داد.


ترجمه و تکمیل : سعید تقوی s.taghavi@ece.ut.ac.ir

[1]. /images/down/syscall.tar.bz2

[2]. /images/down/blockproc.tar.bz2

منابع :

1. http://www.tldp.org/LDP/lkmpg/2.6/html
2. http://www.linuxhq.com/guides/LKMPG/mpg.html

PDF Version


تمامی مطالب و مقالات این سایت تحت مجوز GNU FDL قرار دارند. بنابراین کپی و ایجاد تغییر در آنها مطابق شرایط این مجوز آزاد می‌باشد.