کامپایل کردن Kernel Modules ، چرا ؟ و چگونه ؟
یکی از جذاب ترین کارهای ممکن در لینوکس کامپایل کردن کرنل و به تبع این کار نوشتن ماژول هایی برای کاربردی خاص در کرنل لینوکس است . مثلا فرض کنید بعد از آپدیت کرنل لینوکس ، یکی از درایور ها از کار افتاده و بعد از درخواست از سازنده ، کد C درایور را دریافت میکنید . در این حالت کامپایل کد C و الحاق ماژول ایجاد شده بر عهده خود شما خواهد بود . این کار نه تنها برای اضافه کردن درایور های سخت افزاری بلکه برای انجام کاری خاص در فضای کرنل که شما تعریف میکنید کاربرد دارد .
من فرض میکنم شما قبلا برنامه هایی معمولی با زبان C نوشته اید و با قابلیت های اساسی این زبان آشنا هستید و قصد دارید قدرت این زبان برنامه نویسی را در کرنل لینوکس ،جایی که با یک اشتباه مهلک میتوانید فایل های سیستمی لینوکس خود را از دست بدید به کار بگیرید .
اما واقعا Kernel Module چیست ؟ ماژول های کرنل یک قطعه کد هستند که با درخواست سرویس های دیگر میتوانند در کرنل بارگزاری (Load) شوند یا از کرنل پایین آورده شوند (UnLoad) . این ماژولها به توسعه قابلیت های کرنل کمک میکنند . بارگزاری این ماژولها در کرنل معمولا احتیاجی به ریبوت سیستم ندارد . یکی از مثالهای خوب در این مورد اضافه کردن سخت افزارها به کرنل لینوکس است ، با اضافه شدن هر سخت افزار ماژول درایور آن در فضای کرنل راه اندازی میشود . در صورت وجود نداشتن این قابلیت در لینوکس ما با یک کرنل یکپارچه روبرو میشدیم که درایور هر قطعه باید مستقیما در ایمیج کرنل قرار میگرفت . در این صورت برای توسعه هر قابلیت در کرنل یا اضافه کردن هر سخت افزار به سیستم باید این کرنل بزرگ دوباره ساخته شده و سیستم از نو راه اندازی میشد .
پیش نیازهای کامپایل Kernel Module
برای کامپایل Kernel Module در لینوکس باید kernel headers packages در سیستم نصب باشد ، در حالت عادی این پکیج ها در سیستم نصب نیست ،برای نصب در خانواده دبیان بصورت زیر عمل میکنیم :
$ sudo apt-get update $ apt-cache search linux-headers-$(uname -r) $ sudo apt-get install linux-headers-$(uname -r)
در آرچ لینوکس از دستور زیر استفاده میکنیم :
sudo pacman -Sy sudo pacman -S linux-headers
برای تست به نوشتن یک برنامه ساده اکتفا میکنیم:
/* * hello-1.c - The simplest kernel module. */ #include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_INFO */ int init_module(void) { printk(KERN_INFO "Hello world 1.\n"); /* * A non 0 return means init_module failed; module can't be loaded. */ return 0; } void cleanup_module(void) { printk(KERN_INFO "Goodbye world 1.\n"); }
برنامه بالا را بنام hello-1.c سیو میکنیم و یک فایل تکست بنام Makefile بصورت زیر در محل سورس برنامه فوق ایجاد میکنیم:
obj-m += hello-1.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
از نظر تکنیکی تنها وجود خط اول Makefile کاملا ضروری است . بعد از نوشتن Makefile با استفاده از ترمینال Kernel Module ای که نوشتیم را با نوشتن دستور make در ترمینال کامپایل میکنیم ، دقت کنید که با نوشتن دستور کامپایل از کد توسط gcc نمیتوانیم مستقیما kernel Module را کامپایل کنیم .
اضافه کردن Kernel Module به هسته لینوکس
برای اضافه کردن ماژولی که نوشتیم از دستور insmod استفاده میکنیم :
$ su - # insmod hello.ko
تحلیل کارکرد Module نوشته شده
در Kernel Modules ما حداقل ۲ تابع داریم . تابع init_module برای مقدار دهی اولیه -initialization- برنامه ما در آغاز برنامه و در هنگام استارت Module استفاده میشود . و تابع cleanup_module در انتهای برنامه و هنگامی که برنامه از کرنل پایین آورده میشود -UnLoad- اجرا میشود .
معمولا تابع init_module یک کارکرد جدید برای کرنل ایجاد میکند و یا یکی از قابلیت فعلی کرنل را تغییر داده و تعریف جدیدی از آن ارائه میدهد . و تابع cleanup_module قابلیتهایی که تابع init_module تغییر داده بود به حالت پیش فرض خود باز میگرداند .
هر Kernel Module حتما باید شامل هدر فایل linux/module.h باشد . در اینجا ما برای استفاده از تابع printk از هدر فایل linux/kernel.h استفاده کردیم .
تابع ()printk یک تابع برای نوشتن اطلاعاتی در log سیستم میباشد . سیستم لینوکس دارای ۸ اولویت میباشد که با نوشتن ماکرو در تابع printk میتوان اولویت نوشتن در log سیستم را تغییر داد . اولویت ها به همراه ماکروها بصورت زیر است :
Name String Meaning alias function KERN_EMERG "0" Emergency messages, system is about to crash or is unstable pr_emerg KERN_ALERT "1" Something bad happened and action must be taken immediately pr_alert KERN_CRIT "2" A critical condition occurred like a serious hardware/software failure pr_crit KERN_ERR "3" An error condition, often used by drivers to indicate difficulties with the hardware pr_err KERN_WARNING "4" A warning, meaning nothing serious by itself but might indicate problems pr_warning KERN_NOTICE "5" Nothing serious, but notably nevertheless. Often used to report security events. pr_notice KERN_INFO "6" Informational message e.g. startup information at driver initialization pr_info KERN_DEBUG "7" Debug messages pr_debug, pr_devel if DEBUG is defined KERN_DEFAULT "d" The default kernel loglevel
برای دیدن پیغامی که تابع printk در log سیستم چاپ میکند باید برنامه syslogd در حال سرویس باشد :
sudo pacman -S syslog-ng systemctl enable syslog-ng systemctl start syslog-ng cat /var/log/messages.log
با توجه به اینکه هر لحظه message های زیادی وارد فایل messages.log میشود میتوانیم از دستور grep برای پیدا کردن message مورد نظر خودمان استفاده کنیم :
sudo cat /var/log/messages.log | grep Hello
پایین آوردن -UnLoad- کرنل ماژول
برای unload کردن یک Kernel Module از دستور rmmod استفاده میکنیم :
rmmod hello-1
این نوشته قبلا در وبلاگ من به آدرس https://nutshell.ir منتشر شده بود.
هیچ نظری موجود نیست:
ارسال یک نظر