یوشا

دست نوشته ها و تجربیات شخصی

یوشا

دست نوشته ها و تجربیات شخصی

شهید دکتر مصطفی چمران: می گویند تقوا از تخصص لازمتر است، آنرا می پذیرم، اما می گویم آنکس که تخصص ندارد و کاری را می پذیرد بی تقواست!

طبقه بندی موضوعی

۱۱ مطلب با موضوع «نرم افزار :: برنامه نویسی :: C» ثبت شده است

۰۸
۱۴۰۴/۰۴

 

در ادامه مقاله قبلیم(12 سال پیش!) که ماژول Kernel لینوکس رو تعریف کردم، در این مقاله به نوشتن ماژول برای Kernel لینوکس می پردازم.

در این مقاله، به صورت ساده یاد می‌ گیریم که چگونه یک ماژول Hello World برای کرنل Linux بنویسیم، کامپایل و اجرا کنیم... و همه اینها بر پایه توزیع های معروف مثل Debian, RedHat و Slackware انجام میدیم.

 

ماژول های هسته لینوکس

هسته لینوکس / Linux kernel قلب سیستم‌ عامل لینوکس است و مسئول مدیریت منابع سخت‌ افزاری، ارتباط با سخت‌ افزار و اجرای برنامه‌ هاست. هسته لینوکس به صورت ماژولار طراحی شده، یعنی بسیاری از قابلیت‌ های آن به صورت فایلهای جدا و Load شدنی (Loadable Kernel Modules - LKM) پیاده‌ سازی می‌ شوند.

ماژول های هسته Linux دارای چند نوع هستند:  ماژول‌ های درایور دستگاهها (Device drivers)، ماژول‌ های فایل سیستم (Filesystem)، ماژول‌ های شبکه (Network)، ماژول‌ های امنیتی (Security) و ماژول‌ های مدیریت حافظه و پردازش (Memory & process management). این ماژول ها باعث اضافه شدن feature در هسته، افزایش کارایی، سفارشی‌ سازی آسان و نگهداری ساده‌ تر خود هسته می‌ شوند.

 

پیش‌ نیازها برای توزیع‌ های مختلف

جهت توسعه و تولید ماژول هسته لینوکس، به تعدادی ابزار و سورس نیاز هست که در زیر آنها را نصب می کنیم:

1. خانواده دبیان (Debian/Ubuntu)

sudo apt update
sudo apt install build-essential linux-headers-$(uname -r)

2. خانواده ردهت (Fedora/RHEL/CentOS)

sudo dnf install kernel-devel gcc make

3. اسلکور (Slackware) 

sudo slackpkg install gcc make kernel-headers kernel-source

 

نوشتن ماژول Hello World (برای همه توزیع‌ ها)

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Hello World module for Slackware and other distros");
MODULE_VERSION("1.1");

static int __init hello_init(void) {
    printk(KERN_INFO "Hello from Slackware Kernel!\n");
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Goodbye from Slackware Kernel!\n");
}

module_init(hello_init);
module_exit(hello_exit);

 توضیحات کدهای بالا:

  • #include <linux/module.h>: یک فایل هدر (header) است که به توسعه‌ دهندگان ماژول‌ امکان می‌ دهد تا ماژول‌ های خود را با هسته لینوکس ادغام کنند. این فایل شامل defines، structures، ماکروها و توابعی است که برای نوشتن ماژول‌ های هسته ضروری هستند.

  • MODULE_LICENSE("GPL"): مشخص می‌ کند که ماژول تحت پروانه GPL است (اجباری).

  • printk: تابع چاپ در Log کرنل (مشابه printf اما برای کرنل).

    • KERN_INFO سطح Log را تعیین می‌ کند (در /var/log/kern.log یا dmesg دیده می‌ شود).

  • __init و __exit:

    • __init تعیین می کند که تابع فقط یک بار هنگام لود اجرا شود و سپس حافظه آزاد شود.

    • __exit برای توابعی است که فقط هنگام حذف ماژول اجرا می‌ شوند.

  • module_init و module_exit: توابع initialize و finalize ماژول را register می‌ کنند.

 

کامپایل و نصب ماژول

