کامپایل کردن Kernel Module در لینوکس ۱

Posted by

کامپایل کردن 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

Leave a Reply

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *