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

کامپایل کردن 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 سیستم را تغییر داد . اولویت ها به همراه ماکروها بصورت زیر است :

KERN_EMERG     "<0>"
KERN_ALERT     "<1>"
KERN_CRIT      "<2>"
KERN_ERR       "<3>"
KERN_WARNING   "<4>"
KERN_NOTICE    "<5>"
KERN_INFO      "<6>"
KERN_DEBUG     "<7>" 

برای دیدن پیغامی که تابع 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

با استفاده از دستور grep همانند بالا میتوان پیغام چاپ شده در log را مشاهده کرد :

sudo cat /var/log/messages.log | grep world

این نوشته با نگاهی به سایت The Linux Documentation Project نوشته شده است .

Leave a Comment

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