جهت کامپایل و نصب ماژول می توان بصورت دستی هم عمل کرد و دستورات را وارد کرد، اما بهتره یک فایل builder بنام Makefile بسازید و محتویات زیر رو داخل وارد کنید.

و سپس دستور make رو در کنسول سیستم در همون مسیر بزنید تا ماژول تولید بشه.

1. برای دبیان/ردهت

obj-m := hello_world.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    make -C $(KDIR) M=$(PWD) clean

2. برای اسلکور

obj-m := hello_world.o
KDIR ?= /usr/src/linux-$(shell uname -r)
PWD := $(shell pwd)

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    make -C $(KDIR) M=$(PWD) clean

 توضیحات کدهای بالا:

obj-m := hello_world.o فایل ماژول نهایی: hello_world.ko.

KDIR مسیر هدر های کرنل.

PWD مسیر فعلی.

make ساخت ماژول (hello_world.ko).

make clean پاکسازی فایل‌ های موقت.

نکته برای اسلکور:
در بعضی نسخه های Slackware ممکن است نیاز باشد مسیر کرنل را به صورت دستی مشخص کنید:

export KDIR=/usr/src/linux-$(uname -r)

 

اجرا و توقف ماژول

برای اجرا یا متوقف کردن ماژول از دستورات زیر استفاده می کنیم. این دستورات در همه توزیع های لینوکس موجود هستند:

1. برای لود ماژول

sudo insmod hello_world.ko

2. جهت بررسی خروجی

dmesg | tail -n 2

خروجی نمونه

[123456789] Hello from Slackware Kernel!

3. جهت حذف ماژول

sudo /sbin/rmmod hello_world

نکات ویژه Slackware

  1. مسیر کرنل:
    در Slackware کرنل در /usr/src/linux-$(uname -r) قرار دارد.

  2. تفاوت در ابزارها:
    در Slackware از insmod/rmmod به جای modprobe استفاده می شود..

  3. مدیریت ماژول‌ ها:
    برای لیست ماژول‌ های لود شده در Slackware:

    cat /proc/modules
    

 

تفاوت syscall با function call

در حین توسعه هسته یا ماژول های هسته ممکنه با واژه های syscall زیاد سروکار داشته باشید، اما syscall دقیقا چی هست:

  • معقوله system call (یا همون syscall) با محیط kernel در ارتباطه ولی function call با محیط user در ارتباطه.
  • system call با سیستم عامل تعامل برقرار می کنه ولی function call با برنامه و کتابخانه.
  • بخاطر گستردگی و تودرتو بودن system call، معمولاً منابع بیشتری صرف call کردن اونها میشه، ولی function call هزینه های کمتری رو دربر میگیره.
  • دستورات system call توسط function call صدا زده میشه ولی function call توسط برنامه صدا زده میشه.
  • یک دستور system call تکه ای کد در kernel-space هستش ولی function call تکه ای کد در user-space هستش.

تنها شباهت اینها در مهیا کردن خدمات به caller هستش.

 

مشکلات رایج

مشکل: Kernel Panic پس از لود ماژول
دلایل: دسترسی به حافظه نامعتبر (NULL pointer dereference) یا استفاده از توابع فضای user در کرنل (مثل printf به جای printk یا kmalloc بجای malloc)

 

مشکل: ماژول روی نسخه‌ های مختلف هسته کار نمی‌ کند
دلایل: تغییرات API کرنل بین نسخه‌ ها. پس کد را با آخرین نسخه‌ های کرنل تست کنید یا از LINUX_VERSION_CODE برای بررسی نسخه کرنل استفاده کنید

 

مشکل: Memory Leak در ماژول
دلایل:  آزاد نکردن حافظه در تابع exit یا فراموش کردن آزادسازی منابع. می توانید از ابزارهایی مانند kmemleak برای تشخیص leak حافظه استفاده کنید.

 

معرفی ماژولهای اوپن‌ سورس جهت یادگیری
1. پروژه eBPF

مانیتورینگ شبکه و trace سیستم

لینک: github.com/iovisor/bcc

2. پروژه FUSE
پیاده‌ سازی سیستم فایل در User-space

