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/

No comments: