رتبه موضوع:
  • 1 رای - 5 میانگین
  • 1
  • 2
  • 3
  • 4
  • 5
ماژول نویسی برای هسته لینوکس
#1
ماژول نویسی برای هسته لینوکس (قسمت اول)
ماژول هسته چیست؟
اولیــن سوالی کــه ممکن است به ذهن خواننده برسد این است که ماژول هسته دقیقا چیست؟ در پاسخ باید گفت که ماژول‌ها تـکه کدهایی هـستند که در حین اجرای هسته لینوکس می توانند وارد آن شده و یا از آن خارج شوند. این تکه کدها عملکرد هسته را بدون نیاز به راه اندازی دوباره کامپیوتر توسعه می‌دهند.
به عنوان مثال یــک نــوع از مــاژول‌ها device driver ها هستند کـه به هسته امکان استفاده از قابلیت سخت افزار ها را می‌دهند.
اگر ماژول‌ها وجود نداشتند، بـرای هر قابلیتی که می‌خواستیم بــه هسته اضافه کنیم یـا از آن کم کنیم، می‌بایستی یک بار هسته را کامپایل می‌کردیم و برای استفاده از ان قابلیت یا حذف آن یک بار سیستم را از نو راه اندازی می‌کردیم.
ماژول ها چگونه به هسته وارد می شوند؟
شما می‌توانید بــا اجرای دستور lsmod ماژول‌هایی کــه هــم اکنـون در هسته وارد شده‌انـد را ببیـنـید و از اطـلاعات آنها باخبر شوید. این دستور اطلاعات خود را از فایل proc/modules/ دریافت می‌کند.
هنگامی که هسته، به امکان و عملکردی نیاز دارد که هم اکنون در آن نیست، یکــی از deamon های آن بــه نـام kmod دستور modprobe را اجرا می‌کند تا ماژول مربوطه که آن عملکرد را دارد وارد هسته شود . هنـگامی که modprobe اجرا می شود به آن یک رشته کاراکتر به دو صورت زیر داده می‌شود:
  1. ۱) نام ماژول مانند 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 */ int init_module(void) /* this function is called as initialization for all modules */ { printk(KERN_INFO “Hello World1.\n”); /* if this function returns non zero means init_module failed and this module can’t be loaded . */ return 0; } void cleanup_module(void) /* it is called when module is terminated and unloaded */ { 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.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
حال با اجرای دستور 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 از هسته خارج شده است.در قسمت های بعدی با مثال های بیشتر به دیگر جزئیات ماژول نویسی برای هسته لینوکس خواهیم پرداخت.
[عکس: www.Mojsazan.com.gif]
پاسخ
سپاس شده توسط mafia1990
#2
ماژول نویسی برای هسته لینوکس (قسمت دوم)
ماژول نویسی برای هسته لینوکس (قسمت دوم)
در قسمــت قبــل بـــا مــاژول هسته، نحوه 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>/*needed for macros like KERN_INFO,KERN_ALERT,etc */ #include <linux/init.h>/*needed for 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);/*sets hello_2_exit() as termination function */
برای اینکه این کد مانند مثال hello-1.c مورد کامپایل قرار گیرد کافی است در Makefile خط زیر را اضافه کنید:
obj-m += hello-2.o
پس از اعمال این تغییر Makefile به صورت زیر خواهد بود:
obj-m += hello-1.o obj-m += hello-2.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
ماکروهای 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 Jay Salzman <p@dirac.org>” #define DRIVER_DESC “A sample driver” 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 */

منبع
[عکس: www.Mojsazan.com.gif]
پاسخ
سپاس شده توسط mafia1990
#3
ماژول نویسی برای هسته لینوکس (قسمت سوم)
ماژول نویسی برای هسته لینوکس (قسمت سوم)
فرستادن ورودی های خط دستور به یک ماژول
ماژولها می‌توانند ورودی‌هایی از طـریق خط دستور دریافت کنند البته این کار با استفاده از 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 ها خواهیم داشت.
[عکس: www.Mojsazan.com.gif]
پاسخ
سپاس شده توسط
#4
ماژول نویسی برای هسته لینوکس (قسمت چهارم)
ماژول نویسی برای هسته لینوکس (قسمت چهارم)
در قسمت‌های قبل با مفاهیم اولیه ماژول‌های هسته اشنا شدیم و به عنوان مثال چند ماژول بسیار ساده نوشتیم و ان ها را در هسته 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 می‌گویند) را دارد. بنابراین مشاهده می‌کنید که دو فایل دستگاه یک دستگاه را مشخص می‌کند. بنابراین در بحث مان بیشتر به معنی دستگاه و سخت افزار توجه داشته باشید. در‌ اینجا‌این قسمت به پایان می‌رسد. در قسمت‌ آینده در مورد درایور‌های دستگاه‌های کاراکتری که از اهمیت ویژه‌ای در ماژول نویسی هسته لینوکس برخوردارند بحث خواهیم کرد.
[عکس: www.Mojsazan.com.gif]
پاسخ
سپاس شده توسط
#5
ماژول نویسی برای هسته لینوکس (قسمت پنجم)


در قسمت قبل با 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 ) وجــود دارد کــه مقدار دادن به این ساختار داده را بسیــار راحت می‌کند. شما این موارد را در راه اندازهای مدرن امروزی خواهید دید که ممکن اســت باعث تعجب شما شـود. در زیر نحوه مقدار دهی را با استفاده از این امکانات می‌بینید:

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 ) معمولا توسط راه اندازها استفاده نمی‌شوند.

منبع
[عکس: www.Mojsazan.com.gif]
پاسخ
سپاس شده توسط
#6
ماژول نویسی برای هسته لینوکس (قسمت ششم)

ثبت (register) یک دستگاه در هسته
همان طور که در قسمت قبل بحث کردیم، دستگاه‌های کاراکتری از طریق فـــایل‌های دستگاه کــه معمولا در dev/ قرار می‌گیــرند قــابل دسترسی هستند. در قسمت قبــل به این موضوع نیز پرداختیم که عدد اصلی هــر فایل دستگاه نشان می‌دهد که دستگاه توسط چه راه اندازی کنترل شده و عــدد فـــرعی تــوسط خود راه انداز بــرای تمایز بین دستگاه‌های تحت کنترل خود به کار می‌رود.
اضافه کردن یک راه انداز به سیستم به معنی ثبت آن در هسته است. مترادف این جمله آن است کــه در هنــگام شروع به کار ماژول (initialization)، به آن یــک عــدد اصلی نسبت داده شــود. شما می‌توانید این کــار را بـــا استـــفاده از تابع register_chrdev که در <linux/fs.h> تعریف شده است انجام دهید:
int register_chrdev(unsigned int major, const char *name,
struct file_operations fops);
unsigned int major عـــدد اصلی مورد درخواست و const char *name نام دستگاه شما اســت کــه در proc/devices/ ظاهر خواهد شد و struct file_operations *fops نیز اشاره‌گــر بــه سـاختار داده file_operations مورد استفاده راه انداز هستند. اگر مقدار بازگشتی این تابع منفی باشد ثبت دستگاه در هسته موفقیت آمیز نبوده است.
به این نکته توجه داشته باشید که ما عدد فرعی‌ای بـــه ایــن تابع پاس نکردیم چون که این موضوع برای هسته اهمیتی ندارد و ماژول خود از ان استفاده می‌کند.
سوال بسیار مهمی‌کــه ممکن است مطــرح شــود این است که چگونه عدد اصلی ای به تابع register_chrdev بدهیم و مطمئن باشیم که قبلا توسط راه انداز دیگری اختیار نشده است؟
آسان‌ترین راه برای ایــن کار رجــوع بــه فایل linux/Documentation/devices.txt از کد منبع هسته لینوکس و انتخاب یک عدد رزرو نشده است. مطمئنا این راه بدترین راه است چــون هیـــچ اطمینانی در قابـــل استفاده بودن آن عــدد اصــلـی نمی‌دهد. بهترین راه آنست کــه شما از خود هسته بخواهید که به صورت دینامیک یک عدد اصلی در اختیار شما بگذارد. اگر ورودی اول تابع register_chrdev که همان unsigned int major است را صفر قرار دهید، هستـه یک عدد اصلی را به عنوان مقدار بازگشتی تابع register_chrdev به شما باز می‌گرداند.
خروج (unregister) یک دستگاه از هسته
ما نمی‌توانیم اجازه دهیم که هر هنگام یک ماژول هسته rmmod شــود. اگــر فایل دستگاه توسط یک پروسه بــاز شـده باشد و مــا مــاژول را از هسته خارج کنیم، آن پروسه بـه آن قسمت از حافظه که آدرس آن تــابــع از راه انـــداز است، دسترسی داشته و می‌تواند آن تابع را صدا نماید. اگر خیلی خوش شانس باشیم و کــد جدیدی در آن آدرس load نشده باشد، فقط یک پیغام خطا دریافت خواهیم کرد. ولــی اگـر خیلی خوش شانس نباشیم و ماژول جدیدی در آن ادرس load شده باشد، به این معنی است که در میــان تابع جدیدی از هستــه خــواهیم پرید. نتیجه این کار به هیچ وجه قابل پیش بینی نیست ولی در اکثر موارد اتفاق‌های نسبتا ناگواری در سیستم به بار می‌آید.
معمولا برای اینکه شما اجازه انجام کاری را ندهید، یک پیغام خطا (یک عدد منفی) توسط آن تابع انجام دهنده کــار بر خواهید گردانــد. در مــورد تابع cleanup_module ایــن کار ممکن نیست، چـون ایــن تــابــع void بـر می‌گرداند (چیزی بر نمی‌گرداند). راه متداول این کار وجود یک شمارنده در ماژول است کـه تعداد پروسس‌هایی که از ماژول استفاده می‌کنند را نگه می‌دارد. شما می‌توانید مقدار این شمارنده را در سومین فیلد proc/modules/ مشاهده کنیــد. اگـر این مقدار غیر صفر باشد دستور rmmod با شکست مواجه خواهد شد.
توجه داشته باشیــد کــه شما نبایستی این شمـارنده را در cleanup_module چک نمایید بدلیل اینکه این ارزیابی توسط تابع سیستمی‌sys_delete_module که در linux/module.c تعریف شده است بـــرای شمـــا انــجــام می‌شـــود. شـمــا نمی‌توانید از این تابع بــه طور مستقیم استفــاده نمایید امــا توابعی در <linux/modules.h> تعریف شده است که اجازه افزایش، کاهش و مشاهده این شمارنده را می‌دهد. به عنوان مثال:
try_module_get(THIS_MODULE)
که یک واحد شمارنده را زیاد می‌کند و
try_module_get(THIS_MODULE)
که یک واحد شمارنده را کاهش می‌دهد.
نکته بسیار مهم این است که مقدار این شمارنده بایستی همیشه صحیح نگه داشته شود. اگر این مقدار خراب شود به هیچ عنوان نمی‌توان ماژول را unload کرد. در این مرحله هیچ کاری به جز reboot نمی‌توان کرد.

