Friday, September 04, 2009

A little about C++ Standard Template Library (STL)

به نام دوست

استفاده از توابع کتابخانه‌ای STL مزیتهای گوناگونی برای یک برنامه‌نویس و حتی برای کل پروژه به دنبال دارد. استفاده از کدهای موجود که با توجه به موضوع Performance سعی شده تا به خوبی پیاده‌سازی شوند و همچنین Bug Free بودن آنها از جمله موارد مطلوب در استفاده از این کتابخانه‌ها میباشد.

STL شامل مجموعه‌ای از ساختمان داده‌‌های رایج و تعدادی الگوریتمهای مرسوم برای پردازش داده‌های موجود در آنها میباشد. STL بر مبنای قابلیت Template در C++ نوشته شده است.

STLها به ۳ دسته تقسیم میشوند:
۱- Containors: ساختمان داده‌ها برای ذخیره‌سازی اطلاعات
۲- Iterators: ابزار جابه‌جایی بر روی اطلاعات و یا ابزار دسترسی به اطلاعات ساختمان داده‌ها
۳- Algorithms: ابزار پردازش اطلاعات ساختمان داده‌ها. این ابزار برای دسترسی به اطلاعات ساختمان‌داده‌ها از iteratorها استفاده میکنند
ساختمان‌داده‌‌ها نیز به سه دسته اصلی تقسیم میشوند: First Class, adapters , near Containors

۱- ساختمان داده‌ها (Containors)
انواع ساختمان‌داده‌های موجود به این شرح میباشند:

Standard Library container class

Description

Sequence containers

vector

rapid insertions and deletions at back direct access to any element

deque

rapid insertions and deletions at front or back direct access to any element

list

doubly linked list, rapid insertion and deletion anywhere

Associative containers

set

rapid lookup, no duplicates allowed

multiset

rapid lookup, duplicates allowed

map

one-to-one mapping, no duplicates allowed, rapid key-based lookup

multimap

one-to-many mapping, duplicates allowed, rapid key-based lookup

Container adapters

stack

last-in, first-out (LIFO)

queue

first-in, first-out (FIFO)

priority_queue

highest-priority element is always the first element out


نکته: ساختمان داده‌های Sequence Containers و Associative Containers به عنوان ساختمان‌داده‌‌های First Class شناخته میشوند.

هر ساختمان داده مجموعه‌ای از توابع را برای کار با آن در اختیار کاربر قرار میدهد، در این میان توابعی وجود دارند که در بین تمامی ساختمان داده‌ها مشترک میباشند. لیست این توابع به این شرح است:

Common member functions for all STL containers

Description

default constructor

A constructor to provide a default initialization of the container. Nor-mally, each container has several constructors that provide different initialization methods for the container.

copy constructor

A constructor that initializes the container to be a copy of an existing container of the same type.

destructor

Destructor function for cleanup after a container is no longer needed.

empty

Returns true if there are no elements in the container; otherwise, returns false.

size

Returns the number of elements currently in the container.

operator=

Assigns one container to another.

operator<

Returns true if the first container is less than the second container; otherwise, returns false.

operator<=

Returns TRue if the first container is less than or equal to the second container; otherwise, returns false.

operator>

Returns true if the first container is greater than the second con-tainer; otherwise, returns false.

operator>=

Returns true if the first container is greater than or equal to the sec-ond container; otherwise, returns false.

operator==

Returns true if the first container is equal to the second container; otherwise, returns false.

operator!=

Returns TRue if the first container is not equal to the second con-tainer; otherwise, returns false.

swap

Swaps the elements of two containers.

Functions found only in first-class containers

max_size

Returns the maximum number of elements for a container.

begin

The two versions of this function return either an iterator or a const_iterator that refers to the first element of the container.

end

The two versions of this function return either an iterator or a const_iterator that refers to the next position after the end of the container.

rbegin

The two versions of this function return either a reverse_iterator or a const_reverse_iterator that refers to the last element of the container.

rend

The two versions of this function return either a reverse_iterator or a const_reverse_iterator that refers to the next position after the last element of the reversed container.

erase

Erases one or more elements from the container.

clear

Erases all elements from the container.

Note: Overloaded operators operator<, operator<=, operator>, operator>=, operator== and operator!= are not provided for priority_queues.


برای Sequence Containers فانکشنهای مشترک دیگری به این شرح وجود دارد:
front: ارجاع به اولین عنصر Container را برمیگرداند
back: ارجاع به آخرین عنصر
push_back: درج یک عنصر در انتهای Container
pop_back: حذف یک عنصر از انتهای Container

در Containersها تعدادی typedef جهت راحتی(و یکسان سازی) کار با ساختمان‌داده‌ تعریف شده است. لیست این typedef ها به این شرح است:

typedef

Description

value_type

The type of element stored in the container.

reference

A reference to the type of element stored in the container.

const_reference

A constant reference to the type of element stored in the container. Such a reference can be used only for reading elements in the container and for performing const operations.

pointer

A pointer to the type of element stored in the container.

iterator

An iterator that points to the type of element stored in the container.

const_iterator

A constant iterator that points to the type of element stored in the container and can be used only to read elements.

reverse_iterator

A reverse iterator that points to the type of element stored in the container. This type of iterator is for iterating through a container in reverse.

const_reverse_iterator

A constant reverse iterator that points to the type of element stored in the container and can be used only to read elements. This type of iterator is for iterating through a container in reverse.

difference_type

The type of the result of subtracting two iterators that refer to the same container (operator - is not defined for iterators of lists and associative containers).

size_type

The type used to count items in a container and index through a sequence container (cannot index through a list).


