تبليغاتX
آموزش و ترفندهای لینوکس ردهت 9

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

در قسمت قبل با Device Driver‌راه انداز‌ها به عنوان دسته مهمی‌ از ماژول‌های هسته اشنا شدیم و بعضی مفاهیم بنیادی در هسته لینوکس را مــورد ارزیـابی قرار دادیم.در این قسمت و قسمت اینده توجهمان را به دسته بسیار مهمی‌از راه انداز‌ها که همان راه اندازهای دستگاه‌های کاراکتری هستند معطوف می‌نماییم.


راه انداز‌های دستگاه‌های کاراکتری


ساختار داده file_operations
ساختار داده file_operations در <linux/fs.h> تعریف شــده است و اشــاره گرهایی به توابع مختلف که توسط راه انداز تعریف شده و عملیات مختلفی بر روی دستگاه انجام می‌دهند را نگه داری می‌کند. هر فیلدی از این ساختار داده متناظر آدرس تابعی تعریف شده از راه انداز است که می‌تواند یک درخواست را براورده سازد. به عنوان مثال هر راه انداز دستگاه کاراکتری نیاز به تابعی دارد که بتواند از دستگاه بخواند.
ساختار داده file_operations ادرس توابع مـاژول که این عملیات را انجام می‌دهند در خود نگه می‌دارد. در ذیل تعریف این ساختار داده را بر اساس هسته 2.6.13 مشاهده می‌کنید:

کد:

struct file_operations {        struct module *owner;        loff_t (*llseek) (struct file *, loff_t, int);        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);        ssize_t (*aio_read) (struct kiocb *, char __user *, size_t,  loff_t);        ssize_t (*write) (struct file *, const char __user *, size_t,  loff_t *);        ssize_t (*aio_write) (struct kiocb *, const char __user *,size_t, loff_t);        int (*readdir) (struct file *, void *, filldir_t);        unsigned int (*poll) (struct file *, struct poll_table_struct *);        int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);        int (*mmap) (struct file *, struct vm_area_struct *);        int (*open) (struct inode *, struct file *);        int (*flush) (struct file *);        int (*release) (struct inode *, struct file *);        int (*fsync) (struct file *, struct dentry *, int datasync);        int (*aio_fsync) (struct kiocb *, int datasync);        int (*fasync) (int, struct file *, int);        int (*lock) (struct file *, int, struct file_lock *);        ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);        ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);        ssize_t (*sendfile) (struct file *, loff_t *, size_t,  read_actor_t, void *);        ssize_t (*sendpage) (struct file *, struct page *, int, size_t,  loff_t *, int);        unsigned long (*get_unmapped_area)(struct file *,unsigned long, unsigned long, unsigned long,  unsigned long);        int (*check_flags)(int);        int (*dir_notify)(struct file *filp, unsigned long arg);        int (*flock) (struct file *, int, struct file_lock *);};

بعضـی از عمـــلگرها توسط راه انداز پـیاده سازی نمی‌شوند. به عنوان مثال یک راه انداز کارت گرافیک نیازی ندارد که تابع مربوط به خواندن از ساختارهای دایرکــتوری ( directory structures ) را پیــاده ســازی کند. فیــلد متناظر در ساختار داده file_operations که استفاده نمی‌شود به NULL مقدار دهی می‌شود.
امکاناتی در GNU C Compiler ) gcc ) وجــود دارد کــه مقدار دادن به این ساختار داده را بسیــار راحت می‌کند. شما این موارد را در راه اندازهای مدرن امروزی خواهید دید که ممکن اســت باعث تعجب شما شـود. در زیر نحوه مقدار دهی را با استفاده از این امکانات می‌بینید:
بعضـی از عمـــلگرها توسط راه انداز پـیاده سازی نمی‌شوند. به عنوان مثال یک راه انداز کارت گرافیک نیازی ندارد که تابع مربوط به خواندن از ساختارهای دایرکــتوری ( directory structures ) را پیــاده ســازی کند. فیــلد متناظر در ساختار داده file_operations که استفاده نمی‌شود به NULL مقدار دهی می‌شود.
امکاناتی در GNU C Compiler ) gcc ) وجــود دارد کــه مقدار دادن به این ساختار داده را بسیــار راحت می‌کند. شما این موارد را در راه اندازهای مدرن امروزی خواهید دید که ممکن اســت باعث تعجب شما شـود. در زیر نحوه مقدار دهی را با استفاده از این امکانات می‌بینید:

کد:

struct file_operations fops = {   read: device_read,      write: device_write,        open: device_open,      release: device_release<};

Syntax رایج تر برای مقدار دادن به این ساختار داده روش C99 است که برای ســازگاری بیشتر توصیه می‌گردد که از این روش استفاده گردد. در زیر این روش را مشاهده می‌کنید:

کد:

struct file_operations fops = { .read = device_read,   .write = device_write,     .open = device_open,    .release = device_release};

معنی هر دو ساختار تقریبا مشخص است. تــوجه داشته باشید کـــه بقیـه فیـــلدهای سـاختار داده file_operations که توسط دو روش بالا مقداردهی به صورت مشخص نگردد توسط gcc به NULL مقدار دهی می‌شود.


ساختار داده file
هر دستگاه در هسته توسط یک ســاختار داده file نشان داده می‌شود کـــه در <linux/fs.h> تعــریف شده است. توجه داشته باشید که این ساختار یک ساختار سطح هسته است و هرگــز در بـــرنــامه‌های معمـولی که در فضای کاربر اجرا می‌شوند ظاهر نخواهد شد. ایــن ســاختــار بــا ساختار FILE که در کتابخانه استاندارد زبان C تعریف شده است متفاوت می‌باشد.
نام این ساختار ممکن است شما را دچار اشتباه کند. در حقیقت این ساختار یک تجرید ( abstraction ) می‌باشد و تصور اینکه این ساختار یک فایل در دیسک که با ساختار inode مشخص می‌گردد را نشان می‌دهد نیز کاملا اشتباه است.
یک اشاره گر به ساختار داده file معمولا filp نامیده می‌شود. در زیر تعریف ساختار file در هسته 2.6.13 را می‌بینید:

کد:

struct file {        struct list_head                f_list;        struct dentry                       *f_dentry;        struct vfsmount           *f_vfsmnt;        struct file_operations              *f_op;        atomic_t                             f_count;        unsigned int                      f_flags;        mode_t                         f_mode;        loff_t                          f_pos;        struct fown_struct   f_owner;        unsigned int              f_uid, f_gid;        struct file_ra_state  f_ra;        size_t                  f_maxcount;        unsigned long     f_version;        void                   *f_security;        /* needed for tty driver,and maybe others */        void                *private_data;#ifdef CONFIG_EPOLL        /* Used by fs/eventpoll.c to link all the hooks to this file */        struct list_head   f_ep_links;        spinlock_t            f_ep_lock;#endif /* #ifdef CONFIG_EPOLL */        struct address_space   *f_mapping;};

بیشتر فیلدهایی که در این ساختار به کار رفته اند ( مانند dentry ) معمولا توسط راه اندازها استفاده نمی‌شوند.

+ نوشته شده توسط امیرحسین عربی زاده در سه شنبه بیست و یکم فروردین 1386 و ساعت 22:21 |

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

در قسمت‌های قبل با مفاهیم اولیه ماژول‌های هسته اشنا شدیم و به عنوان مثال چند ماژول بسیار ساده نوشتیم و ان ها را در هسته load کـــردیم. در‌این قسمت به بسط مفاهیم قبلی خواهیم پرداخت و بـــا Device Driver ها به عنوان دسته مهمی‌از ماژول‌های هسته لینوکس اشنا خواهیم شد.


ماژول ها چگونه اغاز و پایان می‌یابند؟
برنامه‌های معمولی , معمولا با تابعی به نام ()main اغاز شده , لیستی از دستورات را انجام داده و به پایان می‌رسند. ماژول‌های هسته در‌این مورد متفاوت عمل می‌کنند. یک ماژول همیشه با تابع ()init_module و یا تابعی که به وسیله ماکروی ()module_init به عنـــوان ورودی ثبت شده اغاز می‌گردد. در حقیقت‌این تابع قابــلیت خود را به هسته اعلام می‌دارد و به هسته‌این امــکان را داده که در موقع نیاز از توابع ماژول استفاده کند. پس از‌اینکه‌این تابع به پایان می‌رسد توابع ماژول دیگر اجرا نخواهند شد تا زمانی که هسته بخواهد از‌این توابع استفاده کند.
تمامی‌ماژول ها با صدا کردن cleanup_module() و یا تابعی که به وسیله ماکروی module_exit() به عنوان ورودی ثبت شده به پایان می‌رسند.‌این تابع تمامی‌اعمالی را که تابع ورودی انجام داده خنثی می‌کند.


توابعی که در اختیار ماژول ها هستند
توسعه دهندگان معمولا از توابعی استفاده می‌کنند که خود انها را تعریف نکرده اند. یک مثال ساده از‌این توابع , تابع ()printf می‌باشد. شما از‌این توابع که در کتابخانه استاندارد زبان C تعریف شده اند استفاده می‌کنید. کد‌این توابع تا زمان لینک یا پیوند در کد شما وارد نمی‌شوند و در زمـــان لینک ادرس موردنظر در کد شما به آدرس‌ این کد اشاره داده خواهد شد.
ماژول‌های هسته در‌این مورد نیز متفاوت هستند. شما اگر در مثال‌های قسمت قبل دقت کرده باشید ما از تابعی به نام ()printk استفاده کردیم امـــا از کتابخانه استانداردی بــرای IO استفاده نکردیم.‌ ایــن مــوضــوع بــه‌ ایـن خـاطر است که ماژول ها فایل‌های object‌ای هستند که سمبول‌هایشان در هنــــگــام insmod مشخص می‌گردند. کد‌این سمبول ها در خود هسته وجود دارد. اگــر می‌خواهیـــد سمبــــولهایی را کـــه هسته تعــــریف کـــرده اســت را مشاهده کنید به فایل proc/kallsyms/ رجوع کنید.
یکــی از نـکات بسیار مهمی‌که باید مـــورد توجه قرار گیـــرد تفـــاوت بین توابع کتابخانه‌ای ( library functions ) و توابع سیستمی‌( system calls ) است. تــوابـــع کتــابخانه‌ای توابعی سطح بالا هستند که در فضای کاربر اجرا می‌شوند و در حقیقت واسط بین کاربر و توابع سیستمی‌که اصل کار هر برنامه را می‌کنند می‌باشند. توابع سیستمی‌در فضای هسته اجرا می‌شوند. تابع کتابخانه‌ای ()printf یک تابع نمایش بسیار عمومی‌به نظر می‌اید اما در حقیقت‌این تابع رشته ورودی را شکل دهی کرده و ان را تحویل تابع سیستمی‌()write ( که کار حقیقی را انجام می‌دهد ) می‌دهد.
برای‌اینکه از جزییات عملکرد ()printf با خبر شوید کد زیر را در یک فایل به نام hello.c بنویسید.

کد:

#include <stdio.h>

int main() { printf("hello"); return 0; }

و با دستور زیر ان را به فایل اجرایی hello تبدیل کنید:

$ gcc -Wall -o hello hello.cحال در‌این مرحله hello را به صورت زیر اجرا کنید:

$ strace ./helloچیزی که مشاهده خواهید کرد مجموعه توابع سیستمی‌است که برنامه hello صدا زده است. در چند خط اخر خروجی دستور قبل , خطی به صورت زیر خواهید دید :

write( 1 , "hello" , 5hello )این خط در حقیقت خطی است که اصل عملکرد ()printf اجرا شده است. برای اگاهی بیشتر از تابع سیستمی‌()write دستور man 2 write را اجرا کنید.
شما حتی می‌توانید ماژول‌هایی بنویسید که توابع سیستمی‌هسته را تغییر دهد. cracker ها معمولا از‌این خاصیت برای نوشتن backdoor یا trojan ها استفاده می‌کنند.
فضای کاربر در مقابل فضای هسته
هسته به تمام منابع سیستم دسترسی مستقیم دارد.‌این منابع می‌توانند کارت ویدیو , دیسک سخت یا حافظه باشند. در حالی که برنامه‌های معمولی، بــر سر تصاحب منابع سیستم رقابت دارند. هم اکنون که من درحال نوشتن‌این متن هستم، updatedb در حال به روز رسانی پایگاه داده locate می‌باشد، logger ها در حـــال ثبـــت وقـایع هستند. بنابراین برنامه‌های syslogd ،updatedb ،openoffice.org بـــه طــــور همــزمان از هارد دیسک استفاده می‌کنند. واضح است که هسته بایستی ترتیب استفاده را مشخص کند و به برنامه‌ها و کاربران اجازه دسترسی بـــه منـــابع هـر زمان که دوست دارند ندهد.
بــرای رسیدن به‌این هدف یک CPU در حالـــت‌های مختلفی می‌تواند بــــه اجرای دستورات بپردازد. هــر حــــالتی سطح مختلــفــی از آزادی بـــرای کســـب منــابــع سیستم در اختیار کاربران قرار می‌دهد. معـماری Intel 80386 چهار حالت یا اصطلاحا mode دارد. لینوکس تنها از دو حالت استفاده می‌کند:
۱) بالاترین سطح آزادی یا حالت سرپرست و مدیر: که در‌ ایــن حالت همه چیز امکان پذیر است و به همه منابع سیستم می‌توان دسترسی مستقیم داشت.
۲) پایین ترین سطح آزادی یا حالت کاربر: که در‌این حالت استفـــاده از منابع سیستم تنـــها بـــا اجازه هسته امکان پذیر است.
بحث قبلی در مورد توابع کتابخانه‌ای و توابع سیستمی‌را به خاطر آورید. تــوابع کتابخانه‌ای اغلب در حالت کاربر استفاده می‌شوند.‌این توابع نیز یک یا چند تابع سیستمی‌را صدا می‌زنند.‌ ایـن توابع سیستمی‌با‌این که از طرف توابع کتابخانه‌ای صـــدا زده می‌شوند در فضای هسته اجرا می‌شــوند بدلیل‌ اینــــکه‌ ایـن توابع بخشی از هسته هستند. هنــگــامی‌ کــه تابع سیستمی‌به طور کامل اجرا شد حالت اجرای دستورات به حالت کاربر بر می‌گردد.