خلاصه : در قسمت پیش و ایــن قسمت با دو ساختــار داده file_operations و file و اهمیت آنها آشنــا شــدیـم و نحوه مقدار دهی آنــها را نیز فرا گرفتیم. در ادامه با استفاده از تابع register_chrdev به ثبت یک راه انداز در هسته پرداختیم و در انـتـها به بعضی از نکات در خروج ماژول از هسته پرداختیم.
در قسمت اینده یک مثال از یک راه انداز را به طور کامل بررسی خواهیم کرد.
[عکس: www.Mojsazan.com.gif]
پاسخ
سپاس شده توسط
#7
ماژول نویسی برای هسته لینوکس (قسمت هفتم)
ماژول نویسی برای هسته لینوکس (قسمت هفتم)
در دو قسمت پیشین با مفاهیم ابتدایی دستگاه‍‌های کاراکتری آشنا شدیم و نحوه ثبت این گونه دستگاه‌ها در هسته را متوجه شدیم . در این قسمت مطالبی را که در دو قسمت قبل فرا گرفتیم در ساده ترین مثال بررسی می‌نماییم.
کد ایـن مثــال را که حــاوی توضیحات تقریبا کامــلی است، می‌توانید از اینجا بــدست آورید. پـس از کـامـپـایل مـــاژول از دستور زیر برای وارد کردن این ماژول در هسته استفاده کنید:
# insmod ./chardev.ko
اگـــر اکنون به انتهای فایل var/log/messages/ بروید مشاهده می‌کنید کــه هسته به صورت دینامیک به ماژول شما یک عدد اصلی اختصاص داده است . با استفاده از راهنمایی‌ای کــه در ایــن فــایـل شــده، دســتـور mknod را بــرای ایـجـاد dev/chardev/ بــه صورت ذکــر شده اجرا کنید. اکنــون راه انداز شما قابــل استفاده است. می‌توانید آن را باز کنید، از آن بخوانید و در آن بنویسید. برای خواندن از این دستگاه کاراکتری به صورت زیر عمل کنید:
# cat /dev/chardev
و یا برای نوشتن عبارتی مثلا “Hello” در آن به صورت زیر عمل کنید:
# echo “Hello” > /dev/chardev
همان گونه که ذکر شد توضیحات کامل این مثال در کد مثال به صورت comment آمده است. در زیر بــه دو نـکتــه از ایــن مثال اشاره کرده و در نهایت بحث این مثال را با یک سوال به پایان می‌بریم.
  1. ۱) در تابع device_read همان طــور کــه مشـاهده می‌کنید از تابع put_user استفاده شده است. بـه طور کلی هر پروسس دارای یک بافر در فضای کاربر است و هـر ماژول نیز دارای یک بافر در فضای هسته است. عملی که این تابع و توابع مشابه آن انجام می‌دهند، اطلاعات را بــه صـــورت کامــلا محافظت شده بین این بافرها جابجا می‌کنند. ۲) همان طور که در تابع device_write می‌بینید، ایـن تابع عملی انجام نمی‌دهد. شما می‌توانید این تابع را به هر صورتی که می‌خواهید تغییر دهید. به عنوان مثال می‌توانید بــا دانستــن ساختار سخت افزاری مودم خود این تابع را پیاده سازی کرده و بدین ترتیب اطلاعات خود را در مودم خـود بنویسید. البته برای خواندن از مودمتان باید تابع device_read را با توجه به ساختار مودمتان تغییر دهید.