نکته: هنگامی که یک عنصر به داخل ساختمان‌داده‌ insert میشود یک کپی از ان ایجاد میشود. بنابراین Type عناصر میبایست حداقل عملیات کپی(Copy Constructor) و عملیات انتساب(Assign Operator) را پشتیبانی کند. موارد ذکر شده حداقل قابلیتهای یک عنصر میباشد. باید توجه داشت که هر تایپ میبایست حداقل عملیات مورد نیاز در Containers مورد نظر که از یک ساختمان داده به دیگری متقاوت میباشد را پیاده‌سازی کند.

۲- تکرارکننده‌ها (Iterators)
تکرارکننده‌ها به پنج دسته تقسیم میشوند:

Category

Description

input

Used to read an element from a container. An input iterator can move only in the forward direction (i.e., from the beginning of the container to the end) one element at a time. Input iterators support only one-pass algorithms-the same input iterator cannot be used to pass through a sequence twice.

output

Used to write an element to a container. An output iterator can move only in the forward direction one element at a time. Output iterators support only one-pass algorithms-the same output iterator cannot be used to pass through a sequence twice.

forward

Combines the capabilities of input and output iterators and retains their position in the container (as state information).

bidirectional

Combines the capabilities of a forward iterator with the ability to move in the backward direction (i.e., from the end of the container toward the beginning). Bidirectional iterators support multipass algorithms.

random access

Combines the capabilities of a bidirectional iterator with the ability to directly access any element of the container, i.e., to jump forward or backward by an arbitrary number of elements.





قابل توجه است که الگوریتمها برای کار بر روی ساختمان‌داده‌‌ها نیاز به iteratorهای خاصی دارند و از طرف دیگر ساختمان‌داده‌‌ها نیز نوع خاصی از Iteratorها را پشتیبانی میکنند.
بنابراین شرط استفاده از یک الگوریتم بر روی یک ساختمان‌داده‌ پشتیبانی کردن از Iterator مورد نیاز آن الگوریتم توسط ساختمان‌داده‌ مورد نظر میباشد.
نکته دیگر اینکه هنگامی که یک ساختمان‌داده‌ از یک نوع Iterator پشتیبانی میکند، قابلیت کار با ساختمان‌داده‌ با استفاده از Iteratorهای با قدرت کمتر از Iterator پشتیبانی شده وجود دارد.

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

Container

Type of iterator supported

Sequence containers (first class)

vector

random access

deque

random access

list

bidirectional

Associative containers (first class)

set

bidirectional

multiset

bidirectional

map

bidirectional

multimap

bidirectional

Container adapters

stack

no iterators supported

queue

no iterators supported

priority_queue

no iterators supported



جدول زیر عملگرهای تکرارکننده‌ها را نمایش میدهند. قابل توجه است که در این جدول هر تکرارکننده‌ عملگرهای تکرارکننده‌ قبل خود را نیز شامل میشود.

Iterator operation

Description

All iterators

++p

Preincrement an iterator.

p++

Postincrement an iterator.

Input iterators


*p

Dereference an iterator.

p = p1

Assign one iterator to another.

p == p1

Compare iterators for equality.

p != p1

Compare iterators for inequality.

Output iterators

*p

Dereference an iterator.

p = p1

Assign one iterator to another.

Forward iterators

Forward iterators provide all the functionality of both input iterators and output iterators.

Bidirectional iterators

--p

Predecrement an iterator.

p--

Postdecrement an iterator.

Random-access iterators

p += i

Increment the iterator p by i positions.

p -= i

Decrement the iterator p by i positions.

p + i

Expression value is an iterator positioned at p incremented by i positions.

p - i

Expression value is an iterator positioned at p decremented by i positions.

p[ i ]

Return a reference to the element offset from p by i positions

p <>

Return true if iterator p is less than iterator p1 (i.e., iterator p is before iterator p1 in the container); otherwise, return false.

p <= p1

Return TRue if iterator p is less than or equal to iterator p1 (i.e., iterator p is before iterator p1 or at the same location as iterator p1 in the container); otherwise, return false.

p > p1

Return TRue if iterator p is greater than iterator p1 (i.e., iterator p is after iterator p1 in the container); otherwise, return false.

p >= p1

Return TRue if iterator p is greater than or equal to iterator p1 (i.e., iterator p is after iterator p1 or at the same location as iterator p1 in the container); otherwise, return false.


موفق باشید.

Thursday, August 13, 2009

Make A FreeDos Bootable Flash

به نام دوست
ساخت یک فلش قابل بوت برای کارهایی مانند دستکاری بایوس سیستم و مواردی از این قبیل بسیار مفید هست. در سایت FreeDos راهنمای انجام این موضوع وجود داره اما خوب نقایص کوچیکی هم داره که شاید فرآیند انجام این کار رو کمی طولانی میکنه.
از جمله مواردی که من از این فلش قابل بوت استفاده کردم دستکاری بایوس سیستم و فعال کردن قابلت مجازی سازی بر روی سیستم خودم بود که به صورت پیش‌فرض توسط سازنده غیرفعال شده بود.

۱- چند دستور مناسب برای دسترسی به اطلاعات بایوس
دو دستور خیلی مفید برای دسترسی به اطلاعات بایوس سیستم در لینوکس قابل ذکر هستند که عبارتند از biosdecode و dmidecode. شما با استفاده از این دستورات میتوانید به اطلاعات دوایسهای موجود در سیستم دسترسی پیدا کنید. دستیابی به اطلاعات سطح پایین در مورد سخت افزار سیستم ممکن است در خیلی از موارد بسیار راهگشا باشه.
دسترسی به شماره ویرایش بایوس هم یکی از مزایای استفاده از این دستورات است.

۲- ساخت یک فلش قابل بوت (Bootable Flash)
برای ساخت نیاز به یک سری فایل دارید که میتونید این فایلها را( که ما به آنها با نام FreeDos_Bootable_Usb_Files رجوع خواهیم کرد) از اینجا دانلود کنید. قبل از هر کاری بسته‌های نرم‌افزاری makebootbat و syslinux را نصب کنید.
توجه ۱: توجه داشته باشید دستوراتی که در اینجا آمده‌اند کلیه پارتیشنهای موجود بر روی فلش را پاک خواهند کرد.
توجه ۲: همچنین آدرس استفاده شده برای فلش در اینجا به صورت نمونه /dev/sdb است که شما باید از آدرس درست استفاده کنید.