لینک: github.com/libfuse/libfuse

3. پروژه Open-IOMMU
درایور IOMMU برای مدیریت دسترسی به حافظه

لینک: github.com/OpenIOMMU

4. پروژه LTTng
سیستم trace عملکرد کرنل جهت دیباگ و آنالیز سیستم

لینک: github.com/lttng

5. پروژه KVM
توضیحات: ماژول مجازی‌ سازی جهت ساخت ماشین‌ های مجازی

لینک: git.kernel.org/pub/scm/virt/kvm/kvm.git

  • یوشا آل ایوب
۱۴
۱۴۰۰/۰۱
  • یوشا آل ایوب
۱۰
۱۳۹۹/۰۷
  • هر قطعه کد تست باید کوتاه، قابل فهم و خوانا باشه.
  • کدهای تست باید قطعه قطعه و به واحدهای مستقل از هم تقسیم بشن.
  • کدهای تست باید مستقل از محیط اجرایی باشن و وابستگی به platform نداشته باشن.
  • بهتره شیوه اجرای کدهای تست بسادگی و توسط یک دستور انجام بشه.
  • امکان گزارش گیری Test code coverage باید فراهم باشه.
  • اجرای تست case ها از پایین ترین سطح(Unit) به بالاترین سطح(E2E) باید باشه.
  • تست case هارو توسط الگوی AAA بنویسید.
  • در کدهای تست باید از نقل/انتقال اطلاعات حجیم خودداری کرد تا پروسه تست بسرعت انجام بشه.
  • کدهای تست باید بروز باشن و با هر تغییر جدی روی کدهای اصلی باید تغییر کنند.
  • حجم کل کدهای تست تولید شده معمولاً باید برابر یا بیشتر از حجم کدهای اصلی باشه. (یعنی برای همه موارد تست نوشته شده باشه)
  • کدهای تست باید در همان روزی که کدهای اصلی پروژه نوشته میشن تولید بشن. (به روزهای آینده موکول نشه)
  • تست case ها باید کدها، متدها و امکانات پروژه رو به سخت ترین شکل به چالش بکشن.
  • از test double ها استفاده مجدد کنید.
  • تا حد امکان از تست کردن محتوای private کلاس ها و Private API ها پرهیز کنید. (بواسطه کدهای public تست blackbox انجام بدید یا CUT رو redesign کنید)
  • هنگام نوشتن تست case تا حد امکان از بکار بردن دستورات شرطی if/else/switch... پرهیز کنید.
  • بهتره کدهای تست به خارج از محدوده پروژه dependency نداشته باشن.
  • بهتره نام فایل تست به کلمه Test ختم بشه. مثل EmailTest, UtilityTest, DatabaseTest
  • بهتره نام توابع/متدهای داخل فایل تست با کلمه test شروع بشن. مثل test_if_email_is_valid یا testIsEmailValid
  • همیشه تست هارو هم برای سناریوی happy path و هم unhappy path بنویسید. (تست happy path تضمین می کنه که سیستم در شرایط عادی به درستی کار می کنه، اما unhappy path به کشف و رسیدگی به مسائل مربوط به مدیریت خطا، امنیت و انعطاف پذیری در مواجهه با سناریوهای غیرمنتظره کمک می کنه.)

 

نکته: وظیفه نوشتن کدهای تست برای Unit Testing بعهده فرد برنامه نویس هستش نه فرد Tester. زیرا:

- بدلیل حفظ مالکیت کدها/پروژه، Tester نباید به سورس پروژه دسترسی داشته باشه.

- بدلیل مسایل امنیتی و کاهش تهدیدها، Tester نباید به داخل کدها و مکانیزم سیستم دسترسی داشته باشه.

- همچنین Tester قادر نیست به همه ابزارها، سبکها و زبانهای مختلفی که در پروژه استفاده شده مسلط بشه و test case طراحی کنه.

- تنها برنامه نویس هستش به کدهایی که پیاده سازی کرده مسلطه و test case رو در کمترین زمان با بالاترین کیفیت تولید میکنه.

  • یوشا آل ایوب
۱۵
۱۳۹۹/۰۲

 

