پنجشنبه، خرداد ۰۳، ۱۳۸۶

کامپایل کردن کرنل لینوکس

کرنل یا هسته لینوکس از قسمت‌هایی تشکیل شده که به اصطلاح به آن‌ها module یا اگر بخواهیم کامل‌تر بگوییم kernel module می‌گویند. بسیاری از این module ها درایورهای مختلف هستند. گاهی لازم می‌شود که برای کاری تحقیقاتی یک سیستم سبک و کم حجم بسازید، یا module ای را به کرنل خود اضافه کنید. این کار را با کامپایل و نصب کردن کرنل لینوکس خود می‌توانید انجام دهید. دستورات این پست با Fedora Core 6 امتحان شده‌اند. احتمالا با کمی تغییر می‌توانید همین کار را با distribution های دیگر هم انجام دهید. برای انجام این کار احتیاج به پکیج سورس کرنل خود نیز دارید. در مورد فدورایی که من استفاده می‌کردم این فایل (که یک فایل .src.rpm است) از این‌جا (فایل:+، فولدر:+) قابل دانلود است.

 

۱.

اگر نتوانستید به راحتی بالا سورس کرنل لینوکس خود را دانلود کنید، اول باید ببینید نسخه آن چیست. برای این کار از دستور زیر استفاده کنید:

$ uname -r
[this will print kernel release version]
$ uname -a
[this will print all system information]

سپس با اطلاعات بالا دنبال پکیج سورس آن بگردید، یا به پانوشت ۱ مراجعه کنید.

 

۲.

فرض کنید پکیج سورس کرنل را دانلود کرده‌اید. در مورد فدورایی که من استفاده می‌کنم، نام این فایل kernel-2.6.18-1.2798.fc6.src.rpm است. در دستورات این نام را با نام بسته خودتان عوض کنید. برای اجرای بقیه دستورها باید کاربری با اختیارات root یا خود root باشید.

$ rpm -Uvh kernel-2.6.18-1.2798.fc6.src.rpm
$ cd /usr/src/redhat/SPECS
$ rpmbuild -bp --target $(arch) kernel-2.6.spec

دستور rpm اولی موجب نصب پکیج می‌شود، اما چون این بسته تنها یک بسته سورس است، یک سری فایل سورس در /usr/src کپی خواهد کرد. –Uvh به معنای upgrade کردن بسته است (U) و گزارش لحظه به لحظه از کاری که در حال انجام است (v) و نشان دادن میزان پیشرفت با کاراکتر hash که # است (h). حاصل آن فایل .spec ایست که rpmbuild در دستور بعدی از آن استفاده خواهد کرد.

 

۳.

حالا وقت اضافه و کم کردن module هاست. این که به طور پیش فرض چه module هایی نصب می‌شوند در فایل مخفی .config نوشته شده. برای تغییر محتویات این فایل به طور گرافیکی یا متنی از دستورات زیر استفاده کنید.

$ cd /usr/src/redhat/BUILD/kernel-2.6.18/linux-2.6.18-i686
[the .config file and the Makefile reside here]

$ make help
[lists all available options to configure and make]

[run one of the following, as you wish]

$ make xconfig
[GUI configuration, using Qt for KDE]
[probably the most user-friendly option]

$ make gconfig
[GUI configuration, using GTK for GNOME]

$ make menuconfig
[text based configuration, using ncurses]

اگر از روش گرافیکی استفاده می‌کنید، می‌توانید درختی از تمامی module ها ببینید. آن‌هایی را که می‌خواهید نصب کنید را انتخاب کنید و آن‌هایی را که نمی‌خواهید را از حالت انتخاب بیرون بیاورید. مثلا می‌توانید به قسمت filesystems بروید و ntfs را انتخاب کنید. با save کردن تغییراتی که دادید فایل .config به‌روز خواهد شد.

 

۴.

الآن دیگر می‌توانید کامپایل و نصب را شروع کنید. این کار تقریبا نیم الی یک ساعت طول خواهد کشید. توجه کنید که مرحله آخر نصب، boot-loader را نیز update خواهد کرد.

$ make
$ make modules_install
$ make install

 

۵.

برای استفاده از کرنل جدید باید سیستم را reboot کنید. با این کار خواهید دید که یک گزینه به گزینه‌های grub (یا lilo) اضافه شده. گزینه جدید که کلمه prep در آن به چشم می‌خورد، همان کرنل جدید است.

 

 

پ.ن.

 

۱. نسخه‌های اصلی کرنل را گروهی به سرپرستی لینوس توروالدس تولید می‌کنند. سپس distribution های مختلف، بسته به کاربرد یا ایدئولوژی خود، آن را تغییر می‌دهند (patch تغییراتشان اکثرا از سایت خودشان قابل دانلود است). نسخه‌های اصلی کرنل را می‌توان ازwww.kernel.org  دانلود کرد. اگر قصد کامپایل چنین نسخه‌ای را دارید، توجه کنید که نسخه آن باید با نسخه کرنل لینوکس شما سازگار باشد، مثلا با 2.6.18 شروع شده باشد. اگر این نسخه را کامپایل کنید، کرنل حاصل دیگر کرنل patch شده توسط distribution شما نیست، بلکه کرنلی است که لینوس توروالدس منتشر کرده.

 

۲. این دستورات مخصوص distribution های redhat ایست، و به احتمال زیاد برای distribution های debian ای مثل Ubuntu کار نخواهند کرد. اصلا در این نسخه‌ها دستور rpm‌ وجود ندارد، و احتمالا پسوند پکیج سورس آن‌ها .deb است.

 

 

سه‌شنبه، اردیبهشت ۱۸، ۱۳۸۶

یک الگوی مناسب برای برنامه‌های console

برای ساده شدن بحث، فرض کنید می‌خواهیم برنامه‌ای بنویسیم که جملاتی را دریافت، و حروف کوچک آن را بزرگ کند. گرچه این برنامه بسیار ابتدایی است اما برای بحث ما مناسب است. یک شیوه طراحی چنین برنامه‌ای این است که، یک آرگومان برای نام فایل ورودی درنظر بگیریم و یک آرگومان نیز برای فایل خروجی. سپس برنامه آرگومان‌ها را بررسی و کار خود را انجام دهد. اگر آرگومان‌ها پاس نشدند، یا اشتباه پاس شدند برنامه پیغام خطا خواهد داد و اجرا متوقف می‌شود. بد نیست!

اما شیوه خوب طراحی (که شاید به نظر بسیار بد بیاید)، این است که برنامه ورودی‌هایش را از stdin بخواند، خروجیش را در stdout چاپ کند، و خطاهای احتمالی را در stderr. روش خواندن از stdin هم به گونه‌ای باشد که هرگاه به EOF رسید متوقف شود. نیازی هم به پشتیبانی از فایل نیست! اما اگر باشد چه بهتر. مثال ساده زیر، همین کار را می‌کند. فرض کنید نام فایل اجرایی my2upr[.exe] باشد.

my2upr.cpp
  01:   #include <iostream>
  02:   #include <string>
  03:   using namespace std;
  04:   
  05:   int main()
  06:   {
  07:       char szBuffer[1024];
  08:       while(cin.getline(szBuffer, 1024))
  09:       {
  10:           cout << strupr(szBuffer) << endl;
  11:       }
  12:   
  13:       return 0;
  14:   }

خط ۸ موجب خواندن خط به خط از ورودی تا زمان مشاهده EOF می‌شود. هنگام اجرای برنامه برای ایجاد EOF در Windows از Ctrl+Z و در UNIX از Ctrl+D استفاده کنید. خوب تا این‌جا ممکن است این روش، در مقایسه با روش آرگومانی، به نظر افتضاح بیاید. اما:

 

خواندن یک فایل و مشاهده آن در خروجی:

./my2upr < input.txt 

خواندن یک فایل و ذخیره خروجی در فایلی دیگر:

./my2upr < input.txt > output.txt

مشاهده محتویات یک فایل به حروف بزرگ:

cat readme.txt | ./my2upr | less