سوال: اکنون در کد مثال خط زیر را پیدا کنید:
static char msg[BUF_LEN];
و به صورت زیر تغییر دهید:
static char *msg;
یعنی در حقیقت برای اشاره گر بافر ماژول فضایی در نظر نگیرید. حــال دوباره مـاژول را در هسته وارد کرده و عملیات cat را انجام دهید. مشــاهده می‌کنید کــه بـه پیغام segmentation fault برخورد خواهید کرد. در صـورتی که در چند قسمت قبل گفتیم که اگر ماژول دچار seg fault شود هسته دچـــار seg fault خواهد شد. بـــا تــوجـه به این نکات این مساله را توجیه کنید. پاسخ‍‌های خود را به e-mail بنده که در انتهای این مقاله است بفرستید و جواب صحیح را دریافت دارید.
در اینجا این قسمت به پایان می‌رسد. در قسمت بعدی ادامه مطالب ماژول نویسی برای هسته لینوکس را از نگاهی جدیدتر پی خواهیم گرفت.
منبع
[عکس: www.Mojsazan.com.gif]
پاسخ
سپاس شده توسط
#8
ماژول نویسی برای هسته لینوکس (قسمت هشتم)
در قسمت های قبل با اصول و مبانی ماژول نویسی برای هسته لینوکس آشنا شدیم و ابتدایی ترین مفاهیم نوعی از دستگاهها موسوم به دستگاه های کاراکتری را بررسی نمودیم. در این قسمت و دو قسمت آینده مطالبمان را با بررسی فایل سیستم proc/ و کاربرد آن در ماژول نویسی برای هسته لینوکس ادامه خواهیم داد.
در لینوکس مکانیزم ویژه‌ای بـرای هسته و ماژولهای هسته بــرای ارسال و دریافت اطلاعـات از پروسس‌ها وجـود دارد که در قالب فایل سیستم مجازی proc/ پیاده سازی شده است. این فایل سیستم بـرای ســهولت دستـرسی به اطلاعاتی در زمینه پروسس‌ها طراحی شده است. به عنوان مثــالproc/modules/ لیستی از مــاژول‌های وارد شــده در هـسته و proc/meminfo/ اماری از میزان مصرف حافظه را نشان می‌دهند. برای آشنایی بیشتر با این فایل سیستم این مقاله [۱] را مطالعه بفرمایید.
روشی که برای استفاده از فایل سیستم proc/ به کار می‌رود بسیـار شبـیـه روشـی است که در مورد راه اندازها به کار می‌رود، یک نمونه یا instance از struct ای که تمامی‌ این اطلاعات را به همراه اشاره گــرهــایی بـه توابع مورد نظر ایجاد می‌گردد. سپس در تابع شروع ماژول که همان init_module است ایــن سـاخـتـار داده در هسـتـه ثبت شده و در هنگام اتمام ماژول که cleanup_module صدا زده می‌شود این ساختار داده از هسته خارج می‌گردد.
بحثمان را با یک مثال شروع می‌کنیم. کد این مثال و مثال بعدی را کــه حــاوی comment کــاملی هستند را می‌توانید از اینجا [۲] بدست اورید. با مثال اول ( فایل procfs1.c ) شــروع می‌کنـیـم. ایــن مثال از ۳ قسمت تشکیل شده است: در تابع init_moduleفایلproc/helloworld/ ایجــاد می‌شـود، هنگامی‌که از این فایل خوانده می‌شود تابع procfs_read صدا زده می‌شود که یک مقدار ( و یک بافر ) بر می‌گرداند. در نهایت در تابع cleanup_module این فایل حذف می‌گردد.
فایل proc/helloworld/ هنگامی‌که ماژول در هسته وارد می‌شود توســط تابع create_proc_entry ایجاد می‌گردد. مقدار بازگشتی ایـن تابع یک '* struct proc_dir_entry' اسـت کــه بــرای پـیـکـربندی فایل proc/helloworld/ (به عنوان مثال تعیین صاحب فایل) به کار می‌رود. مقدار بازگشتی NULL نشان می‌دهد که اجرای این تابع ناموفق بوده است.
هر هنگام کــه از فایل proc/helloworld/ خوانده می‌شود تــابع procfs_read صدا زده می‌شود. دو پارامتر ورودی این تابع بسیار مهم هستند. buffer ( اولین پارامتر ) و offset ( سومین پارامتر ). محتــوای بافر به برنامه‌ای کــه تقاضای خواندن داده است باز می‌گردد (به عنوان مثال دستور cat). پارامتر offset نیز مکان فعلی در فــایــل را نـشـان می‌دهد. اگر مقدار بازگشتی این تابع NULL نباشد، این تابع دوباره صدا زده خواهد شد. بنــابـراین مراقب این تابع باشید اگر مقدار بازگشتی این تابع هیجگاه صفر نشود صدا زدن این تابع به صورت بی پایان ادامه خواهد داشت.
مثالprocfs1.c را کامپایل کرده و ماژول تولیدی را در هسته وارد نمــایـیــد. بـا استفاده از دستور زیر از proc/helloworld/ بخوانید :
#cat /proc/helloworld
خواندن از و نوشتن در یک فایل proc/
مثال قبل که مثال ساده ای از خواندن از یک فایل proc/ بود را دیدیم. نکته‌ای کــه می‌خواهیم در ایــن قســمت بـررسـی کنیم، نوشتن در یک فایل proc/ است. هنگام نوشتن در فایل proc/ مانند حالت خواندن یک تابع مانند procfs_write صدا زده می‌شود. اما تفاوتهایی بیــن خــواندن و نوشتن وجــود دارد کــه مـهم ترین آن انتقال یافتن اطلاعات از فضای کاربر به فضای هسته در حال نوشتن است که این کار توسط توابعی مانند copy_from_user یا get_user انجام می‌شود.
دلیل وجود توابعی مانند دو تابع بالا این است که حافظه در لینــوکس (در معماری پردازنده اینتل، ممکن است در پردازنده های دیگر متفاوت باشد) بــه segment هایی تقسیم شده است. ایــن بدان معنا است که یک اشاره گر به تنهایی به ادرس یکتایی در حافظه اشاره نمی‌کند , بلکه به موقعیتی در segment اشاره می‌کند و شما نیاز داید که segment حافظه را بدانید تا بتوانید از ان استفاده کنید.
فقط یک segment برای هسته وجود دارد و برای هر پروسس نیز یک segment اختصاص می‌یابد. تنها segment ای که یک پروسس می‌تواند به ان دسترسی داشته باشد segment خود پروسس است. بنابراین هنگامی‌که شما برنامه‌ای توسعه می‌دهید یا اجرا می‌کنید، واقعا لازم نیست که نگران مدیریت segmentهای حافظه باشید. امــا هنـگـامی‌ که یک ماژول هسته می‌نویسید، معمــولا می‌خواهید کــه بــه فضــای حـافـظه segment هسته کــه توسط سیستم راه اندازی می‌شود دسترسی داشته باشید. با این حال هنگامی‌که نیــاز است محتـوای یک بافر حافظه بین یک پروسس و هسته رد و بدل شود هسته یــک اشاره گر به بافر حافظه segment پروسس دریافت می‌دارد. ماکروهای put_user و get_user اجازه دسترسی به این حافظه را می‌دهند. این توابع فقط یک کاراکتر تحویل می‌دهـند. شما می‌توانید با استفاده از توابع copy_to_user و copy_from_user کاراکترهای متعددی را دریافت دارید و یا به هسته تحویل دهید.
چون که بافر (در توابع خواندن و نوشتن) در فضای هسته است، برای نوشتن شما نیاز دارید کــه اطــلاعاتـتـان را import کنید اما در تابع خواندن اطلاعات هم اکنون در فضای هسته است.
برای جا افتادن مطلب به مثال procfs2.c مراجعه کنید. کد این مثال را از اینجا [۲] می‌توانید بدست آورید.
در قسمت آینده نحوه مدیریت فایل سیستم مجازی proc/ را بررسی خواهیم کرد.
[عکس: www.Mojsazan.com.gif]
پاسخ
سپاس شده توسط
#9
ماژول نویسی برای هسته لینوکس (قسمت نهم)
در قسمت قبل با چگونگی خواندن از و نوشتن در فایل سیستم مجازی proc/ آشنا شدیم. در این قسمت مدیریت این فایل سیستم را با استفاده از inode‌ها و seq_file‌ها از نظر می‌گذرانیم.
در هسته لینوکس مکانیزم استانداردی برای ثبت فایل سیستم وجود دارد. از آن جاییکه هر فایل سیستم بایستی توابع مخصوص به خود برای به کار بردن inode و عملیات فایل داشته باشد ساختار ویژه‌ای به نام struct inode_operations برای نگهداری اشاره‌گر به تمام آن توابع وجود دارد که در خود اشاره‌گری به ساختار struct file_operations دارد.
در فایل سیستمproc/ هنگامی‌که یک فایل جدید ثبت می‌شود ما اجازه خواهیم داشت که ساختار inode_operations ای که برای دسترسی به این فایل به کار می‌رود را تعیین کنیم.
بـــه طـور خلاصه در ایـن مــکـانیزم یــک struct inode_operations وجـــود دارد کــه در خــود اشاره‌گــری بــه یــک struct file_operations دارد کــه این ساختار نیز اشاره‌گرهایی بــه تــوابـعـی مانند procfs_read و procfs_write دارد که وظیفه خواندن از و نوشتن در proc/ را بر عهده دارند.
نکته قابل توجه دیگر وجود توابعی مانند module_permission است. این تابع هنگامی‌ کــه یـک پروسه عملی در proc/ انجام می‌دهد صدا زده شده و تعیین می‌کند که آیا پروسه مذکور حق انجام عمل مورد نظر را دارد یا نه؟
در حال حاضر این قضاوت بر اساس عمل مذکور و uid کاربر حاضر (که در متغییر current که اشــاره‌گری بــه ساختاری در مورد اطلاعات پروسه حاضر است , وجود دارد.) صورت می‌پذیرد. اما باید توجه داشت که این قضاوت می‌تواند به دلخواه ما که ماژول هسته را می‌نویسیم صورت گیرد.
نکته دیگری که در اینجا بایستی ذکر گردد نقش توابع read و write برای هسته برعکس نقشی اســت کــه در مـورد یک پروسه وجود دارد. تابع read به عنوان تابع خروجی و تابع write به عنوان تابع ورودی در هسته استفاده می‌شود. دلیل این موضوع آن است که هنگامی‌که یک پروسه از هسته چیزی می‌خــوانـد هسته بایستی آن را به عنوان خروجی بیرون دهد و هنگامیکه یک پروسه در هسته می‌نویسد هسته بایستی آن را به عنوان ورودی دریافت دارد.
بــرای فــهم عمیق‌تــر و چگــونــگی کاربرد ساختارهای inode_operations و module_permission به مثال procfs3.c که توضیحات کاملی در کــد برای آن وجود دارد مراجعه کنید. کـــد این مثال را از اینجا [۱] می‌توانید بــدست آوریــد. مـاژول را کامپایل نموده و در هسته وارد نمایید و با دستورات cat و echo عملکرد آن را با بررسی فایل var/log/messages/ مورد بررسی قرار دهید. مدیریت فایل سیستم مجازی proc/ با استفاده از seq_file
همان گونه که دیدیم کار با یک فایل proc/ ممکن است کاملا پیچیده باشد. بــرای رفـــع این پیــچـیدگــی API ای بــه نام seq_file در هسته لینوکس وجــود دارد کـه ما را در قالب بندی یک فایل proc/ برای خروجی یاری می‌نماید که بر اساس تــرتیب (sequence) می‌باشد. ایـن تـرتیب از ســه تابع ()start و ()next و ()stop تشکیل شده است. seq_file هنگامی‌ که یک کاربر از proc/ می‌خواند راه اندازی می‌شود.
یک ترتیب با صدا کردن تابع ()start آغــاز می‌شود. اگــر مقــدار بازگشتــی ایــن تــابـع NULL نبـاشد تابع ()next صدا زده می‌شود. نقش این تابع به عنوان یک iterator می‌باشد و هــدف از صــداکــردن پشت ســرهــم ایـن تابع جابجا شدن به اطلاعات بعدی فایل proc/ می‌باشد.
هر هنگام که این تابع صـدا زده می‌شود تابع دیگری به نام ()show نیز صدا زده می‌شود که اطلاعات دریافتی را در بافری که توسط پروسه فراهم شده می‌نویسد. تابع ()next تا زمانیکه NULL را به عنوان خروجی برگرداند صدا زده می‌شود. در این هنگام ترتیب با صدا زده شدن تابع ()stop خاتمه می‌پذیرد.
توجه نمایید که هنگامی‌که یک ترتیب خاتمه می‌پذیرد تــرتیــب جــدیدی شروع می‌شود. این بدان معناست که در انتهای تابع ()stop تابع ()start دوباره صدا زده می‌شـــود. ایــن چرخه، هــنگامی‌که ()start مقدار بازگشتی NULL باز می‌گرداند خاتمه می‌پذیرد. نحوه عملکرد seq_file در شکل ۱ نشان داده شده است.
[عکس: kmprogramming-IX_html_2c3f23e0.jpg]
شکل ۱ - نحوه عملکرد seq_file
seq_file توابع ساده‌ای مانند seq_read و seq_lseek و... را بــرای file_operations فــراهــم می‌نماید. امــا نمـی‌تواند در proc/ بنویسد. برای انجام این کار بایستی مانند مثال قبلی عمل کرد.
به عنوان مثالی از seq_file به مثال procfs4.c مراجعه کنید. برای کسب اطلاعات بیشتر در این زمینه به این صفحات وب در سایت‌های LWN و KernelNewbies مراجعه کنید: http://lwn.net/Articles/22355 http://www.kernelnewbies.org/documents/seq_file_howto.txt
همچنین می‌توانید پیــاده ســازی seq_file در هسته لینوکس را در مســیر linux source/fs/seq_file.c مورد مطالعه قرار دهید. در قسمت آینده مرور مختصری بر proc/ به عنوان ورودی و sysfs خواهیم داشت.
[عکس: kmprogramming-IX_html_6b0f1c07.gif]
[عکس: www.Mojsazan.com.gif]
پاسخ
سپاس شده توسط
#10
ماژول نویسی برای هسته لینوکس (قسمت دهم)
ماژول نویسی برای هسته لینوکس ( قسمت یازدهم )

