نوشتن Module برای Kernel لینوکس
در ادامه مقاله قبلیم(12 سال پیش!) که ماژول Kernel لینوکس رو تعریف کردم، در این مقاله به نوشتن ماژول برای Kernel لینوکس می پردازم.
در این مقاله، به صورت ساده یاد می گیریم که چگونه یک ماژول Hello World برای کرنل Linux بنویسیم، کامپایل و اجرا کنیم... و همه اینها بر پایه توزیع های معروف مثل Debian, RedHat و Slackware انجام میدیم.
ماژول های هسته لینوکس
هسته لینوکس / Linux kernel قلب سیستم عامل لینوکس است و مسئول مدیریت منابع سخت افزاری، ارتباط با سخت افزار و اجرای برنامه هاست. هسته لینوکس به صورت ماژولار طراحی شده، یعنی بسیاری از قابلیت های آن به صورت فایلهای جدا و Load شدنی (Loadable Kernel Modules - LKM) پیاده سازی می شوند.
ماژول های هسته Linux دارای چند نوع هستند: ماژول های درایور دستگاهها (Device drivers)، ماژول های فایل سیستم (Filesystem)، ماژول های شبکه (Network)، ماژول های امنیتی (Security) و ماژول های مدیریت حافظه و پردازش (Memory & process management). این ماژول ها باعث اضافه شدن feature در هسته، افزایش کارایی، سفارشی سازی آسان و نگهداری ساده تر خود هسته می شوند.
پیش نیازها برای توزیع های مختلف
جهت توسعه و تولید ماژول هسته لینوکس، به تعدادی ابزار و سورس نیاز هست که در زیر آنها را نصب می کنیم:
1. خانواده دبیان (Debian/Ubuntu)
sudo apt update
sudo apt install build-essential linux-headers-$(uname -r)
2. خانواده ردهت (Fedora/RHEL/CentOS)
sudo dnf install kernel-devel gcc make
3. اسلکور (Slackware)
sudo slackpkg install gcc make kernel-headers kernel-source
نوشتن ماژول Hello World (برای همه توزیع ها)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Hello World module for Slackware and other distros");
MODULE_VERSION("1.1");
static int __init hello_init(void) {
printk(KERN_INFO "Hello from Slackware Kernel!\n");
return 0;
}
static void __exit hello_exit(void) {
printk(KERN_INFO "Goodbye from Slackware Kernel!\n");
}
module_init(hello_init);
module_exit(hello_exit);
توضیحات کدهای بالا:
#include <linux/module.h>
: یک فایل هدر (header) است که به توسعه دهندگان ماژول امکان می دهد تا ماژول های خود را با هسته لینوکس ادغام کنند. این فایل شامل defines، structures، ماکروها و توابعی است که برای نوشتن ماژول های هسته ضروری هستند.MODULE_LICENSE("GPL")
: مشخص می کند که ماژول تحت پروانه GPL است (اجباری).-
printk
: تابع چاپ در Log کرنل (مشابهprintf
اما برای کرنل).KERN_INFO
سطح Log را تعیین می کند (در/var/log/kern.log
یاdmesg
دیده می شود).
-
__init
و__exit
:__init
تعیین می کند که تابع فقط یک بار هنگام لود اجرا شود و سپس حافظه آزاد شود.__exit
برای توابعی است که فقط هنگام حذف ماژول اجرا می شوند.
module_init
وmodule_exit
: توابع initialize و finalize ماژول را register می کنند.
کامپایل و نصب ماژول
جهت کامپایل و نصب ماژول می توان بصورت دستی هم عمل کرد و دستورات را وارد کرد، اما بهتره یک فایل builder بنام Makefile بسازید و محتویات زیر رو داخل وارد کنید.
و سپس دستور make رو در کنسول سیستم در همون مسیر بزنید تا ماژول تولید بشه.
1. برای دبیان/ردهت
obj-m := hello_world.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
2. برای اسلکور
obj-m := hello_world.o
KDIR ?= /usr/src/linux-$(shell uname -r)
PWD := $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
توضیحات کدهای بالا:
obj-m := hello_world.o
فایل ماژول نهایی: hello_world.ko
.
KDIR
مسیر هدر های کرنل.
PWD
مسیر فعلی.
make
ساخت ماژول (hello_world.ko
).
make clean
پاکسازی فایل های موقت.
نکته برای اسلکور:
در بعضی نسخه های Slackware ممکن است نیاز باشد مسیر کرنل را به صورت دستی مشخص کنید:
export KDIR=/usr/src/linux-$(uname -r)
اجرا و توقف ماژول
برای اجرا یا متوقف کردن ماژول از دستورات زیر استفاده می کنیم. این دستورات در همه توزیع های لینوکس موجود هستند:
1. برای لود ماژول
sudo insmod hello_world.ko
2. جهت بررسی خروجی
dmesg | tail -n 2
خروجی نمونه
[123456789] Hello from Slackware Kernel!
3. جهت حذف ماژول
sudo /sbin/rmmod hello_world
نکات ویژه Slackware
مسیر کرنل:
در Slackware کرنل در/usr/src/linux-$(uname -r)
قرار دارد.تفاوت در ابزارها:
در Slackware ازinsmod/rmmod
به جایmodprobe
استفاده می شود..-
مدیریت ماژول ها:
برای لیست ماژول های لود شده در Slackware:cat /proc/modules
تفاوت syscall با function call
در حین توسعه هسته یا ماژول های هسته ممکنه با واژه های syscall زیاد سروکار داشته باشید، اما syscall دقیقا چی هست:
- معقوله system call (یا همون syscall) با محیط kernel در ارتباطه ولی function call با محیط user در ارتباطه.
- system call با سیستم عامل تعامل برقرار می کنه ولی function call با برنامه و کتابخانه.
- بخاطر گستردگی و تودرتو بودن system call، معمولاً منابع بیشتری صرف call کردن اونها میشه، ولی function call هزینه های کمتری رو دربر میگیره.
- دستورات system call توسط function call صدا زده میشه ولی function call توسط برنامه صدا زده میشه.
- یک دستور system call تکه ای کد در kernel-space هستش ولی function call تکه ای کد در user-space هستش.
تنها شباهت اینها در مهیا کردن خدمات به caller هستش.
مشکلات رایج
مشکل: Kernel Panic پس از لود ماژول
دلایل: دسترسی به حافظه نامعتبر (NULL pointer dereference) یا استفاده از توابع فضای user در کرنل (مثل printf به جای printk یا kmalloc بجای malloc)
مشکل: ماژول روی نسخه های مختلف هسته کار نمی کند
دلایل: تغییرات API کرنل بین نسخه ها. پس کد را با آخرین نسخه های کرنل تست کنید یا از LINUX_VERSION_CODE برای بررسی نسخه کرنل استفاده کنید
مشکل: Memory Leak در ماژول
دلایل: آزاد نکردن حافظه در تابع exit یا فراموش کردن آزادسازی منابع. می توانید از ابزارهایی مانند kmemleak برای تشخیص leak حافظه استفاده کنید.
معرفی ماژولهای اوپن سورس جهت یادگیری
1. پروژه eBPF
مانیتورینگ شبکه و trace سیستم
لینک: github.com/iovisor/bcc
2. پروژه FUSE
پیاده سازی سیستم فایل در User-space
لینک: github.com/libfuse/libfuse
3. پروژه Open-IOMMU
درایور IOMMU برای مدیریت دسترسی به حافظه
لینک: github.com/OpenIOMMU
4. پروژه LTTng
سیستم trace عملکرد کرنل جهت دیباگ و آنالیز سیستم
لینک: github.com/lttng
5. پروژه KVM
توضیحات: ماژول مجازی سازی جهت ساخت ماشین های مجازی
اگر قبلا در بیان ثبت نام کرده اید لطفا ابتدا وارد شوید، در غیر این صورت می توانید ثبت نام کنید.