توضیح این که، < موجب می‌شود محتویات یک فایل به عنوان ورودی برنامه (یعنی به جای stdin) قرار گیرد. > خروجی برنامه (یعنی stdout) را به یک فایل منتقل می‌کند. پایپ نیز نتیجه اجرای برنامه قبل خود را به عنوان ورودی برنامه بعد خود قرار می‌دهد.

 

خلاصه این که، استفاده از این روش، قابلیت‌های اسکریپتی بسیار زیادی به برنامه می‌بخشد. بسیاری از دستورات UNIX به همین شیوه طراحی شده‌اند.


موسیقی متن پرنده‌ی خارزار

قطعه‌ای از موسیقی متن فیلم Thorn Birds اثر هنری مانچینی؛ تقدیم به لحظات دل‌تنگی‌تان.

و مختصری توضیح:

هنری مانچینی رو بیشتر با موسیقی پلنگ صورتی می‌شناسند. اکثر کارهای او ملایم و عاطفی است (حتی پلنگ صورتی). او در سال ۱۹۹۴ در گذشت.

Thorn Birds نام سریال کوتاهی است که در سال ۱۹۸۳ ساخته شد. نسل قبل از ما این سریال را به سختی فراموش خواهد کرد. رمان Thorn Birds به فارسی پرنده‌ی خارزار ترجمه شده.

جمعه، اردیبهشت ۱۴، ۱۳۸۶

دنیا ارزش جنگیدن را دارد؟

سامرست در سکانس پایانی فیلم هفت

«همینگوی می‌گوید:
"دنیا جای خوبیست، ارزش جنگیدن را دارد ..."
من با بخش دوم موافقم...»

دوشنبه، اردیبهشت ۱۰، ۱۳۸۶

درک

این پست از وبلاگ یک لیوان چای داغ برام خیلی جالب بود...

جمعه، اردیبهشت ۰۷، ۱۳۸۶

به ازای هر کودک یک لپتاپ

قصد بدبینی یا پیش‌داوری ندارم؛ اما خیلی وقت‌ها کسانی که اعلام می‌کنند دنبال ساختن دنیای بهتری برای زندگی کردن هستند، بیشتر دنبال ارضای نیازهای مادی یا روانی درونی خودشون هستند، و بدتر با خارج کردن امور از روند طبیعی خودش دنیا رو جهنم‌تر می‌کنند. باز هم می‌گم، قصد بدبینی ندارم. امیدوارم کارهای این موسسه وضع آموزشی کودکان کم بضاعت رو (همون طور که خودشون تبلیغ می‌کنند) بهتر کنه.

چند لینک:

موسسه OLPC: One Laptop Per Child

یک مقاله از IBM در مورد مشخصات دستگاه و شبیه ساز آن برای PC

پنجشنبه، اردیبهشت ۰۶، ۱۳۸۶

پیش‌پردازنده در C++

۱.
در
C/C++ عباراتی وجود دارند که کامپایل نمی‌شوند، بلکه کامپایلر را هدایت می‌کنند. این دستورات با کاراکتر # شروع می‌شوند. مثل #define و #pragma و #if و ... به این عبارات preprocessor directive می‌گویند.

 

۲.
در
C/C++ کامپایل هر فایل دو مرحله دارد. در یک مرحله تمام preprocessor directive ها خوانده و جایگذاری می‌شوند (به غیر از آن‌هایی که سروکارشان با خود کامپایلر، یعنی مرحله بعد، است) و در مرحله بعد فایل حاصل به یک Object File کامپایل می‌شود. واحدی که وظیفه مرحله اول را بر عهده دارد preprocessor نام دارد و علت نام‌گذاری آن هم از همین نکته نشئت گرفته: واحدی که عباراتی را قبل از عمل کامپایل اصلی پردازش می‌کند.

 

۳.
برای این‌که منظور از جایگذاری
preprocessor directive ها را متوجه شویم به چند مثال توجه کنید. مثلا در عبارت

#include "somefile.h"

واحد preprocessor کل این خط را حذف می‌کند و محتویات "somefile.h" را به جایش قرار می‌دهد و فایل نهایی حاصل را به کامپایلر می‌فرستد و یا در عبارت

#define MSG "Hello"

preprocessor کلیه توکن‌هایی را که مساوی MSG باشند را با رشته "Hello" جایگزین می‌کند.

 

۴.
برای مشاهده خروجی
preprocessor، تنها و بدون کامپایل source برنامه، هنگام استفاده از gcc از دستور زیر استفاده کنید.

$ g++ -E src.cpp -o src-no-prep.cpp
[you can use gcc instead of g++ too]

و هنگام استفاده از کامپایلر Microsoft Visual C++ از دستور زیر استفاده کنید.

$ cl.exe src.cpp /E > src-no-prep.cpp

 

۵.
برای مثال برنامه کوتاه زیر را در نظر بگیرید:

C++
#include <iostream>
using namespace std;

#define MSG "Hello"

int main()
{
    cout << MSG << endl;
    return 0;
}

حالا فایل‌های خروجی preprocessor را باز کنید. جالب است بدانید که gcc فایل مبدا ۱۰ خطی شما را به یک فایل غول‌پیکر ۲۳۸۵۹ خطی تبدیل کرده. خروجی Visual C++ هم یک فایل ۴۵۰۴۱ خطی است (که البته تعداد زیادی از خط‌های آن خالی است). تنها حدود ۱۰ خط آخر این فایل‌ها را شما نوشته‌اید. این فایل‌ها بدون هیچ مشکلی قابل کامپایل کردن هستند.

 

۶.
هنوز فایل‌های خروجی تعدادی
preprocessor directive دارند که از بین نرفته‌اند. اگر دقت کنید این directive ها همگی یا #line هستند یا #pragma. کامپایلر عبارت #line را برای تولید خطا و راهنمایی برنامه‌نویس لازم دارد. همچنین #pragma هم یک سری option ها را برای کامپایلر مشخص می‌کند.

 

۷.
حالا شاید واضح‌تر شده باشد که چرا ترتیب نوشتن
preprocessor directive ها مهم است.

 

پ.ن.

۱. خیلی جاها به این عبارات به اشتباه preprocessor گفته می‌شود. در حالی که preprocessor یک قسمت از کامپایلر است. نام صحیح‌تر این عبارات preprocessor directive می‌باشد.


چهارشنبه، اردیبهشت ۰۵، ۱۳۸۶

مخالفت با جنگ در برابر مخالفت با کشتار

مخالفین جنگ در آمریکا از کدوم آمار ناراحتند؟ چرا تعداد کم قاتل‌ها با دقت ۱ نفر در خط اول نوشته می‌شه و تعداد کثیر مقتولین بی‌گناه با دقت ۱۰۰۰ نفر با قید also در خط دوم؟

باز دم این یکی گرم که نوشته‌اش خط دوم رو داشت ...

دوشنبه، اردیبهشت ۰۳، ۱۳۸۶

آزادسازی منابع در C#

حافظه در .NET مدیریت شده است. مدیریت شده بدین معنی که اگر شیءای در حافظه باشد و به آن اشاره‌ای نشود، این شیء از دید Framework یک آشغال است و جمع‌آوری (پاک) می‌شود. به این عمل Garbage Collection می‌گویند. اما بسیاری از منابع مدیریت شده نیستند. مثلا برنامه‌نویس باید فایلی را که باز کرده، بعد از استفاده، خود، ببندد. همین طور اتصال به database باید توسط خود برنامه‌نویس قطع شود و ... می‌توان آزادسازی این منابع را در مخرب کلاس انجام داد. در C# مخرب‌ها درست مانند C++ تعریف می‌شوند.

C#
class SomeClass
{
    ~SomeClass()
    {
        // release resources here
    }
    ...
}

این تابع پس از کامپایل شدن تبدیل به تابعی virtual با نام Finalize() می‌شود. در واقع کد بالا پس از کامپایل شدن فرمی پیدا می‌کند که انگار کد زیر نوشته شده:

C#
class SomeClass
{
    virtual void Finalize()
    {
        try
        {
            // release resources here
        }
        finally
        {
            base.Finalize();
            // chaining to the base class
        }
    }
    ...
}

تابع Finalize() یا مخرب را فقط Garbage Collector (GC) می‌تواند صدا بزند نه برنامه‌نویس. همچنین برنامه‌نویس نمی‌تواند تابع Finalize() را خودش تعریف کند. هر گونه شباهت توابع برنامه‌نویس به تابع Finalize() بسته به میزان شباهت موجب ایجاد warning یا error می‌شود. تنها و تنها تابع مخرب به فرم C++ مجاز است.

 

اگر GC شیءای را پاک کند، مخربش را هم صدا می‌زند. اما معلوم نیست چه زمانی Garbage Collector به سراغ شیء می‌رود. اگر به شیء تنها یک reference اشاره کند با مساوی null قرار دادن آن reference، شیء برای جمع‌آوری شدن علامت می‌خورد و به صف آشغال‌ها اضافه می‌شود. حال اگر Garbage Collector را مجبور کنیم تمام آشغال‌ها را پاک کند، شیء ما میان مابقی آشغال‌ها پاک می‌شود. این کار زمان‌بر است چون که به بهانه پاک کردن یک شیء کل آشغال‌ها پاک خواهند شد.

C#
SomeClass someObject = new SomeClass();
// do something with it
...
// when finished set
someObject = null;
// force GC to collect all garbage
GC.Collect();

راه دیگر استفاده از IDisposable است. اگر کلاسی IDisposable را پیاده‌ (implement) کند، می‌تواند از متد Dispose() برای آزاد کردن منابع استفاده کند. این تابع را برنامه‌نویس می‌تواند صریحا صدا بزند. همان گونه که از اسم آن پیداست کلاسی که IDisposable را پیاده کند، disposable یا قابل دورانداختن می‌شود.

C#
public interface IDisposable
{
    void Dispose();
}

در پیاده‌سازی تابع Dispose() باید GC.SuppressFinalize() هم صدا زده شود تا دیگر GC شیء مذکور را برای بار دوم پاک نکند. برای پیاده کردن همچین سناریویی از یک متغیر Boolean که این‌جا اسمش را گذاشتیم disposed و دو نسخه از تابع Dispose() که یکی از آن دو متغیری Boolean به عنوان پارامتر می‌گیرد، به شکل زیر استفاده می‌شود.

C#
public class SomeClass
{
    public SomeClass()
    {
        // initialize resources
        disposed = false;
    }

    ~SomeClass()
    {
        dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if(!disposed)
        {
            if(disposing)
            {
                // dispose managed resources
                // e.g. call their Dispose()
            }
            // now dispose unmanaged resources
            // e.g. close files or database connections
            disposed = true;
        }
    }
    ...
    private bool disposed;
}

متغیر disposed موجب می‌شود که قطعه کد آزادسازی تنها یک بار اجرا شود. اگر این کار توسط فراخوانی تابع Dispose() انجام شود کلیه منابع چه مدیریت شده چه مدیریت نشده آزاد می‌شوند، چرا که دیگر قرار نیست GC به سراغ آن‌ها برود. اما اگر مخرب این کار را انجام دهد فقط منابع مدیریت نشده آزاد می‌شوند، چرا که منابع مدیریت شده قبلا توسط خود GC آزاد شده‌اند.

 

یک راه کوتاه و تمیز برای استفاده و بلافاصله آزاد کردن اشیایی که disposable‌ هستند استفاده از using است.

C#
using (someObject = new SomeClass())
{
    // use someObject
}

کد بالا پس از کامپایل به فرمی تبدیل می‌شود که انگار به شکل زیر نوشته شده بود.

C#
someObject = new SomeClass();
try
{
    // use someObject
}
finally
{
    if(someObject != null)
        someObject.Dispose();
}

مثلا در زیر روش استفاده از using برای باز کردن یک فایل و خواندن از آن نشان داده شده است.

C#
using (StreamReader sr = new StreamReader("file.txt"))
{
    // read the file
}