در قسـمت قـــبل بــررسی مدل جدید راه اندازها در هسته لینوکس که به Unified Device Model مــوسوم است را با معرفی چند ساختار داده اصـلی مانند kset , ktype , kobject و subsystem شــروع نمودیم. در این قســـمت به نحوه استفاده از این ساختار های داده در ماژول نویسی هسته لینوکس خواهیم پرداخت. به دلیل حجم بالای مطالب نکات اصلی این توابع مورد بحث وبررسی قرار خواهند گرفت و جزییات بیشتر به خواننده واگذار می شود.




مدیریت kobject ها

با توجه به آنچه در قسمت قبل در مورد kobject ها فرا گرفتیم به بررسی توابعی که مدیریت kobject ها را تسهیل می کنند می پردازیم. ساختار داده kobject معمولا مستقیما به کار نمی رود. بلکه در درون ساختار داده دیگری ( به عنوان مثال cdev که در قسمت قبل آن را بررسی نمودیم ) جاسازی می شود.

اولین قدم در استفاده از kobject هـا تعریف و مقداردهی اولیه آنهاست. این کار با تابع kobject_init انجام می گیرد که در <linux/kobject.h> تعریف شده است.
void kobject_init(struct kobject *kobj);
این تابع kobject ورودی را گرفته و فیلد های آن را مقداردهی می کند. قبل از صدا کردن این تابع بایستی فضای حافظه kobject صفر شود. این کار را می توان با تابع memset انجام داد.
memset(kobj, 0, sizeof (*kobj));
بعد از صفر کردن kobject می توان parent و kset آن را مقدار دهی کرد. به عنوان مثال :
kobj = kmalloc(sizeof (*kobj), GFP_KERNEL);
if (!kobj)
return -ENOMEM;
memset(kobj, 0, sizeof (*kobj));
kobj->kset = kset;
kobj->parent = parent_kobj;
kobject_init(kobj);
بــعداز مــقدار دهی اولیه بایستی نامی برایkobject درنظر گرفته شود.این کار بااستفاده ازتابع ()kobject_set_name انجام می گیرد :
int kobject_set_name(struct kobject * kobj, const char * fmt, ...);
syntax این تابع مانند printk است کــه مـی‌توان آن را بــا fmt بـه صــورت دلخواه نام گذاری کرد. با استفاده از این تابع k_name در ساختار داده kobject مقداردهی می گردد.

بعد از ایجاد یک kobject و مقدار دهی آن شما نیاز دارید که فیــلد‌هــای kset و ktype آن را تنظــیم نمایید. اگر kset نوع kobject را مشخص نکرده باشد تنظیم ktype اجباری می شود در غیر این صورت اختیاری است.







Reference Counts

یکی از امکانات اولیه ای که توسط kobject هــا فراهم شده است مکانیزم یکتایی برای شمارش ارجاعات است. بعد از مقداردهی اولیه مقدار شمارنده ارجاعات kobject به مــقدار ۱ تنظیم می شود. تا زمــانی که این شمارنده صفر نشده است object به حیات خود در حافظه ادامه خواهد داد.

هر کدی که یک ارجاع به object دارد ابتدا یک واحد این شمارنده بالا برده می شود و هنگامی که این کد به پایان رسید یک واحد این شمارنده کاهش می یابد. هنگامی که مقدار این شمارنده به صــفر رســیدobject , از بین رفته و حافظه تخصیص یافته به آن ازاد می شود.

افزایش این شمارنده توسط تابع ()kobject_get انجام می گیرد. این تابع اشاره گری به kobject و در صورت خطا NULL بر می گرداند.
struct kobject * kobject_get(struct kobject *kobj);

متقابلا کاهش این شمارنده توسط تابع ()kobject_put صورت می پذیرد.
void kobject_put(struct kobject *kobj);
اگر شمارنده به صفر برسد تابع release ای که در ktype به آن اشاره شده است صدا زده خواهد شد.