Functionality test, Performance/Speed test, User experience test, Compatibility test, Security test

 

نکته: هر 5 نوع تست بالا رو میشه با 2 روش manual و automatic و در 5 سطح/لایه مختلف انجام داد:

Unit Test, Integration Test, Feature/Module Test. System Test, [User] Acceptance/E2E Test

  • یوشا آل ایوب
۱۲
۱۳۹۸/۰۱

مقایسه پرفورمنس(startup, rendering, event handling)

از سریع‌ ترین به کندترین:

Windows platform
FLTK > wxWidgets > GTK > Qt

Linux platform
FLTK > GTK > (wxWidgets / Qt)

نکته: در اینجا منظور از GTK نسخه GTKmm هستش که wrapper/مخصوص  ++C  هستش.
نکته 2: عناصر کتابخانه  wxWidgets صددرصد native هستن. اما عناصر کتابخانه FLTK و Qt و GTK فقط "شبیه" به native هستن. که برای این شبیه سازی از CSS و API های theming سیستم استفاده می کنن.


این توضیحات رو هم در نظر بگیرید:
- کتابخانه FLTK از نظر استایل بهترین گزینه برای لینوکس با محیط EDE هستش. از نظر فضای دیسک، بسیار کم حجم هستش. پرفورمنس بالاتری نسبت به بقیه کتابخانه ها داره. استفاده ازش آسون هست. از بعضی دیگر زبانهای برنامه نویسی هم پشتیبانی میکنه. در ویندوز برروی WinAPI، در لینوکس برروی X Window و در مک بر روی Quartz سوار هست. FLTK بعضی از widget های مدرن مثل Treeview رو نداره. بعضی از widget ها حالت RTL (راست به چپ) رو ندارن.

 

- کتابخانه wxWidgets از نظر استایل بهترین گزینه برای ویندوز و مک و لینوکس هستش. از نظر فضای دیسک، حجیم هستش. یادگیری و استفاده ازش کمی سخته. UI designer هم داره. از خیلی زبانهای برنامه نویسی پشتیبانی میکنه. در لینوکس بر روی کتابخانه GTK قرار داره و در پشت پرده از API های GTK استفاده میکنه. wxWidgets فقط کتابخانه GUI نیست، یک فریم ورک محسوب میشه.

 

- کتابخانه GTK از نظر استایل بهترین گزینه برای لینوکس با محیط Cinnamon, GNOME, XFCE یا Mate هستش. یادگیری و استفاده ازش آسونه. UI designer هم داره. از خیلی زبانهای برنامه نویسی پشتیبانی میکنه.

 

- کتابخانه Qt از نظر استایل بهترین گزینه برای لینوکس با محیط KDE, TDE یا LXQt هستش. از نظر فضای دیسک، حجیم هستش. یادگیری و استفاده ازش بواسطه Qt Creator آسونه. UI designer هم داره. از خیلی زبانهای برنامه نویسی پشتیبانی میکنه. امکانات غیر استاندارد هم داره(meta-object). طیف گسترده تری از platform ها رو در بر گرفته. Qt فقط کتابخانه GUI نیست، یک فریم ورک هستش. برای پروژه های غیر opensource باید لایسنس داشته باشید. از مدل برنامه نویسی reactive پشتیبانی می کنه. (به کمک شتابدهنده گرافیکی میشه بار سنگین رندر رو از روی پردازنده برداشت، بنابراین باعث افزایش پرفورمنس میشه)

 

جمع‌بندی انتخاب کتابخانه بر اساس نیاز

معیار بهترین گزینه
بالاترین پرفورمنس FLTK
Native Look در ویندوز wxWidgets
Native Look در لینوکس GTK
multi-platform + امکانات پیشرفته Qt
سبک‌ وزنی و Embedded systems FLTK
پروژه تجاری با لایسنس Qt یا wxWidgets
  • یوشا آل ایوب
۰۲
۱۳۹۶/۰۴

💡 مقایسه سرعت الگوریتم binary search مقابل الگوریتم linear search برای آرایه های مرتب شده(sorted)

(برای آرایه های عددی سنگین)

 

$needle = range(1, 500000);

 