فضای متغییرها (Name Space)
هنگامی‌ کــه شما یک برنامه به زبان C می‌نویسید از اسامی‌راحتی به عنوان نام متغیرهایتان استفاده می‌کنید و با‌ این کار خود سعی در هرچه بیشتر خوانا تر کردن کد خود می‌کنید. اگــر شــما روتین‌هایی بنویسید که بخشی از یک مساله بــزرگتر باشند، هر متغیر عمومی‌یا global که استفاده می‌کنید جزیی از مجموعه متغیرهای عمومی‌دیگران خـــواهد بود. بنابراین مواردی پیش خواهد امد که متغیر‌های عمومی‌در دو کد مجزا یکسان باشند و کار دچار مشکل شود.
هنگامی‌ کـــه یک برنامه دارای متغیرهای عمومی‌بسیاری می‌باشد کـــه بـــه اندازه کافی معنادار نیستند به‌این مساله آلودگی فضای متغیرها یا name space pollution گویند.
در پروژه‌های بزرگ بایستی سعی شود که روش‌هایی برای‌ایجاد نام‌های متغیرها‌ ایجاد شوند که نام متغیرها هــم یکتا باشند و هم دارای معنی مناسب باشند. یکی از همین پروژه‌های بسیار بزرگ هسته لینوکس است. هنــگام نوشتن کد هسته، حتـــی کوچکترین ماژول نیز با کل هسته پیوند ( link ) خواهد شد. بــنابراین‌این موضوع از اهمیت بسیار بالایی برخوردار است. بهترین راه برای حل‌این مساله تعریف کردن متغیرهای عمومی‌ بــه صورت static و یا‌ استفاده از پیشوند یــا پسوند‌های مناسب برای نام گذاری است.معمولا تمام پیشوند‌هایی که در هسته تعریف می‌شوند با حروف کوچک اغاز می‌شوند.
اگـــر شما نمی‌خواهید که متغیرهایتان را به صورت static تعریف کنید گزینه دیگری که پیش روی شماست تعریف یک جدول سمبول ها ( symbol table ) و ثبت آن در هسته است. بعدا به‌این مقوله بیشتر خواهیم پرداخت.
فایل proc/kallsyms/ تمامی‌ سمبول‌هایی که در هسته تعریف شده‌اند و شما می‌توانید از آنـــها برای مــــاژول‌های خود استفاده کنید را نگهداری می‌کند.


فضای کد (Code Space)
مدیریت حافظه یکی از پیچیده ترین و با اهمیت ترین مــوضـــوعات در هسـته است (موضوعی که بیشتر کتاب OReilly’s Understanding the Linux Kernel در‌این باره می‌باشد). مـــا در‌این راهنما قصد نداریم کـــه در زمیـــنه مدیـــریـت حافظه حـــرفه‌ای شویم. اما نیاز داریم که حقایق بسیار مهمی‌را بدانیم تا بتوانیم مـــاژول‌های واقـــعی بـــرای هــستـه لینوکس بنویسیم.
اگـــر شمـــا چـیزی در مورد segfault ها نمی‌دانید ممکن است متعجب شوید که اشاره گر ها (pointers) که در برنامه نویسی به خصوص با زبان C به کار می‌روند واقعا به آدرسی از حافظه اشاره نمی‌کنند.
هنگامی‌که یک پروسه ایجاد می‌شود، هسته قسمتی از حافظه فیزیکی را گرفته و در اختیار پروسه قــرار می‌دهد که برای اجرای کد، نگهداری متغیرها، heap ،stack و تمام چیزهایی که یک پروسه نیاز دارد از ان استفاده کند.‌این فضا برای تمام پروسه‌ها از آدرس $0$ شروع شده و به میزان خطوط آدرس دهـــی ( Address Bus ) حـــافظه (32^2 بایت) قـــابل گسترش است. از آنجایی کــه پروسه‌ها بــر روی یکــدیگر قرار نمی‌گیرند، بنـــابراین هر چند پروسه کـــه بــه یــک آدرس دسترسی دارند ( مثلا 0xbfffff978 ) در حقیقت به آدرس‌های متفاوتی از حافظه فیزیکی دسترسی دارند!‌ ایـــن مطــلب دقیقا همان است که اشاره گرها به آدرسی از حافظه فیزیکی اشاره نمی‌کنند.
در حقیقت در تمام پروسه‌هایی که دارای اشاره گری با مقدار 0xbfffff978 هستند‌، این اشاره گر به نوعی از offset که فقط در آن پروسه تعریف شده است اشاره می‌کند. هیــچ وقت دو پروسه نمی‌توانند بـــه فضای یکدیگر وارد شوند ( البته راه‌هایی وجود دارد که بعدا ذکر خواهیم کرد).
خود هسته نیز فضایی از حافظه را برای خود در اختیار دارد. یک ماژول کدی است که به صورت دینامیک می‌تواند وارد‌ این فضا از حافظه شده یا از ان خارج شود. توجه به‌این نکته بسیار حیاتی است که هر ماژول سهمی‌از فضای هسته را استفاده می‌کند و فضای جدیدی برای آن گرفته نمی‌شود. بنابراین اگر ماژول شما دچار segmentation fault شود، کل هسته دچار segmentation fault خواهد شد.
نکته دیگر‌این است که‌این بحث برای تمام سیستم‌هایی که به صورت microkernel هستند درست است. دو نمونه از‌این سیستم عامل ها GNU Hurd و QNX Nutrino هستند.