kref

شمارنده kobject توســـط ساختار داده kref که در <linux/kref.h> تعریف شده است و در lib/kref.c پیاده سازی شده است.
struct kref {
atomic_t refcount;
};
تنها فیلد این ساختار داده متغیر refcount است که به صورت atomic تعریف شده و مـــقدار شمارنده را در خود نگه می دارد. قبل از استفاده از kref بایستی آن را با استفاده از ()kref_init مقدار دهی کنید.
void kref_init(struct kref *kref)
{
atomic_set(&kref->refcount, 1);
}
برای گرفتن یک ارجاع ( بالا بردن شمارنده ) از تابع ()kref_get استفاده می شود.
void kref_get(struct kref *kref)
{
WARN_ON(!atomic_read(&kref->refcount));
atomic_inc(&kref->refcount);
}
برای رها کردن یک ارجاع ( پایین اوردن شمارنده ) از تابع ()kref_put استفاده می شود. هنگامی که مقدار شمارنده به صفر رسید تابعی که توسط اشاره گر به تابع release فراهم شده است صدا زده می شود.



void kref_put(struct kref *kref, void (*release) (struct kref *kref))
{
WARN_ON(release == NULL);
WARN_ON(release == (void (*)(struct kref *))kfree);
if (atomic_dec_and_test(&kref->refcount))
release(kref);
}
فایل سیستم [b] sysfs[/b]

فایل سیستم sysfs یک فایل سیستم مجازی در حافظه است که نمایش درختی از kobject ها را برای ما فراهم می کند. این فایل سیستم توپولوژی دستگاه ها را در یک فایل سیستم به ما نمایش می دهد. با استفاده از attribute ها kobject ها می توانند با استفاده از فایل ها این اجازه را بدهند که بتوان بعضی از متغیرهای هسته را خواند و یا در انها نوشت.

اگر چه هــدف ابتدایــی این مدل جدیــد ایجاد یک فرایند کنــتــرل انرژی هوشمند در یک سیستم کامپیوتری بود توسعه دهندگان هسته لینوکس به این نتیجه رسیدند که برای امکانات رفع خطای ساده تر آن را به صورت یک فایل در معرض دیـد بگذارند و sysfs ایجاد شد و به مرور در حال جایگزینی فـــایــل‌های دستگاه‌هـــا در proc/ می‌شــود. امروزه تـــمــام سیستم هایی که از کرنل 2.6 استفاده می کنند فایل سیستم sysfs را به صورت mount شده در سیستم خود دارند.

نمایشی از این فایل سیستم که در mount , /sys شده است را ملاحظه می فرمایید.



[عکس: sysfs.jpg]
شکل ۱- نمایی از فایل سیستم sys/


ریشه این فایل سیستم به طور استاندارد حاوی ۷ دایرکتوری است(در هسته های مختلف ممکن است متفاوت باشد) : block , bus , class , devices , firmware , module , power

دایرکتوری block برای هر دستگاه ( block device ) یـــک دایـــرکتـــوری جـــداگانه دارد. دایرکتوری bus نمایشی از bus سیستم را در خود نگه می دارد. دایرکتوری class نمایشی از دستگاه ها را که توسط توابع سطح بالایی سازمان یافته اند را در بر دارد. دایرکتوری devices نمایشی از توپولوژی دستگاه های موجود در سیستم را در خود نگه می دارد. این دایرکتوری مستقیما به ساختار درختی که از ساختارهای داده ذکر شــــده در هسته تشکیل شده انگاشته می شود. دایرکتوری firmware نیز درختی از اجزای سطح پایین سیستم مانند EFi , EDD , ACPi و ... را نگه می دارد. دایرکتوری های module و power نیز به ترتیب ساختارهایی از ماژول های کرنل و مدیریت انرژی در کرنل را نگه می دارند.

مهمترین دایرکتوری در این فایل سیستم devices است که مدلی از دستگاه های موجود در سیستم را در خود نگه می دارد. تعداد کثیری از فایل های موجود در دایرکتوری های دیگر در حقیقت اشاره گرهایی به فایل های این دایرکتوری هستند. برای اشنایی بیشتر با این فایل سیستم بهتر است ترمینال سیستم گنو/لینوکس خود را باز کرده و چرخی در این فایل سیستم بزنید تا با اجزای آن بیشتر اشنا شوید.







توابع کار با فایل سیستم [b] sysfs[/b]




در این قسمت به دلیل تعداد زیاد توابع , توابع اصلی به صورت تیتروار مورد بررسی قرار می گیرند. برای جزییات بیشتر به کتاب ها یا مقالات اشنایی با هسته لینوکس ( مانند منابع انتهای مقاله ) مراجعه کنید.

