برمجة وحدة Linux Kernel: برنامج Hello World

وحدات Kernel عبارة عن أجزاء من التعليمات البرمجية التي يمكن تحميلها وتفريغها في kernel عند الطلب. تعمل على توسيع وظائف النواة دون الحاجة إلى إعادة تشغيل النظام. يمكن إضافة رموز مخصصة إلى نواة Linux عبر طريقتين.
  • الطريقة الأساسية هي إضافة الكود إلى شجرة مصدر النواة وإعادة ترجمة النواة.
  • الطريقة الأكثر فعالية للقيام بذلك هي إضافة تعليمات برمجية إلى النواة أثناء تشغيلها. تسمى هذه العملية تحميل الوحدة حيث تشير الوحدة إلى الكود الذي نريد إضافته إلى النواة.
نظرًا لأننا نقوم بتحميل هذه الرموز في وقت التشغيل وهي ليست جزءًا من نواة Linux الرسمية، فهي تسمى وحدة kernel القابلة للتحميل (LKM) والتي تختلف عن النواة الأساسية. يقع kernel الأساسي في دليل /boot ويتم تحميله دائمًا عندما نقوم بتشغيل أجهزتنا بينما يتم تحميل LKMs بعد تحميل kernel الأساسي بالفعل. ومع ذلك، تعد LKM هذه جزءًا كبيرًا من النواة الخاصة بنا وتتواصل مع النواة الأساسية لإكمال وظائفها. يمكن لـ LKMs أداء مجموعة متنوعة من المهام ولكنها تندرج بشكل أساسي ضمن ثلاث فئات رئيسية
  • سائق الجهاز
  • برنامج تشغيل نظام الملفات و
  • مكالمات النظام.
إذن ما هي الميزة التي تقدمها LKMs؟ إحدى المزايا الرئيسية التي تتمتع بها هي أننا لا نحتاج إلى الاستمرار في إعادة بناء النواة في كل مرة نضيف فيها جهازًا جديدًا أو إذا قمنا بترقية جهاز قديم. وهذا يوفر الوقت ويساعد أيضًا في الحفاظ على خلو النواة الأساسية من الأخطاء. القاعدة الأساسية المفيدة هي أنه لا ينبغي علينا تغيير النواة الأساسية لدينا بمجرد أن يكون لدينا نواة قاعدة عاملة. كما أنه يساعد في تشخيص مشاكل النظام. على سبيل المثال، لنفترض أننا أضفنا وحدة نمطية إلى النواة الأساسية (أي أننا قمنا بتعديل النواة الأساسية لدينا عن طريق إعادة ترجمتها) وأن الوحدة بها خطأ. سيؤدي هذا إلى حدوث خطأ في تمهيد النظام ولن نعرف أبدًا أي جزء من النواة يسبب المشاكل. بينما إذا قمنا بتحميل الوحدة في وقت التشغيل وتسببت في حدوث مشكلات، فسنعرف المشكلة على الفور ويمكننا إلغاء تحميل الوحدة حتى نصلحها. تتميز LKMs بالمرونة الشديدة، بمعنى أنه يمكن تحميلها وتفريغها باستخدام سطر أوامر واحد. يساعد هذا في توفير الذاكرة حيث نقوم بتحميل LKM فقط عندما نحتاج إليها. علاوة على ذلك، فهي ليست أبطأ من النواة الأساسية لأن استدعاء أي منهما يؤدي ببساطة إلى تحميل التعليمات البرمجية من جزء مختلف من الذاكرة. ** تحذير: LKMs ليست برامج مساحة المستخدم. هم جزء من النواة. لديهم تشغيل مجاني للنظام ويمكنهم تعطله بسهولة. So now that we have established the use loadable kernel modules we are going to write a hello world kernel module. That will print a message when we load the module and an exit message when we unload the module. Code: CPP
   /**    * @file hello.c    * @author Akshat Sinha    * @date 10 Sept 2016    * @version 0.1    * @brief An introductory 'Hello World!' loadable kernel    * module (LKM) that can display a message in the /var/log/kern.log    * file when the module is loaded and removed. The module can accept    * an argument when it is loaded -- the name which appears in the    * kernel log files.   */   #include         /* Needed by all modules */   #include         /* Needed for KERN_INFO */   #include         /* Needed for the macros */   /// < The license type -- this affects runtime behavior   MODULE_LICENSE  (  'GPL'  );   /// < The author -- visible when you use modinfo   MODULE_AUTHOR  (  'Akshat Sinha'  );   /// < The description -- see modinfo   MODULE_DESCRIPTION  (  'A simple Hello world LKM!'  );   /// < The version of the module   MODULE_VERSION  (  '0.1'  );   static     int     __init     hello_start  (  void  )   {      printk  (  KERN_INFO     'Loading hello module...  n  '  );      printk  (  KERN_INFO     'Hello world  n  '  );      return     0  ;   }   static     void     __exit     hello_end  (  void  )   {      printk  (  KERN_INFO     'Goodbye Mr.  n  '  );   }   module_init  (  hello_start  );   module_exit  (  hello_end  );   