راه انداز ها (Device Drivers)
یکی از انواع ماژول ها راه اندازهای قطعات سخت افزاری هستند کـــه روتین‌های لازم بــرای استــفـاده از قطعات سخت افزاری مانند کارت TV یا پورت سریال و.... را در اختیار قرار می‌دهند.
در Unix هر قطعه سخت افزاری با یک فایل که در dev/ قرار می‌گیرد مشخص می‌گـــردد. به‌این فایل، فــایـــل دســتگاه (device file) گویند که برای برقراری ارتباط برنامه‌های مختلف با قطعه سخت افزاری مورد نظر می‌باشد.
راه انـــدازی که به‌این فایل مربوط می‌شود، در مقابل ارتباط برنامه‌های کاربران در مـــورد‌ ایـن قطعه سخت افزاری پاسخ می‌دهد. بنا براین راه انداز کارت صدایی مانند es1370.o فایل دستگاه dev/sound/ را به کارت صدای Ensoniq IS1370 متصل می‌کند. یک برنامه مانند mp3blaster می‌تواند از dev/sound/ بدون‌اینکه حتی بداند چه کارت صدایی نصب شده است استفاده کند.


اعداد اصلی ( major ) و فرعی ( minor )
بیایید چند فایل device را مورد بررسی قرار دهیم. در مثال زیر 3 پارتیشن اول هارددیسک master نشان داده شده اند:

کد:

$ ls -l /dev/had[1-3]

brw-rw---- 1 root disk 3, 1 Jul 5 2000 /dev/hda1

brw-rw---- 1 root disk 3, 2 Jul 5 2000 /dev/hda2

brw-rw---- 1 root disk 3, 3 Jul 5 2000 /dev/hda3

بــه ستـــونی که با کاما از هم جـــدا شده‌اند تـــوجه کنید. اولیــن عدد در‌این ستون عدد اصلی دستگاه یا device major number نامیده می‌شود. دومین عدد در‌این ستون عدد فرعی دستگاه یا device minor number می‌باشد. عدد اصلی به شما می‌گوید که چه راه اندازی برای‌این دستگاه مورد استفاده قرار گرفته است. به هر راه اندازی یک عدد یکتا نسبت داده شده است. تمام دستگاه‌های با عدد اصلی یکسان توسط یک راه انداز کنترل می‌شوند. در مثال بالا عدد اصلی هر سه دستگاه 3 می‌باشد که نشان می‌دهد که هر سه توسط یک راه انداز کنترل می‌شوند.
عدد فرعی توسط خود راه انداز برای تمایز بین دستگاه‌هایی که تحت کنترل دارد استفاده می‌شود. در مثال بالا با‌اینکه هر سه دستگاه توسط یک راه انداز کنترل می‌شوند ولی عددهای فرعی متفاوتی ( و البته یکتا ) دارند چون که از دید راه انداز ان ها سه دستگاه متفاوت هستند.
دستگاه ها به دو دسته تقسیم می‌شوند:
۱) دستگاه‌های کاراکتری ( character devices )
۲) دستگاه‌های بلوکی ( block devices )
تفاوت بین‌ این دو دسته در‌ این است که دستگاه‌های بــلــوکی برای انجام تقاضاهای مختلف از بافر ( buffer ) استفاده می‌کنند. در دستگاه‌های ذخیره سازی اطلاعات خواندن یا نوشتن اطلاعات به صورت مجموعه‌ای ( بلوک یا سکتور ) از اهمیت بالایی برخوردار است. تفاوت دیگر بین‌این دو نوع دستگاه‌این است که دستگاه‌های بلوکی فقط به صورت بلوکی از داده ها ( که می‌تواند اندازه متغیری داشته باشد ) ورودی دریافت می‌کنند و خــروجــی بـــر می‌گـــردانند در حالی که دستگاه‌های کاراکتری به هر تعداد بایت می‌توانند ورودی دریافت کنند و خروجی برگردانند.
بیشتر دستگاه ها از نوع کارکتری هستند به دلیل‌اینکه در اکثر موارد نیازی به‌این نوع بافر وجود ندارد و انها اغلب با یک بلوک ثابتی از داده ها کار نمی‌کنند. برای فهمیدن‌اینکه کدام دستگاه به صورت بلوکی و کدام به صورت کاراکتری عمل می‌کنند اولین حرف از خروجی دستور ls –l نـــوع دستـــگاه را مشخص می‌کند. اگـــر c بــود کاراکتری و اگر b بود بلوکی می‌باشد. در مثال سه پارتیشن هارد دیسک همگی از نوع بلوکی هستند.
مثالی از دستگاه‌های کاراکتری پورت سریال می‌باشد :

کد:

$ls -l /dev/ttyS[0-3]

crw-rw---- 1 root dial 4, 64 Feb18 23:34 /dev/ttyS0

crw-rw---- 1 root dial 4, 65 Nov17 10:26 /dev/ttyS1

crw-rw---- 1 root dial 4, 66 Jul 5 2000 /dev/ttyS2

crw-rw---- 1 root dial 4, 67 Jul 5 2000 /dev/ttyS3

اگر می‌خواهید بدانید که چه اعداد اصلی در حال حاضر ثبت شده هستند به فایل زیر در کد منبع هسته لینوکس مراجعه کنید :

linux/Documentation/devices.txtهنگامی‌که سیستم را نصب می‌کنید تمام ان فایل‌های دستگاه ها با دستور mknod‌ایجاد می‌شوند.
برای‌ایجاد یک فایل دستگاه جدید از نوع کاراکتری به نام coffee با اعداد اصلی و فرعی 12 و 2 به صورت زیر می‌توان عمل کرد :