kobject هایی که مقدار اولیه داده شده اند به طور اتوماتیک به این فایل سیستم اضافه نمی شوند. برای این کار از تابع kobject_add استفاده می شود. مکان این فایل در این فایل سیستم از موقعیت kobject در ساختار درختی خود تعیین می شود. با استفاده از تابع ()kobject_register می توان دو عمل kobject_init و kobject_add را به یکباره انجام داد. برای حذف نمایشkobject درsysfs ازتابعkobject_dell استفاده می شود. تابع kobject_unregister ترکیبی از دو تابع kobject_dell و kobject_put می باشد.

kobject ها به دایرکتوری ها در sysfs نگاشته می شوند. برای نگاشتن فایل ها به sysfs از فیلد attribute موجود در kobject ها و ktype ها استفاده می شود. ساختار داده attribute در <linux/sysfs.h> تعریف شده و به صورت زیر می باشد:
struct attribute {
char *name; /* attribute's name */
struct module *owner; /* owning module, if any */
mode_t mode; /* permissions */
};
فیلدهای owner , name و mode به ترتیب بیانگر نام , صاحب و سصح دسترسی فایل موجود در sysfs می باشد.

همان طور که در قسمت قبل در بخش ktype گفتیم رفتار پیش فرض دسته ای از kobject ها در ساختار ktype در فیلد default_attrs قرار می گیرد. ولی با استفاده از فیلد sysfs_ops در همین ساختار داده می توان رفتار های kobject را مشخص کرد.
struct sysfs_ops {
/* method invoked on read of a sysfs file */
ssize_t (*show) (struct kobject *kobj,
struct attribute *attr,
char *buffer);
/* method invoked on write of a sysfs file */
ssize_t (*store) (struct kobject *kobj,
struct attribute *attr,
const char *buffer,
size_t size);
};
به طور خلاصه متد ()show برای خواندن به کار می رود. این تابع مقدار attr را در بافری به نام buffer کپی می کند. متد ()store نیز برای نوشتن به کار می رود. این تابع به اندازه size از buffer می خواند و در attr می نویسد. روش ذکر شده برای دسته ای از kobject ها کارایی دارد. برای تنظیم attribute برای یک kobject از توابع زیر استفاده می شود:


int sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);
void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr);
void sysfs_remove_link(struct kobject *kobj, char *name);
نتیجه گیری: در این دو قسمت اخیر با مدل جدیدی از دستگاه ها موسوم به sysfs and kobjects اشنا شدیم. ساختار های داده ای مانند kref , ktype , attribute , subsystem , kset و ... را مورد بررسی قرار دادیم و با روش های متفاوتی نحوه استفاده و مدیریت kobject ها و نحوه نمایش انها در sysfs را بررسی کردیم. مطالبی که در این دو قسمت بررسی شدند از جدیدترین موضوعاتی هستند که در هسته لینوکس مورد پیاده سازی قرار گرفته اندو به نظر می رسد که اشنایی هر توسعه دهنده هسته با این مبانی کاملا ضروری می نماید. مطالب این مدل جدید را در همین جا به پایان می بریم و یادگیری جزییات بیشتر را به خواننده واگذار می کنیم. در قسمت اینده به ادامه بحثمان در ماژول نویسی هسته لینوکس خواهیم پرداخت و مطالبی چون کار با توابع سیستمی را مورد بررسی قرار می دهیم.

منبع


[عکس: www.Mojsazan.com.gif]
پاسخ
سپاس شده توسط


موضوعات مشابه ...
موضوع نویسنده پاسخ بازدید آخرین ارسال
  دانلود کتاب آموزش زبان برنامه نویسی برای سیستم های آندروید مهرداد عباسی 1 1,533 12-04-2013, 12:45 PM
آخرین ارسال: harry_persian138
  شبیه wdk در ویندوز در لینوکس mahmood0197 1 767 03-06-2012, 04:08 PM
آخرین ارسال: مهرداد عباسی
  برنامه نویسی موبایل در لینوکس مهرداد عباسی 2 1,136 01-02-2012, 02:49 PM
آخرین ارسال: مهرداد عباسی
  Scala؛ جدیدترین زبان برنامه نویسی آندروید مهرداد عباسی 0 895 12-10-2011, 01:41 PM
آخرین ارسال: مهرداد عباسی
  کامپایلر GCC برای اندروید مهرداد عباسی 0 1,542 12-10-2011, 01:37 PM
آخرین ارسال: مهرداد عباسی
  اسکریپت نویسی در اندروید مهرداد عباسی 0 860 12-10-2011, 01:35 PM
آخرین ارسال: مهرداد عباسی
  برنامه نویسی با Android NDK مهرداد عباسی 0 2,187 12-10-2011, 01:32 PM
آخرین ارسال: مهرداد عباسی
  دانلود c4droid: کامپایلر c/c++ برای سیستم عامل اندروید + GCC plug-in مهرداد عباسی 0 4,771 12-10-2011, 01:30 PM
آخرین ارسال: مهرداد عباسی
  شروع کار برای برنامه نویسی اندروید مهرداد عباسی 0 1,132 12-10-2011, 01:27 PM
آخرین ارسال: مهرداد عباسی
  می خواهم برای اندروید برنامه بنویسم؟!(قسمت 1) مهرداد عباسی 0 1,190 12-10-2011, 01:26 PM
آخرین ارسال: مهرداد عباسی

پرش به انجمن:


کاربران در حال بازدید این موضوع: 1 مهمان