مراحل کار به این شرح است:
مرحله اول: ساخت جدول پارتیشنها و یک پارتیشن برای فایلهای مورد نیاز:
parted /dev/sdb mklabel msdos
parted /dev/sdb mkpart primary fat16 0 64MB

مرحله دوم: make bootable
makebootfat -o /dev/sdb -E 255 -1 /path/to/FreeDos_Bootable_Usb_Files/fat12.bss -2 /path/to/FreeDos_Bootable_Usb_Files/fat16.bin -3 /path/to/FreeDos_Bootable_Usb_Files/fat32lba.bin -m /usr/share/syslinux/mbr.bin /path/to/FreeDos_Bootable_Usb_Files/

مرحله سوم: فراهم کردن محیط عملیاتی
فلش موجود میتواند برای بوت کردن سیستم مورد استفاده قرار گیرد. اما برای داشتن یک محیط عملیاتی مناسب (خط فرمان و دستورات کمکی) باید فایلهای دانلورد شده را داخل آن کپی کنید:
mount /dev/sdb1 /mnt
cp /path/toFreeDos_Bootable_Usb_Files/* /mnt/


حالا میتونید سیستم رو بوت کنید و متعاقبا لذت وافر رو ببرید.
موفق باشید

Tuesday, July 14, 2009

Art of partitioning #2

به نام دوست

در مطلب قبلی در مورد شیوه پارتیشن بندی سیستم و برخی قابلیتهای LVM به عنوان یک روش مناسب پارتیشن بندی به جهت رویارویی با نیازهای آینده، و روش راه اندازی آن مطالبی عنوان گردید. وجود امکانات متعدد برای کار با پارتیشنها چاشنی این مطلب جدید است.

1- افزایش فضای فایل سیستم از نوع ext3
با فرض وجود فضای خالی در انتهای پارتیشن مورد نظر ما، انچام این کار بسیار راحت است. البته گرفتن نسخه پشتیبان از اطلاعات پیش شرط هر عملیات بر روی پارتیشنهاست. کاری که باید انجام داد به این شرح است (فرض کنید پارتیشن مورد نظر ما /dev/sda1 باشد:
1)قبل از هر کاری فایل سیستمتان را چک کنید: fsck /dev/sda1
2) جورنالینگ را حذف کنید:
tune2fs -O ^has_journal /dev/sda1
3) به fdisk وارد شوید
4) پارتیشن /dev/sda1 را حذف کنید
5) یک پارتیشن جدید بسازید: با همان شماره پارتیشن قبلی و به میزان کل فضای خالی موجود
6) تغییرات را ذخیره کنید.
تا اینجا اگر اقدام به mount کردن پارتیشن نمایید تمام اطلاعات قبلی شما ظاهر خواهد شد، البته بدون افزایش اندازه در فایل سیستم. به عبارتی شما تا این مرحله توانسته اید فضای پارتیشن خود را افزایش دهید اما اندازه فایل سیستم نیاز به افزایش دارد. برای این منظور کارها را به این طریق ادامه دهید:
7) مجددا فایل سیستم را چک کنید البته به این طریق:
fsck -f /dev/sda1
10) میزان فضای فایل سیستم را افزایش دهید:
resize2fs /dev/sda1
9) جورنالینگ را فعال کنید
tune2fs -O has_journal /dev/sda1
10) مونت کرده و لذت ببرید

2- در مورد ntfs
بعد از افزایش فضای پارتیشن برای فایل سیستمهای ntfs میتوانید از ntfsresize که جزو بسته نرم افزاری ntfsprogs است استفاده کنید.

منبع: ----
موفق باشید

Tuesday, June 23, 2009

C++ Function Pointer

به نام دوست

به نظر من یکی از زیباترین قابلیتهای زبان C در کنار قابلیتهای گوناگون دیگر که نشان از ذوق و استعداد طراحان این زبان دارد اشاره‌گر به فانکشن است. شما میتوانید با استفاده از این قابلیت تابعی را که از محتوای پیاده‌سازی ان آگاه نیستید فقط با داشتن اشاره‌گر به آن و همچنین چینش و نوع پارامترهای آن و مقادار برگشتی تابع صدا بزنید.
به مثال زیر توجه کنید:
#include <iostream>
using std::cout;

class Test {
  public:

  int _fire(int x){

          cout << x << "\n";
          return 0;
  }

  int (Test::*fire)(int);

  void  fireMe()
  {
          (this->*fire)(23);
  }

  Test()
  {
          fire = &Test::_fire;
  }
};

int main()
{

  Test t;
  (t.*t.fire)(23);
}
عبارتی که بعد از .* و یا *<- قرار میگیرد، حاوی یک آدرس است. به عبارت دیگر عبارتی است که حاصل آن یک آدرس به فضای آدرس کلاس مورد نظر میباشد. و خلاصه کلام اینکه آنچه بعد از این عملگرهای می‌آید عنصر عضو کلاس نیست بلکه آدرس یک عضو کلاس میباشد.
 مثال زیر شیوه استفاده از آرایه‌ای از اشاره‌گرها به توابع را نشان میدهد:
#include <iostream>
#include <string>

using std::string;
class Test
{
public:
        typedef  string(Test::*action)(void *);

        string _test(void *) {
                std::cout << "test\n";

                return "";
        }
        Test() ;
        action action_list[2];
};

Test::Test() {
        action_list[0] = &Test::_test;
        (this->*action_list[0])(NULL);
}

main ()
{
        Test t; 
        (t.*t.action_list[0])(NULL);
}
اشاره‌گرها به توابع ابزار مناسبی جهت افزایش انعطافپذیری برنامه‌ها میباشند.

Wednesday, February 25, 2009

LVM: Art Of Partitioning

به نام دوست

بیشک پارتیشن‌بندی هارددیسکت به هنگام نصب لینوکس یک دغدغه جدی برای استفاده کننده سیستم به حساب می‌آید. این موضوع جدی‌تر میشود هنگامی که سیستم به عنوان یک سرور ایفای نقش میکند. مباحثی همچون تعداد پارتیشنها، Mount Point مناسب، و از همه مهمتر اندازه مناسب با توجه به نیاز آینده ممکن است شخص را گیج و منگ کند. بالاخره از روز اول که همه چیز مشخص نیست. چه باید کرد؟
در این مقاله کوتاه چند توصیه ساده اما کارا مطرح شده است. همچنین با توجه به جایگاه lvm به عنوان یک ابزار مهم در پارتیشن بندی مناسب سیستم، تعاریف اولیه و نیز طرز استفاده ازآن به طور خلاصه بیان شده است.

چند توصیه اولیه،
در هر پارتیشن بندی معمولا اصولی وجود دارد که رعایت کردن آن خالی از لطف نیست:
۱- میزان فضای مناسب با توجه به حجم برنامه‌های نصب شده بر روی سیستم برای /‌ یا همان root در نظر بگیرید.
۲- فضای boot را نیز به عنوان یک پارتیشن مجزا در نظر بگیرید.
۳- به اندازه دوبرابر فضای RAM یک پارتیشن به عنوان SWAP بسازید
۴- اگر سرویسها و یا دیتاهای خیلی مهمی دارید حتما فضای مجزایی را برای آنها در نظر بگیرید. انتخاب اینکه کدام دیتاها با هم باشند میتواند به میزان ارتباط ذاتی آنها به همدیگر مربوط باشد.
۵- اگر کاربر خانگی هستید سعی کنید پارتیشن مجزایی را برای Home در نظر بگیرید. با فرض داشتن چند لینوکس بر روی سیستم، Home تمام آنها را همین پارتیشن قرار دهید. بنابراین فضای کاری شما به عنوان یک کاربر در تمام لینوکسها ثابت خواهد بود.

نکته ۱-
موارد ۴ و ۵ یک مزیت ذاتی دارند و آن اینکه در صورتیکه حتی نیاز داشتید لینوکس خود را مجددا نصب کنید؛ بدان معنی که پارتیشن ریشه خود را به کل فرمت کرده و مجددا بر روی آن لینوکس نصب کنید، تمامی اطلاعات شما و همچنین فضای کاری و تنظیمات شخصی شما ثابت خواهند بود.
نکته ۲- در مورد ۵ برای کارآیی مناسب باید توجه داشته باشید که user ID یک کاربر مورد نظر در تمام لینوکسها باید یکسان باشد.


مصیبت عوض شدن ترتیب هاردها،
شاید تا کنون با این موضوع روبرو شده باشید که با تغییر تنظیمات Bios و یا با تغییر مکان اتصال هارد به مادربورد و یا با اضافه کردن یک هارد جدید به سیستم، لینوکس شما بالا نیامده و یا اینکه پارتیشنهایی را که در فایل /etc/fstab مشخص کرده اید به درستی mount نشده باشند.
مشکل از آنجا ناشی میشود که با تعویض ترتیب هاردها نام device مربوطه عوض میشود. به عنوان مثال دوایس /dev/sdb مربوط به هارد دوم شما میباشد. حال اگر بنا به حادثه ای این هارد Master شود نام آن /dev/sda خواهد شد و از آنجا که در اکثر اوقات دوایس مروبط به هارد با مسیر کامل دوایس مشخص میشود طبیعی ترین اتفاق آن است که با تعویض مکان هاردها، سیستم قادر به مونت کردن پارتیشن مورد نظر نباشد.
خوشبختانه در لینوکس برای رفع این مشکل دو راه حل وجود دارد:
1- میتوان به جای استفاده از مسیر کامل دوایس مربوط به پارتیشن از نام label (برچسب) مربوط به پارتیشن استفاده کرد. در اینصورت با تعویض دوایس پارتیشن، نظر به ثابت بودن برچسب، سیستم پارتیشن مورد نظر را به درستی مونت خواهد کرد. برای آگاهی از نحوه برچسب گذاری به این مقاله مراجعه کنید.
یک خط نمونه از /etc/fstab که برچسب استفاده کرده است:
LABEL=/ / ext3 defaults 1 1

2- استفاده از uuid مربوط به پارتیشن مورد نظر است. uuid که مخفف Universal Unique Identifier میباشد یک شماره یکتا برای پارتیشن مرود نظر است. با توجه به اینکه این شماره یکتا با تغییر دوایس پارتیشن تغییر نمیکند حاصل آن خواهد بود که پارتیشن به درستی مونت خواهد شد. یک خط نمونه در /etc/fstab که از uuid استفاده میکند:
UUID=ec255e40-8008-40fa-b254-cac1027df18e /boot ext3 defaul

برای بدست آوردن uuid مربوط به پارتیشنها این دستور را اجرا کنید: ll /dev/disk/by-uuid

یک نکته تکمیلی،
در اکثر اوقات، در حال کاربا سیستم هستید و و بعد از فارغ شدن از fdisk و به هنگام سازی پارتیشنها، احتمالا متوجه شده اید که پارتیشنهای تازه ایجاد شده در لیست دوایسها دیده نمیشوند. این موضوع بدان خاطر است که کرنل از وجود آنها بی اطلاع است. برای رفع این موضوع میتوانید با اجرای دستور partprobe اجرا و لذت وافر ببرید.

و اما چه کنیم با کمبود حافظه،
خوب تا اینجا شاید توانسته باشیم تعداد پارتیشنها و همچنین Mount Point‌ آنها را حدس بزنیم. اما چه کنیم برای زمانی که اندازه پارتیشن ما جوابگوی حجم داده‌های ما نباشد. به چه صورت میتوانیم اندازه پارتیشن خود را اضافه کنیم؟. به چه صورت میتوانم از یک هارد جدید برای افزایش فضای پارتیشنهای هارد موجود استفاده کنم؟ و یا شاید به صورت کلی تر باید از خود بپرسیم که چه راهکاری را پیش بگیریم که انجام این امور در کمترین زمان و با صرف کمترین خطر قابل انجام باشد؟ اینجاست که lvm به نچات ما می آید...

LVM چیست؟
Logical Volume Manager، در یک کلام ابزار حیرت انگیزی است. تاکنون تنها نگاه ما به یک هارد‌دیسک و یا هر ابزار ذخیره سازی، بلوکی متشکل از مجموعه‌ای از پارتیشنها بوده است. اما LVM منطق و دیدگاه تازه‌ای ارایه میدهد.
ساختار ذخیره‌سازی در LVM شامل سه لایه میباشد:
۱- در پایین ترین سطح Physical Volum قرار دارد. PV یک دیسک یا یک پارتیشن از دیسک است که میتواند برای ذخیره سازی اطلاعات استفاده گردد.
۲- Volume Group شامل مجموعه ای از PV ها میباشد که جمعا یک واحد ذخیره سازی اطلاعات را تعریف میکنند. چنانکه متوجه شده اید یک VG مجموعه ای از PV ها را شامل میشود که قابلیت اضافه شدن و حذف شدن از VG را دارند. وجود VG راه حلی برای رهایی از محدودیت حاصل از میزان فضای پارتیشنها و یا هارد دیسک میباشد، به صورتیکه شما میتوانید به مرور زمان با اضافه شدن دیسک جدید آن را به فضای VG خود اضافه کنید.
۳- Logical Volume از دید کاربری یک پارتیشن است. به عبارتی واحدی است که file system بر روی آن نصب میشود و به عنوان یک واحد ذخیره سازی اطلاعات قابلیت مونت شدن به مکان دلخواه ما را دارد.
lvm شما را از محدود شدن در حضار اندازه فیزیکی آزاد میکند. سناریوی کار هم معمولا به این شکل است که شما ابتدا اقدام به تعریف چند PV (همانطور که گفته شد یک PV میتواند یک پارتیشن دلخواه بر روی هارد شما و یا یک دسک کامل باشد) میکنید. بعد از آن میتوانید با توجه به منطق داده های خود ایشان در قالب VG گروه بندی کنید. بعد از ایجاد یک VG نوبت به ساخت LVها و یا به عبارتی پارتیشنها میرسد.
در صورتیکه فضای LVشما بعد از مدتی جوابگوی حجم دیتاهای شما نباشد و همچنین فضای خالی در VG در اختیار شما نباشد میتوانید با اضافه کردن یک دیسک جدید (یا یک پارتیشن جدید از هر یک از دیسکهای خودتان) به VG در قالب یک PV ابتدا فضای آزاد لازم را فراهم کنید. سپس اندازه LV خود را به میزان دلخواه افزایش داده و بعد از آن با استفاده از ابزار filesystem مورد استفاده خود فضای داده ای خود را افزایش دهید. این راه امنترین راه در برخورد با کاهش فضای موجود میباشد.

طریقه کار با lvm
1- ابتدا با استفاده از fdisk و یا هر ابزار دیگری پارتیشنهای مورد نظر خود را بسازید.
2- دستور partprobe را اجرا کنید تا پارتیشنهای جدید لود شوند.
3- با استفاده از دستور pvcreate ابتدا PV های خود را آماده کنید. به عنوان مثال
#pvcreate /dev/sda1
این دستور پارتیشن 1 از هارد a را به یک PV تبدیل خواهد کرد.
4- سومین مرحله ساخت VG است که مجموعه ای از چند PV خواهد بود. برای این موضوع باید از دستور vgcreate استفاده کنید.
#vgcreate My-VG-Name /dev/sda1
این دستور یک VG با نام My-VG-Name خواهد ساخت که تنها شامل یک PV (که همان /dev/sda1 است) خواهد بود. در لیست بالا هر چندتا PV که دارید میتوانید لیست کنید.
5- آخرین مرحله ساخت LV میباشد. LV واحد قابل مونت خواهد بود. برای این کار از دستور lvcreate استفاده میکنیم:
#lvcreate -l 50%VG -n My-Data My-VG-Name
این دستور یک LV با نام My-Data به میزان 50 درصد حجم My-VG-Name خواهد ساخت.
6- دستور partprobe را مجددا اجرا کنید.
7- فایل سیستم مورد نظر خود را بسازید.
#mkfs.ext3 /dev/My-VG-Name/My-Data
این دستور فایل سیستم ext3 را بر روی My-Data بارگذاری خواهد کرد.
8- حال نوبت لذت بردن است. دوایس را مونت کنید و لذت ببرید..
#mount /dev/My-VG-Name/My-Data /mnt

برای آگاهی از دیگر امکانات lvm به منوال این دستور مراجعه کنید.

التماس دعا...

Sunday, January 25, 2009

Art of structure definition

به نام دوست

در حالت عادی تعریف یک ساختار شامل تعریف و چینش عناصر آن میباشد. در این میان توجه به مشخصات سخت افزار و رفتار کامپایلر نقش به سزایی در قابل حمل بودن برنامه دارد. این موضوع مخصوصا در تقابل سیستمهای 32 بیتی و 64 بیتی ظهور بیشتری دارد. در صورت استفاده از قابلیتهای مختص GNU C مانند استفاده از آرایه های با طول صفر که معمولا برای دستیابی به انتهای حافظه در یک ساختار مورد استفاده قرار میگیرند، این موضوع پررنگتر میشود.
در این نوشته منظور از کامپایلر، کامپایلر gcc میباشد.

1- توجه به اندازه تایپها
تایپها در سخت افزارهای گوناگون اندازه های متفاوت دارند. بنابراین ساختار ما در سیستمها گوناگون اندازه های متفاوتی خواهد داشت. توجه به این نکته مخصوصا در ساختارهای که به جایگاه قرارگیری عناصرشان در حافظه حساس میباشند بسیار مهم است. در صورتیکه نیازمند استفاده از تایپهایی میباشید که میبایست در ساختارهای گوناگون اندازه یسکانی داشته باشند میبتوانید از تایپهای موجود در فایل asm/types.h و همچنین linux/types.h استفاده کنید.
تایپهای u8 (به معنای unsigned int with 8 bit length)و u16 و u32 و u64 تایپهایی با طول ثابت در تمام سخت افزارها برای استفاده در سطح کرنل میباشند. جهت استفاده در سطح کاربر میبایست از تایپهای u32__ و ... استفاده کرد. همچنین تایپهای u_int8_t و u_int16_t و ... تایپهایی از این تیپ میباشند که برای هر دو سطح کاربر و کرنل قابل استفاده میباشند.
نکته مهم دیگر اندازه اشاره گرها در هر سخت افزار است. درسیستمهای 32 بیتی اشاره گرها با فضای 4 بایتی و در سیستمهای 64 بیتی با فضای 8 بایتی نمایش داده میشوند. در صورتیکه ساختار ما دارای اشاره گر باشد، اندازه آن در سیستمهای 32 و 64 بیتی متفاوت خواهد بود. باید در تعریف عناصر ترتیب به گونه ای در نظر گرفته شود که موضوع Alignment (قسمت بعد) به خوبی رعایت شود و همچنین چنانچه لازم باشد تا مقدار یک اشاره گر در یک متغیر از نوع صحیح ذخیره شود تایپ پیشنهادی unsigned long میباشد. البته در استاندارد C99 تایپ uintptr_t برای این منظور در نظر گرفته شده که در فایل linux/types.h قرار دارد.

2- چینش عناصر: Alignment
مطلب مهم دیگری که باید به آن توجه داشت بحث Alignment میباشد. سخت افزارها معمولا انتظار دارند تا عناصر نسبت به طولشان به صورت درستی Align شده باشند. Align به زبان ساده بیانگر قرارگیری عناصر در آدرسی با ضریب مشخص نسبت به طولشان میباشد. به عنوان مثال در یک سیستم 32 بیتی که به Align حساس است، متغیر به طول 4 (u32) میبایست در آدرسی با ضریب چهار قرار گیرد. نکته مهم از منظر تفاوت در Align برای متغیرهای 64 بیتی میباشد که Align آن در سیستمهای 32 بیتی 4 و در سیستمهای 64 بیتی 8 میباشد.
کامپایلر (منظور gcc است) به صورت خودکار سعی میکند چینش عناصر ساختار را در حافظه مطابق خواست سیستم انجام دهد. چنانچه در هنگام تعریف عناصر بحث چینش به خوبی لحاظ نشود، احتمال بروز مکانهای بلااستفاده در ساختار و همچنین فاصله بایتی بین عناصر ساختار وجود خواهد داشت. به عنوان یک مثال ساختار زیر را در نظر بگیرید:

struct test_align {
u_int8_t e1;
u_int16_t e2;
};


در حالت معمولی اندازه این ساختار باید 3 بایت باشد اما به دلیل بحث Align و لزوم قرارگیری e2 در آدرسی با ضریب 2، یک بایت فضای خالی مابین e1 و e2 قرار گرفته و اندازه ساختار برابر با 4 خواهد شد.

3- اندازه ساختار
توجه به اندازه نهایی ساختار و همچنین جایگاه عناصر یک ساختار در بحث Align پررنگ شد. پارامتر دیگری نیز بر روی اندازه ساختار تاثیر دارد که به خصوصیات سخت افزار برمیگردد. معمولا اندازه ها در دنیای رایانه توان 2 هستند که این موضوع باعث افزایش کارآیی پردازنده در مراجعات به حافظه میشود.
در هنگام کامپایل برنامه، در صورتیکه این موضوع توسط برنامه نویس رعایت نشده باشد، بایتهای بلااستفاده در انتهای ساختار اضافه میشوند. در صورت عدم اگاهی از این موضوع، چنانچه دسترسی به انتهای ساختار در حافظه مهم باشد، دردسر ساز خواهد شد.
در سیستمهای 32 بیتی اندازه یا میتواند 8 و یا 16 بیتی باشد و برای بیشتر از آن باید ضریب 32 بیت باشد. همچنین در سیستمهای 64 بیتی این موضوع صحت داشته مگر اینکه اگر ساختار از 32 بیت بیشتر باشد اندازه آن باید ضریب 64بیت گردد.
البته این موضوع ذاتا به دلیل رعایت کردن همان بحث Alignment است که رخ میدهد.

4- یک مثال
یک مثال ساده برای نمایش تقابل سیستمهای 32(i386) بیتی و 64(x86_64) بیتی:

struct example {
u_int64_t e1;
u_int32_t e2;
char
end[0];
};


int
main() {
struct
example *e = malloc(2 * sizeof(struct example));

e[1].e1 = 10;
printf("%d\n", ((struct example *)e->end)->e1);

return
0;
}


این برنامه درسیستم 32 بیتی مشکلی نخواهد داشت. اما خروجی در محیط 64 بیتی غیر قابل پیش بینی خواهد بود (به علت موضوع سوم).

4- کلام آخر
مطالب بیان شده موضوعات مورد توجه در هنگام برنامه نویسی برای یک نرم افزار قابل حمل میباشد. ممکن است که قوانین بالا برای سخت افزار مشخصی متفاوت باشند اما موضوع اصلی توجه و بررسی به این پارامترها برای سخت افزارهای هدف میباشد.

منابع استفاده شده:
http://www.ibm.com/developerworks/library/l-port64.html
کتاب LDD: http://lwn.net/Kernel/LDD3/ فصل 11 این کتاب اندازه تایپها در سخت افزارهای گوناگون را بیان کرده که مفید میباشد.
http://sourceforge.net/projects/iptablestng/

Thursday, January 01, 2009

Locks in kernel ...

به نام دوست

مدیریت مناطق بحرانی در هسته لینوکس با توجه به قابل اجرا بودن لینوکس بر روی سیستمهای چند پردازنده ای و همچنین قابلیت Preemption جزء لاینفک برنامه نویسی هسته میباشد. اتخاذ مکانیزم مناسب با توجه به طبیعت برنامه امری بسیار مهم است که در صورت رعایت نشدن آن موجبات crashهای نامنظم سیستم را فراهم میکند. در مقاله What is RCU یک روش ایجاد همزمانی شرح داده شد. استفاده از لاکهای از دیگر روشهای مرسوم در ایجاد همزمانی میباشند با سابقه‌ای طولانی‌تر از rcu، که در این مقاله به صورت مختصر با آنها آشنا میشویم.

۱- روندهای اجرایی
در هسته لینوکس سه دسته روندهای اجرایی از نظر اولویت و تاثیر بر یکدیگر وجود دارند:
  1. IRQ Handler
  2. Soft IRQ, Tasklet, Timer
  3. User Context
دسته بندی بدین معناست که در صورت اجرای روند با شماره بیشتر بر روی یک cpu، امکان گرفتن cpu از آن و واگذاری آن به روند اجرایی با شماره کمتر وجود دارد. به عبارتی اولویتها از بالا به پایین کاهش می یابد.
در مورد دسته بندی ۲، اجرای یک نمونه از هر نوع بر روی یک cpu مانع اجرای دیگری از همان دسته بر روی آن cpu میشود.
با توجه به اینکه ساختمان داده مشترک ما (داده های بحرانی) توسط چه دسته‌ای از روندها مورد تاثیر قرار میگیرد مکانیزم مناسب باید اتخاذ گردد. توجه به طبیعت اجرای هر کدام از روندها موجب درک بهتر مکانیزم مورد استفاده خواهد بود.

۲- آشنایی اولیه با روندها
هسته یک برنامه بزرگ است که اجرای کد از مجاری مختلف و در قالب روندهای زیر صورت می‌پذیرد:
IRQ Handler: در اصل قطعه کدی است که در پاسخ به یک وقفه سخت افزاری اجرا میشود. این موضوع مورد توجه است که همیشه یک نمونه از یک نوع وقفه در حال اجرا خواهد بود. به عبارتی در صورتیکه ساختمان داده ما توسط یک نوع وقفه مورد دسترسی قرار میگیرد جای هیچ گونه نگرانی برای ایجاد همزمانی وجود ندارد.
SoftIRQ: وقفه نرم‌افزاری، معمولا قطعه کدی هست که کامل کننده کار یک وقفه سخت افزاری میباشد. به عبارتی این نوع وقفه‌ها برای کوتاه‌تر شدن مدت زمان اجرای وقفه‌های سخت‌افزاری ایجاد شده‌اند. در مورد شیوه اجرای این روندها باید گفت که همیشه امکان اجرای چندین نسخه از یک نمونه وقفه نرم‌افزاری وجود دارد. بنابراین حتی اگر یک نوع وقفه نرم‌افزاری به داده‌ها دسترسی دارد بیشک میبایست متدهای همزمانی مناسب مورد توجه قرار گیرند.
Tasklet: این نوع روندها در اصل نوعی از وقفه‌های نرم‌افزاری میباشند. تفاوت در این است که این نوع روندها در زمان اجرا قابل تعریف میباشند بر خلاف وقفه‌های نرم‌افزاری که به صورت استاتیک در کد کرنل تعریف شده‌اند. همچنین این تضمین وجود دارد که تنها یک نمونه از یک نوع Tasklet در یک زمان در حال اجرا باشد.
Timer: این نوع روندها نیز نوعی وقفه نرم افزاری هستند که به صورت دابنامیک قابل تعریف هستند (مانند Taskletها) و میبایست در موعد مشخصی(از نظر زمانی) اجرا شوند. این روندها در قالب وقعه نرم افزاری TIMER_SOFTIRQ اجرا میشوند.
UserContext: در مواردی همچون اجرای system call از پراسسهای سطح کاربر، با توجه به اینکه کد system call در سطح هسته اجرا میشود، اصطلاحا این روند اجرایی که حاصل سوییچ کردن از مود کاربر به مود هسته است را UserContext مینامند.
نکته مهم در مورد چهار روند اول آن است که با توجه به اینکه این روندها اجرای اینتراپتها را غیرفعال میکنند به هیچ عنوان نباید به حالت wait بروند. در نتیجه متد لاکی که برای این روندهای میبایست مورد استفاده قرار گیرد به هیچ عنوان نباید به حالت wait رود حتی در صورتیکه لاک مورد نظر در دسترس نباشد.

۳- انواع لاکهای
مکانیزمهای اصلی لاک در هسته لینوکس به این شرح هستند:
  1. spinlock (فایل include/asm/spinlock.h): مکانیزم spinlock اصطلاحا busy wait بوده و نمونه یک مکانیزم سریع و سبک میباشد. در این نوع قفل تنها یک پردازش در یک زمان میتواند لاک را تصاحب کند و همچنین پردازشهای منتظر به هیچ عنوان به خواب نمیروند، بنابراین این نوع لاک برای روندهای گروه ۱ و ۲ مناسب میباشد.
  2. semaphore (فایل include/asm/semaphore.h): در این مکانیزم بسته به مقدار اولیه سمافر،‌ تعداد پردازشهایی که میتوانند در یک زمان لاک را تصاحب کنند قابل تنظیم است. البته در اکثر اوقات به صورت حداکثر یک تصاحب کننده در یک زمان(مانند spin) مورد استفاده قرار میگرید که این لاکها اصطلاحا با نام mutex (فایل include/linux/mutex.h)شناخته میشوند. پردازشهایی که نتوانسته‌اند قفل را تصاحب کنند به حالت wait خواهند رفت. استفاده از این لاک برای گروههای ۱ و ۲ به هیچ عنوان صحیح نیست. این لاک مناسب برای استفاده توسط گروه ۳ میباشد.
۴- spinlock
این متد لاک Busy wait هست: بدان معنی که cpu آنقدر چرخ میزند تا لاک را بدست آورد. به عبارتی پراسسی که سعی در بدست آوردن لاک دارد به حالت wait نمیرود.
نکات مورد توجه در استفاده از این لاک:
  1. قسمت بحرانی به هیچ عنوان نباید به حالت wait برود.
  2. قسمت بحرانی در حد امکان باید کوتاه باشد. (نظر به اینکه cpu را مطلقا در اختیار میگیرد)
فرم ساده استفاده از این لاک به این صورت میباشد:
spinlock_t lock = SPIN_LOCK_UNLOCKED;

spin_lock(&lock);

//critical section

spin_unlock(&lock);

بسته به اینکه چه روندهایی به ساختمان داده ما دسترسی دارند میبایست روش مناسبی برای بدست آوردن لاک استفاده گردد. روشهای دیگر برای بدست آوردن لاک به این شرح هستند:
۴-۱: spin_lock/unlock_bh
این تابع علاوه بر بدست آوردن لاک، اجرای SoftIRQها را بر روی پردازنده فعلی غیر فعال میکند. استفاده از این متد برای ایجاد همزمانی User Context ها با گروه دوم الزامی است.
دلیل غیر فعال کردن گروه ۲ احتمال بروز Dead Lock میباشد. در صورتیکه از حالت معمولی استفاده شود احتمال دارد به هنگامی که پردازش UserContext لاک را گرفته و در منطقه بحرانی به سر میبرد، توسط SoftIRQ پردازنده گرفته شده و آن هم سعی در گرفتن همان لاکی کند که هرگز باز نخواهد شد. البته در ایجاد همزمانی در سمت مقابل (SoftIRQها) با UserContext از فرمت ساده استفاده میشود چرا که در صورتی که پردازنده به آنها واگذار شود به هیچ عنوان توسط UserContext قابل بازپس گیری نیست.
استفاده از این مکانیزم بدین شکل خواهد بود:






UserContextSoftIRQ
spin_lock_bh(&lock);spin_lock(&lock);
//critical section//critical section
spin_unlock_bh(&lock);spin_unlock(&lock);

۴-۲: spin_lock/unlock_irq
این متد باعث میشود تا اجرای اینتراپتهای سخت افزاری و نرم افزاری بر روی پردازنده فعلی غیر فعال شود. از این متد برای هماهنگی ما بین گروههای ۳ و ۲ با گروه ۱ استفاده میشود. توجه داشته باشید که این تابع به هیچ عنوان به وضعیت فعال یا غیر فعال بودن اینتراپتهای سخت افزاری توجهی ندارد بنابراین برای صدا زدن از IRQ Handlerها مناسب نیست.
با توجه به اینکه اینتراپتهای نرم‌افزاری نیز غیر فعال میشوند میتوان از این روش برای ایجاد همزمانی مابین گروه ۳ با گروه ۱ استفاده کرد.
۳-۴:spin_lock_irqsave, spin_unlock_irqrestore
این متد علاوه بر بدست آوردن لاک، پرچم حالت (وضعیت فعال یا غیر فعال بودن اینتراپتها) را ذخیره میکند که برای ایجاد همزمانی ما بین IRQ Handlerها مورد استفاده قرار میگیرد.

۵- semaphore
چنانچه قبلا ذکر شد این نوع لاک مناسب برای ایجاد همزمانی مابین روندهای UserContext میباشد. بدست آوردن لاک در این مکانیزم معمولا به دو صورت انجام میشود: ۱- استفاده از تابع down و ۲- استفاده از تابع down_interruptible.
تابع شکل دوم در صورتی استفاده میشود که شما خواسته باشید در صورت ارسال سیگنال(به عنوان مثال فشردن ctrl+c) یا اینتراپت به برنامه‌ای که در حالت wait قرار دارد،‌ برنامه از wait خارج شود. خروج بدین شکل از حالت wait باعث میشود تابع down_interruptible مقدار خطا برگرداند.
با توجه به اینکه معمولا از mutex (سمافور با مقدار اولیه یک - یک نگهدارنده در یک زمان) برای ایجاد همزمانی استفاده میشود طرح کلی استفاده از این سمافور به عنوان نمونه ذکر میشود:
DEFINE_MUTEX(mt_lock);
mutex_lock_interruptible(&mt_lock);
//critical section
mutex_unlock(&mt_lock);
۶- مکانیزم مناسب جهت ایجاد همزمانی
جدول زیر به صورت خلاصه مکانیزم مناسب جهت ایجاد همزمانی با استفاده از لاکها را مابین روندهای گوناگون نشان میدهد. در این جدول متدهای مناسب جهت فراخوانی معرفی شده‌اند.

۳

۲

۱

روندها

spin_lock

spin_lock

spin_lock_irqsave

۱

spin_lock

spin_lock

spin_lock_irq

۲

down_interruptible

spin_lock_bh

spin_lock_irq

۳

اطلاعات جزءی‌تر را میتوانید از اینجا بدست آورید.

منابع استفاده شده:
Unreliable guide to locking
kernel locking techniques
understanding linux kernel