شرح الكود أعلاه: يجب أن تحتوي وحدات Kernel على وظيفتين على الأقل: وظيفة "البدء" (التهيئة) تسمى init_module() والتي يتم استدعاؤها عندما يتم إدخال الوحدة في النواة ووظيفة "النهاية" (تنظيف) تسمى cleanup_module() والتي يتم استدعاؤها قبل أن يتم تعديلها مباشرة. في الواقع تغيرت الأمور بدءًا من النواة 2.3.13. يمكنك الآن استخدام أي اسم تريده لوظائف البداية والنهاية للوحدة النمطية. في الواقع الطريقة الجديدة هي الطريقة المفضلة. ومع ذلك، لا يزال العديد من الأشخاص يستخدمون init_module() وcleanup_module() لوظائف البداية والنهاية الخاصة بهم. في هذا الكود استخدمنا hello_start() كدالة init وhello_end() كدالة تنظيف. شيء آخر ربما لاحظته هو أننا بدلاً من وظيفة printf() استخدمنا printk(). وذلك لأن الوحدة لن تطبع أي شيء على وحدة التحكم ولكنها ستقوم بتسجيل الرسالة في /var/log/kern.log. ومن ثم يتم استخدامه لتصحيح وحدات kernel. علاوة على ذلك، هناك ثمانية سلاسل محتملة لمستوى السجل محددة في الرأس وهي مطلوبة أثناء استخدام printk(). لقد قمنا بإدراجها حسب تقليل الخطورة:
  • KERN_EMERG: يُستخدم لرسائل الطوارئ عادةً تلك التي تسبق حدوث عطل.
  • KERN_ALERT: موقف يتطلب اتخاذ إجراء فوري.
  • KERN_CRIT: غالبًا ما ترتبط الحالات الحرجة بحالات فشل خطيرة في الأجهزة أو البرامج.
  • KERN_ERR: يُستخدم للإبلاغ عن حالات الخطأ؛ غالبًا ما تستخدم برامج تشغيل الأجهزة KERN_ERR للإبلاغ عن صعوبات الأجهزة.
  • KERN_WARNING: تحذيرات بشأن المواقف الإشكالية التي لا تؤدي في حد ذاتها إلى حدوث مشكلات خطيرة في النظام.
  • KERN_NOTICE: المواقف الطبيعية ولكنها لا تزال جديرة بالملاحظة. تم الإبلاغ عن عدد من الحالات المتعلقة بالأمن على هذا المستوى.
  • KERN_INFO: رسائل إعلامية. يقوم العديد من برامج التشغيل بطباعة معلومات حول الأجهزة التي يجدونها عند بدء التشغيل عند هذا المستوى.
  • KERN_DEBUG: يستخدم لتصحيح الرسائل.
  • لقد استخدمنا KERN_INFO لطباعة الرسالة. تحضير النظام لتشغيل الكود: The system must be prepared to build kernel code and to do this you must have the Linux headers installed on your device. On a typical Linux desktop machine you can use your package manager to locate the correct package to install. For example under 64-bit Debian you can use:
    akshat@gfg:~$ sudo apt-get install build-essential linux-headers-$(uname -r)  
    Makefile لتجميع التعليمات البرمجية المصدر:
    obj-m = hello.o all: make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean  
    **ملاحظة: لا تنس مسافات علامات التبويب في Makefile تجميع وتحميل الوحدة: Run the make command to compile the source code. Then use insmod to load the module.
    akshat@gfg:~$ make make -C /lib/modules/4.2.0-42-generic/build/ M=/home/akshat/Documents/hello-module modules make[1]: Entering directory `/usr/src/linux-headers-4.2.0-42-generic' CC [M] /home/akshat/Documents/hello-module/hello.o Building modules stage 2. MODPOST 1 modules CC /home/akshat/Documents/hello-module/hello.mod.o LD [M] /home/akshat/Documents/hello-module/hello.ko make[1]: Leaving directory `/usr/src/linux-headers-4.2.0-42-generic'  
    Now we will use insmod to load the hello.ko object.
    akshat@gfg:~$ sudo insmod hello.ko  
    اختبار الوحدة: You can get information about the module using the modinfo command which will identify the description author and any module parameters that are defined:
    akshat@gfg:~$ modinfo hello.ko filename: /home/akshat/Documents/hello-module/hello.ko version: 0.1 description: A simple Hello world LKM author: Akshat Sinha license: GPL srcversion: 2F2B1B95DA1F08AC18B09BC depends: vermagic: 4.2.0-42-generic SMP mod_unload modversions  
    To see the message we need to read the kern.log in /var/log directory.
    akshat@gfg:~$ tail /var/log/kern.log ... ... Sep 10 17:43:39 akshat-gfg kernel: [26380.327886] Hello world To unload the module we run rmmod: akshat@gfg:~$ sudo rmmod hello Now run the tail command to get the exit message. akshat@gfg:~$ tail /var/log/kern.log ... Sep 10 17:43:39 akshat-gfg kernel: [26380.327886] Hello world Sep 10 17:45:42 akshat-gfg kernel: [26503.773982] Goodbye Mr.