# mknod /dev/coffee c 12 2معمـولا فایل‌های دستگاه ها در dev/ قرار می‌گیرد. لینوس توروالدز فایل‌های دستگاه‌هایش را برای اولین بار در dev/ قرار داد و‌ ایــن کــــار تقریبا مرسوم شده است. با‌ این حال اگـــر برای تست ماژول هسته‌ای کــــه نوشته‌اید می‌خواهید فایل دستگاهی‌ ایجاد کنید، بهتر است که آن را در دایرکتوری جاری قرار دهید.
به عنوان اخرین نکته، هنـــگامی‌ کـه ما می‌گوییم سخت افزار منظورمان کمی‌ متفاوت نسبت به یک قطعه سخت افزاری مثلا PCI Card است که شما می‌توانید در دست خود بگیرید.
به مثال زیر توجه کنید :

کد:

$ ls -l /dev/fd0 /dev/fd0u1680

brwxrwxrwx 1 root floppy 2, 0 Jul 5 2000 /dev/fd0

brw-rw---- 1 root floppy 2, 44 Jul 5 2000 /dev/fd0u1680

با توجه به چیزی که تاکنون گفتیم شما به راحتی می‌توانید بگویید که هر دو دستگاه بالا بلوکی هستند و توسط یـک راه انداز کنترل می‌شوند. شما ممکن است متوجه شده باشید که هر دو درایو فلاپی شما را نشان می‌دهند. در صورتی که شما فقط یک دستگاه فلاپی در سیستم تان دارید. پس چرا 2 فایل دستگاه برای آن‌ ایجاد شده است ؟ جواب‌ ایـن سوال در حقیقت پاسخ مساله‌ای است که در بالا اشاره شد.
اولین فایل فلاپی شما با 1.44MB حافظه را مشخص می‌کند. دومین فایل همان فـــلاپی است با‌ ایـــن تفاوت که توانایی خواندن و نوشتن فـــلاپی‌های بـــا حـــافــظه 1.68MB ( که به آنها super formatted می‌گویند) را دارد. بنابراین مشاهده می‌کنید که دو فایل دستگاه یک دستگاه را مشخص می‌کند. بنابراین در بحث مان بیشتر به معنی دستگاه و سخت افزار توجه داشته باشید. در‌ اینجا‌این قسمت به پایان می‌رسد. در قسمت‌ آینده در مورد درایور‌های دستگاه‌های کاراکتری که از اهمیت ویژه‌ای در ماژول نویسی هسته لینوکس برخوردارند بحث خواهیم کرد.

+ نوشته شده توسط امیرحسین عربی زاده در دوشنبه ششم فروردین 1386 و ساعت 0:28 |

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


فرستادن ورودی های خط دستور به یک ماژول
ماژولها می‌توانند ورودی‌هایی از طـریق خط دستور دریافت کنند البته این کار با استفاده از argc و argv کـه در برنامه‌های معمولی استفاده می شوند نیست.
بــرای این کار متغیرهایی را که می‌خواهید از طریق خط فرمان مقداردهی کنید را بــه صــورت global تعــریف کـــرده و بـا استفاده از مــاکــروی ()MODULE_PARM این مکانیزم را تنظیم کنید. ایــن ماکـــرو دو ورودی می‌گیــرد. اولـی نام متغیر و دومی نوع متغیر . نوع های قابل قبول عبارتند از:

کد:

( s" string ) - ( "l" long ) - ( "i" integer ) - ( "h" short int ) - ( "b" byte" )

String ها باید به صورت *char تعریف شوند. insmod در زمــان اجرا فضای لازم برای ان را می گیرد . به عنوان مثال شما می توانید متغیر myvariable را که یک int است در کد خود تعریف کرده و از طریق خط فرمان ان را مقدار دهی کنید .


کد:

int myvariable = 10 ; // 10 is default value

MODULE_PARM( myvariable , "i" );//setting the type (amout of memory) for it

سپس به صورت زیر آن را مقدار دهی کنید:

# insmod ./mymodule myvariable=250ساختار داده ارایه نیز در این روش مورد پشتیبانی است . به عنوان مثال در کد زیر:

کد:

int myarray[4];

MODULE_PARM( myarray , "3-9i" );/*setting an array of integer with max & min

values 9 , 3 respectively */

یک ارایه 4 تایی از integer به ()MODULE_PARM داده شده و مقادیر مینیمم و ماکزیمم ان نیز به وسیله دو عدد 3 و 9 تعیین شده است.
مثال hello-5.c که مثال جامع تری است تمام این موارد را نشان می‌دهد. (به comment های کد توجه شود).

کد:

/* hello-5.c */

#include <linux/module.h> /* needed by all modules */

#include <linux/moduleparam.h> /* needed by macros like module_param(),

module_param_array(),etc . it also has definitions of many functions and structure

like kernel_param */

#include <linux/kernel.h> /* needed for macros like KERN_INFO */

#include <linux/init.h>/* needed for init and exit macros */

#include <linux/stat.h>/* needed by macros like S_IRUSR , S_IWUSR , S_IRGRP , S_IWGRP */

MODULE_LICENSE("GPL");//setting the module license

MODULE_AUTHOR("Peter Jay Salzman");//setting the module author

 

/* defining some variable for input from shell prompt */

static short int myshort = 1;

static int myint = 420;

static long int mylong = 9999;

static char *mystring = "blah";

 

/* S_IRUSR , S_IWUSR , S_IRGRP , S_IWGRP macros are used for setting

permissions to access to variables and fuctions as common Linux permission

management system . you know in Unix systems we have 3 group of people

( owner – group – others ) and 3 kind of permissions ( read – write – execute (rwx) )

for each group. in modules we use bitwise OR of these macros for setting a user to

set or get a variable or function . */

module_param( myshort , short , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP );

MODULE_PARM_DESC(myshort, "A short integer");

module_param( myint , int , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP );

MODULE_PARM_DESC(myint, "An integer");

module_param(mylong, long , S_IRUSR);

MODULE_PARM_DESC(mylong, "A long integer");

module_param(mystring, charp , 0000);

MODULE_PARM_DESC(mystring, "A character string");

 

static int __init hello_5_init(void)

{

printk( KERN_INFO "Hello, World 5\n=============");

printk( KERN_INFO "myshort is a short integer: %hd\n" , myshort );

printk( KERN_INFO "myint is an integer: %d\n" , myint );

printk( KERN_INFO "mylong is a long integer: %ld\n" , mylong );

printk( KERN_INFO "mystring is a string: %s\n", mystring);

return 0;

}

static void __exit hello_5_exit(void)