❌ Linear search algorithm

function search(array $numbers, $needle)
{
   $_totalItems = count($numbers);

   for ($i = 0; $i < $_totalItems; $i ++)
   {
      if ($numbers[$i] === $needle)
      {
         return TRUE;
      }
   }

   return FALSE;
}

📊 نتیجه(ms)
0.031199932098389
0.031199932098389
0.031199932098389
0.031199932098389
0.031199932098389
0.031199932098389
0.031200170516968
0.031200170516968
0.046799898147583
0.046800851821899

 


✅ Binary search algorithm

function search(array $numbers, $needle)
{
   $_low = 0;
   $_high = count($numbers) - 1;

   while ($_low <= $_high)
   {
      $_middle = (int) (($_low + $_high) / 2);
      if ($numbers[$_middle] > $needle)
      {
         $_high = $_middle - 1;
      }
      else if ($numbers[$_middle] < $needle)
      {
         $_low = $_middle + 1;
      }
      else
      {
         return TRUE;
      }
   }

   return FALSE;
}

📊 نتیجه(ms)
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0

 

x64 Hardware
32bit OS
PHP 5.6 CLI

کد از کتاب "PHP 7 Data Structures and Algorithms"

  • یوشا آل ایوب
۲۱
۱۳۹۵/۰۷

Virtual File System یا سیستم فایل مجازی

 

Virtual File System(مخفف VFS) یک ساب سیستم مهم در Kernel لینوکس هست که لایه بین filesystem و برنامه های user-space رو تشکیل میده. درواقع همه filesystem ها، با تکیه بر VFS می تونن شناسایی بشن و تبادل اطلاعات کنن. همین مسئله، برنامه های user-space رو قادر کرده که توسط دستورات موجود در VFS برای خواندن و نوشتن در filesystem های گوناگون اقدام کنن:

 

نکته: در اینجا منظور از لایه، همون Abstraction layer و منظور از دستورات، همون System call/SysCall هستش.

نکته 2: البته به برکت wrap های GNU C Library، کمتر پیش میاد که برنامه user-space نیاز به استفاده از System call داشته باشه.

  • یوشا آل ایوب
۳۰
۱۳۹۱/۰۵

 

ماژول، قطعه ی نرم افزاری در بخشی جدا از Core هسته هستش که هنگام فراخونی شدن، پیوند و فعال میشه و یکسری عملیات تعریف شده ای رو انجام میده.
این ماژول می تونه سرویس باشه، filesystem باشه، پروتکل شبکه باشه، تعدادی System call باشه و یا درایور یک سخت افزار باشه; که در هر صورت ماژول نام داره.

ماژول ها در لینوکس به دو گروه تقسیم میشن:

  1. Built-in kernel module
  2. Loadable kernel module (یا LKM)

Built-in kernel module: که با قرار دادن سورس ماژول در داخل پوشه های سورس Kernel، همراه با Kernel کامپایل میشه.
Loadable Kernel module: که با load کردن ماژول کامپایل شده در داخل سیستم در حال اجرا فعال میشه.

  • یوشا آل ایوب
۳۰
۱۳۹۱/۰۴

 

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

ابتدا برخی از اصول سیستم عامل و هسته لینوکس(از جمله Version های هسته، دانلود سورس، ساختمان دایرکتوری سورس، چگونگی نصب، Patch کردن، پیکربندی و Compile) رو معرفی می کنم و بعد به مباحث پیشرفته و برنامه نویسی می پردازم.

در این بین از کتابهای Advanced Linux programming، Linux Advanced Administration و Linux Kernel Development هم بهره ای می گیرم.
 

فهرست مندرجات

  • تعریف سیستم عامل
  • انواع معماری سیستم عامل
  • تعریف هسته
  • انواع معماری هسته

  • یوشا آل ایوب
۱۵
۱۳۹۱/۰۴

ExeStrip: این برنامه Header/Stup [داس] رو از فایل های Exe جدا می کنه.

BootSectorWriter: این برنامه 512 بایت از داده یا فایل رو روی سکتور بوت Write می کنه. (هارددیسک و فلاپی دیسک)

 

  • یوشا آل ایوب