{

printk( KERN_INFO "Goodbye, world 5\n");

}

module_init(hello_5_init);

module_exit(hello_5_exit);

دستورات زیر را اجرا کنید تا با نحوه کار این ماژول بیشتر اشنا شوید ( فایل var/log/messeges/ دیده شود.)

کد:

# insmod ./hello-5.ko mystring="bebop" mybyte=255 myintarray=-1

# rmmod hello-5

# insmod ./hello-5.ko mystring="supercalifragilisticexpialidocious" \

> mybyte=256 myintarray=-1,-1

# rmmod hello-5

# insmod ./hello-5.ko mylong=hello

نوشتن یک ماژول در چندین فایل
بعضی اوقات نیاز دارید که یک ماژول هسته را در چندین فایل پیاده سازی کنید.
مثال مورد بررسی شامل دو فایل start.c و stop.c است.

کد:

/* start.c */

#include <linux/kernel.h>

#include <linux/module.h>

 

int init_module(void)

{

printk("Hello, world – this is the kernel speaking\n");

return 0;

}

 

/* stop.c */

#include <linux/kernel.h>

#include <linux/module.h>

void cleanup_module(void)

{

printk("<1>short is the life of a kernel module\n");

}

نکته مهم نحوه build با استفاده از kbuild است . برای این موضوع به تغییرات حاصل در Makefile توجه کنید.

کد:

obj-m += hello-1.o

obj-m += hello-2.o

obj-m += hello-3.o

obj-m += hello-4.o

obj-m += hello-5.o

obj-m += startstop.o

startstop-objs := start.o stop.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

در قسمت بعد با بعضی از مفاهیم بنیادی هسته لینوکس، تــوابع سیستمی آشنــا خــواهیم شــد و گــذر مختـــصری بر مفاهیم بنیادی device driver ها خواهیم داشت.

+ نوشته شده توسط امیرحسین عربی زاده در دوشنبه ششم فروردین 1386 و ساعت 0:27 |

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


در قسمــت قبــل بـــا مــاژول هسته، نحوه load شدن و unload شدن آن و مطالبی دیگر آشنا شدیم و یک ماژول بسیار ساده نوشتیم و با مکانیزمkbuild ان را کامپایل کردیم. در این قسمت به مثال های بیشتری خواهیم پرداخت.


مثال hello world – قسمت دوم
همان طور کـــه در قسمت قبـل گفتیم بعد از هسته 2.3.13 می توانید برای دوتابع ()init_module و ()cleanup_module اســـامی دیگری اختیار کنید. این امکان توسـط دو مــاکــروی ()module_init و()module_exit که در فایل <linux/init.h> تعریف شده اند میسر می گردد.
دو تـابع شروع و پایان ماژول بایستی قبـــل از این دو ماکرو تعریف شده باشند در غیر این صورت خطای کامپایلر را دریافت خواهید کرد .
برای روشن شدن این موضوع به مثال hello-2.c توجه کنید.

کد:

/* hello-2.c */#include <linux/module.h> /*needed by all modules */#include <linux/kernel.h>/*neededfor macros like KERN_INFO,KERN_ALERT,etc */#include <linux/init.h>/*neededfor module_init() & module_exit() */static int __init hello_2_init(void){ printk(KERN_INFO "Hello, World 2\n"); return 0;}static void __exit hello_2_exit(void){ printk(KERN_INFO "Goodbye, World 2\n");}module_init(hello_2_init);/*sets hello_2_init() as initialization function */module_exit(hello_2_exit);/*setshello_2_exit() as termination function */

برای اینکه این کد مانند مثال hello-1.c مورد کامپایل قرار گیرد کافی است در Makefile خط زیر را اضافه کنید:

obj-m += hello-2.oپس از اعمال این تغییر Makefile به صورت زیر خواهد بود:

کد:

obj-m += hello-1.oobj-m += hello-2.oAll: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesClean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

ماکروهای init__ و exit__
ماکــروی init__ در مورد درایـــورهایی کـــه بــه صـورت built-in در هسته استفاده می‌شوند باعث می‌شود که پس از به اتمام رسیدن تابع init، این تابع از حافظه خارج شده و حافظه‌ای کــه برای آن گرفته شده است آزاد می‌گـردد. ایـن ماکرو در مورد درایورهایی که قرار است به صورت ماژول به هسته وارد شوند تاثیری نخواهد داشت.
همانند init__ کـه برای توابع init در نظر گرفته شده ماکروی exit__ برای حذف فضای تابع exit استفاده می‌گـردد و برای درایور هایی که به صورت ماژول وارد هسته می شوند تاثیری نخواهد داشت.
این دو ماکرو در فایل <linux/init.h> تعریف شده‌اند و باعث گرفتن و آزاد کردن حافظه از فضای هسته می‌شوند.
اگر در هنگام بوت هسته پیغامی مانند:

Freeing unused kernel memory : 236k freedرا دریافت کردید دلیل این موضوع استفاده از این دو ماکرو است. عــلت استــفــاده از static را در قســمت بعد بـه تفصیل بررسی خواهیم کرد.


مثال Hello world – قسمت سوم
به مانند دو ماکروی init__ و exit__ که برای توابع به کار می‌رود، مــاکــرویی به نام initdata__ داریم که برای متغیرها به کار می رود و همان تاثیرات ذکر شده در بالا برای متغیر ها را دارد. به مثال ماژول hello-3.c توجه کنید :

کد:

/* hello-3.c */

#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>static int hello_3_data __initdata = 3 ;static int __init hello_3_init(void){ printk(KERN_INFO "Hello, World %d\n", hello_3_data ); return 0;}static void __exit hello_3_exit(void){ printk(KERN_INFO "Goodbye, World 3\n");}module_init(hello_3_init);module_exit(hello_3_exit);

به مانند قبل با اضافه کردن obj-m += hello-3.o در Makefile این فایل مورد build قرار می گیرد.
مثال Hello world – قسمت چهارم
در این مثال به بررسی چند ماکــروی دیگر می‌پردازیــم که بعضی امکانات مفید مانند license و documentation را به یک ماژول اضافه می کنند.
ایــن ماکروها عموما از هستـه2.4 و به بعد اضافه شده‌انـد . license را می توانید با ماکروی()MODULE_LICENSE تعیین کنید. مــاکـــروهــای ()MODULE_DESCRIPTION و ()MODULE_AUTHOR بــه ترتیب بــرای توضیح کاری که ماژول انجام می‌دهد و نویسنده ماژول به کار می روند.
بــرای اینـــکــه مشـــخــص کــنــید کـــه مـــاژول چـــه دستــــه‌ای از دستگاه‌ها را پشتـــیبانی مــی‌کــنــد از مـــاکـــروی ()MODULE_SUPPORTED_DEVICE استفاده کنید. این ماکروها همگی در <linux/module.h> تعریف شده‌اند.
این اطلاعات که در ماژول‌ها ذخیره می‌شوند، توســـط ابـــزارهایی مانند objdump که برای نشان دادن اطلاعاتی از فایل های object به کار می‌روند قابل مشاهده هستند. به مثال hello-4.c که این موارد را نشان می‌دهد توجه کنید:

کد:

/* hello-4.c */#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#define DRIVER_AUTHOR "Peter JaySalzman <p@dirac.org>"#define DRIVER_DESC "A sampledriver"static int __init hello_4_init(void){ printk(KERN_INFO "Hello, World 4\n"); return 0;}static void __exit hello_4_exit(void){ printk(KERN_INFO "Goodbye, World 4\n");}module_init(hello_4_init);module_exit(hello_4_exit);MODULE_LICENSE("GPL");/* macro for setting license information */MODULE_AUTHOR(DRIVER_AUTHOR); /*idenfying module author */MODULE_DESCRIPTION(DRIVER_DESC); /*describe module with this macro */MODULE_SUPPORTED_DEVICE("testdevice"); /* identifying devices module can support and work – here is test and is not important */

+ نوشته شده توسط امیرحسین عربی زاده در دوشنبه ششم فروردین 1386 و ساعت 0:26 |

ماژول هسته چیست؟
اولیــن سوالی کــه ممکن است به ذهن خواننده برسد این است که ماژول هسته دقیقا چیست؟ در پاسخ باید گفت که ماژول*ها تـکه کدهایی هـستند که در حین اجرای هسته لینوکس می توانند وارد آن شده و یا از آن خارج شوند. این تکه کدها عملکرد هسته را بدون نیاز به راه اندازی دوباره کامپیوتر توسعه می*دهند.
به عنوان مثال یــک نــوع از مــاژول*ها device driver ها هستند کـه به هسته امکان استفاده از قابلیت سخت افزار ها را می*دهند.
اگر ماژول*ها وجود نداشتند، بـرای هر قابلیتی که می*خواستیم بــه هسته اضافه کنیم یـا از آن کم کنیم، می*بایستی یک بار هسته را کامپایل می*کردیم و برای استفاده از ان قابلیت یا حذف آن یک بار سیستم را از نو راه اندازی می*کردیم.


ماژول ها چگونه به هسته وارد می شوند؟
شما می*توانید بــا اجرای دستور lsmod ماژول*هایی کــه هــم اکنـون در هسته وارد شده*انـد را ببیـنـید و از اطـلاعات آنها باخبر شوید. این دستور اطلاعات خود را از فایل proc/modules/ دریافت می*کند.
هنگامی که هسته، به امکان و عملکردی نیاز دارد که هم اکنون در آن نیست، یکــی از deamon های آن بــه نـام kmod دستور modprobe را اجرا می*کند تا ماژول مربوطه که آن عملکرد را دارد وارد هسته شود . هنـگامی که modprobe اجرا می شود به آن یک رشته کاراکتر به دو صورت زیر داده می*شود:

۱) نام ماژول مانند softdog یا ppp
۲) یک مشخصه کلی مانند char-major-10-30

اگر حالت اول بــه modprobe داده شود، ایــن دستور بـه دنبال فایلی به نام softdog.ko یا ppp.ko با روشی که در ادامه می*آید می*گردد. ولی اگــر حــالـت دوم بــه modprobe داده شود، ایــن دستــور ابـــتدا بــه دنــبال رشتـه کاراکتر در فایل etc/modprobe.conf/ می گردد و اگر توانست alias یا مستعاری مانند:

alias char-major-10-30 softdogپیدا کند، متوجه می*شود کــه ایــن نــام کلی که در اینجا char-major-10-30 است به ماژول softdog اشاره می*کــند که فایل ماژول آن softdof.ko می*باشد.
در مرحله بعد modprobe فایل lib/modules/version/modules.dep/ را باز کرده و به دنبال ماژول*هایی می*گــردد کـه باید قبل از ماژول مورد نظر به هسته وارد شوند. ایــن فــایل به وسیله دستور depmod -a ایجاد می*شود و حــاوی وابستگی بین ماژول هاست.
به عنوان مثال اگر به دنبال مــاژول msdos.ko در ایــن فــایل بگردید خواهید دید که به ماژول دیگری به نام fat.ko وابسته است یعنی برای اینکه msdos.ko وارد هسته شود حتما باید قبل از ان fat.ko وارد شده باشد. ایــن مسـاله برای fat.ko نیز تــکــرار شده تــا بـه مرحله*ای برسیم که دیگر وابستگی موجود نباشد. در نهایت modprobe دستور insmod را به کار می*برد تا ابتدا وابستگی*ها را به هسته وارد کرده و در نهایت ماژول مورد نظر ما به هسته وارد می*شود.
پس modprobe وظــیـفه پـیـدا کــردن مــاژول، تعیین وابستگیهای آن و وارد کردن آن بــه هستــه بــه وسیــله صـدا کردن insmod را دارد در حالی که insmod فقط وظیفه وارد کردن آن ماژول به هسته را دارد.
به عنوان مثال اگر بخواهیم به صورت دستی msdos.ko را وارد هسته کنیم به صورت زیر عمل می کنیم :

# insmod /lib/modules/2.6.11/kernel/fs/fat/fat.ko
# insmod /lib/modules/2.6.11/kernel/fs/fat/msdos.koمعادل دو دستور بالا با modprobe به صورت زیر است:

# modprobe msdosمطلب قابل ذکر این است که insmod مسیر کامل تا فایل ماژول را می*خواهد در حالی که modprobe فقط نـــام ماژول را می*گیرد.


قبل از شروع
قبل از اینکه وارد کد و کدزنی شویم چند نکته مهم را بررسی می*کنیم:
۱) modversioning: یــک ماژول کـه برای یک هسته خاص کامپایل شده است بر روی هسته دیگر load نخواهد شد مگر اینکه شــما CONFIG_MODVERSIONS را در هسته فــعــال کنــیـد. در قـســمت*های بـعــد بیشتر به این مقوله خواهیم پرداخت.
۲) ماژول*ها نمی*توانند چــیزی به غیر از خطاها و هشدارها را بر روی صفحه نمایش نـشان دهند. آنها بــرای نـشان دادن اطلاعات خود، آنها را در log فایلها می*نویسند.
۳) مورد سوم که کاملا مورد قبول بنده نمی*باشد این است که نویسنده می گوید:
«اغلب توزیع کنندگان لینوکس کد منبع هسته را که مورد Patch نیز قرار گرفته به طرز غیر استانداردی توزیع می*کنند که ممکن است باعث ایجاد مشکلاتی شود. یکی از شایع ترین این مشکلات فایل های ناقص Header برای هسته لینوکس هستند. شما برای ماژول نویسی نیاز دارید که فایل های Header زیــادی را در کـــدهای خـود ضمیمه کـنـید و فایــلهای ناقص اغلب فایل هایی هستند که برای ماژول نویسی به کار می روند.» نویــسنده پیشنهاد می*کند کـه برای جلوگیری از این مشکل هسته را برای خود کامپایل کنید.


یک مثال – ساده ترین ماژول
برای شروع از مثال سنتی Hello World! شروع می*کنیم. فایلی به نام hello-1.c باز کرده و کد C زیر را در آن بنویسید:

کد:

#include <linux/module.h> /*needed by all modules */

#include <linux/kernel.h> /*needed for Macros like KERN_INFO */

/* this function is called as initialization for all modules, if this function returns non-zero means init_module failed and this module can’t be loaded .*/

int init_module(void) { printk(KERN_INFO "Hello World1.\n"); return 0;}

/* it iscalled when module is terminated and unloaded */

void cleanup_module(void) { printk( KERN_INFO "Goodbye World1.\n");}

هــر ماژول هسته*ای حداقل بایستی ۲ تابع داشته باشد. اولـی تابع شروع که init_module() نامیده می*شــود و هنگام load شدن ماژول در هسته صدا زده می*شود و دیــگــری تــابــع پــایان که cleanup_module() نامیده می*شــود و هنگام unload شدن ماژول از هسته صدا زده می*شود. در قسمت*های بـعد به این موضوع می*پـردازیم که بعد از هسته 2.3.13 شما می*توانید هــر نـــام دیــگری بــرای این دو تــابع قرار دهید. بــا ایـــن حــال خـیـلی از افـــراد هنـــوز از ایـن استاندارد قدیمی استفاده می*کنند. دو فــایــل Header در ایــن کــد ضمیمه شده*اند. یــکی linux/module.h می*باشد که برای هر مــاژولی مــورد نیاز است و تعریف خیلی از توابع را در خـود دارد و دیــگــری linux/kernel.h می*باشد کــه حــاوی تعدادی ماکرو می*باشد مانند KERN_INFO.


مختصری درباره printk()
بر خلاف آن چیزی که ممکن است درباره printk() تصور کنید این تابع چیزی در صفحه نمایش چاپ نمی کند و برای کار با کاربر نیست. ایــن تــابع برای مکانیزم log هستــه بـه کار میــرود . هــر printk() بــا یـک اولویت می*آید کــه در ایـن مثال مــاکــروی KERN_INFO بــرای ایــن منظور بــه کــار رفته است. تـعـداد ۸ اولــویت وجــود دارند کــه بــه صــورت مـاکرو در فــایــل linux/kernel.h تعـــریف شــده*انــد. اگـــر شـمــا ایـــن اولــویــت را تـعــیــین نـکــنــیــد بـــه طـــور پــیــش فـــرض DEFAULT_MESSAGE_LOGLEVEL به آن تخصیص می*یابد .
اگــر این اولویت کمــتر از اولــویت int console_loglevel (که در linux/kernel.h تعریف شده) باشــد، message در دستور printk() بر روی صفحه ظاهر می*شود. اگــر syslogd و یــا klogd در سیستم در حــال اجرا باشند این message در فایل var/log/messages/ نوشته می*شود.


کامپایل ماژول های هسته
ماژول های هسته کمی متفاوت نسبت به برنامه های معمولی کامپایل می*شوند. بــرای ایـنکه بتوانید یک ماژول هسته را بــه درستــی کامپایل کنید، نیاز به تنظیمات بسیار زیادی دارید. بــا پیـچیده*تر شــدن ماژول*ها ایـن تنظیمات پیچیده*تر می*شوند. خــوشبختانه مکانیزمی به نام kbuild وجود دارد که تمام این تنظیمات را انجام می*دهد. بـرای اگاهی بیشتر از ایـن مکانیزم به فایلهای مستند هسته که در آدرس linux/Documentation/kbuild/modules.txt کد منبع هسته مـوجـود است مراجعه کنید. بــرای اینکه بتوانیم از مکـانیزم kbuild استفاده کنیم، بایستی Makefile ای با استاندارد آن بنویسیم. برای این کار یک فایل به نام Makefile باز کرده و دستورات زیر را در آن بنویسید:
obj-m += hello-1.oall : make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
حال با اجرای دستور make ماژول خود را کامپایل کنید. در هسته 2.6 بــه بــعد از پسوند ko بــرای نــامیدن مـــاژول*های هسته استفاده شده است که به راحتی قابل تمییز از o که پسوند فایل های object است می*باشد.
برای بدست آوردن اطلاعاتی از ماژول خود دستور زیر را اجرا کنید:

# modinfo hello-1.koبرای وارد کردن ماژول خود در هسته از دستور زیر استفاده کنید:

# insmod ./hello-1.koاگر بعد از اجرای این دستور فایل var/log/messages/ را باز کرده و به انتهای آن بروید، خواهید دیــد کـه ماژول hello-1 در هسته load شده است. با دستور lsmod نیز ماژول load شده را خواهید دید. بــرای unload یــا خارج کردن ماژول خود از هسته از دستور rmmod به صورت زیر استفاده کنید :

# rmmod hello-1دوباره اگر فایل var/log/messages/ را بــاز کنید و به انتهای آن بــروید خواهید دید که ماژول hello-1 از هسته خارج شده است.در قسمت های بعدی با مثال های بیشتر به دیگر جزئیات ماژول نویسی برای هسته لینوکس خواهیم پرداخت.

+ نوشته شده توسط امیرحسین عربی زاده در دوشنبه ششم فروردین 1386 و ساعت 0:25 |
بزرگترین سایت جاوا اسکریپت ایران

بهترین و زیباترین کدهای جاوا اسکریپت به همراه آزمایش آن کد