گنجه
۰۱ ۲۷م, ۱۳۸۸
مبنای بحث در این مطلب، زبانهای C++ و C هستند، ولی مطالعهٔ آن برای برنامهنویسان هر زبانی که در معرض ایراد0های فساد حافظه1 است و حتی هر برنامهنویسی، احتمالاً خالی از سود نخواهد بود.
برنامهای نوشتهاید که ایراد داره. اون رو حسابی آزمودهاید و هر ایراد منطقی2 رو که فکر میکنید ممکن هست وجود داشته باشه بررسی و رفع کردهاید، ولی هنوز برنامه ایراد داره!
برنامهٔ شما رفتارهای عجیبی از خودش نشون میده. مثلاً اگر در حالت ایرادزدایی3 بسازیدش4 کار میکنه، ولی در حالت انتشار5 یا اشتباه کار میکنه، یا کار نمیکنه و دچار سانحه6 میشه. یا مثلاً با ورودیهای مختلف، در جاهای کاملاً متفاوت و نسبتاً بیربط از کد دچار سانحه میشه. یا مثلاً همیشه در پایان یک تابع7 بعد از همهٔ عملیات، ولی قبل از بازگشت (یعنی دقیقاً روی { !!!) دچار سانحه میشه.
در چنین شرایطی هست که شما حدس میزنید برنامهتون دچار ایراد فساد حافظه1 شده.
اگر برنامه همیشه در یک تابع خاص دچار سانحه میشه، حدس میزنیم که در یکی از متغیرهای محلی همون تابع خرابکاری شده و پشته8 خراب شده؛ به همین دلیل در هنگام جمع کردن قاب پشتهٔ9 تابع از روی پشته، به علت فساد نشانی بازگشت10، برنامه دچار سانحه میشه11.12 معمولاً با خوندن دقیق همون تابع، ایراد یافت و حل میشه. اگر نشد، متغیرهای محلی که روی پشته هستند رو به کومه13 منتقل میکنیم (با استفاده از new یا malloc) و میریم به سراغ روشهای مربوط به فساد کومه که الان میخواهیم بگیم.
اولین قدم برای یافتن ایرادهای فساد حافظهٔ کومه، استمداد از حضرت Valgrind است (که در دبین14 بستهاش valgrind است)):
% valgrind --leak-check=full --leak-resolution=high ./buggy
برنامه با سرعتی بسیار کمتر از حد معمول اجرا میشه و هر دسترسی به حافظهٔ کومه که خارج از حافظهٔ تخصیصیافته15 باشه، با نام تابع و (در حالت ایرادزدایی) شمارهٔ خط کد منبع16 گزارش میشه. البته Valgrind بسیار جای حرف و معرفی و ستایش و غیره داره که از موضوع این مطلب خارجه!
اگر Valgrind مشکل رو یافت و ایراد رفع شد که خدا رو شکر میکنیم! اما اگر نه، با یک مورد بدخیم (!) طرف هستیم. یعنی دسترسیهایی داریم که به رغم ایراد داشتنشون، داخل حافظهٔ تخصیصیافته قرار گرفتهاند. مثلاً دو آرایهٔ ۱۰۰تایی پشت سر هم در کومه گرفتهایم و به عنصر ۱۲۵ام از آرایهٔ اول دست زدهایم! دسترسی هنوز داخل حافظهٔ پردازهٔ17 خودمان است، ولی نادرست و ایراددار. اگر کسی تا این جای مطلب رو خونده باشه تعجب میکنم! البته کسی به جز خودم!
انشاء الله در مطلب بعدی شیوهای برای یافتن این موارد بدخیم معرفی خواهد شد که از یکی از همکاران باتجربهترم (حفظه الله) آموختهام.
- Bug [↩]
- Memory corruption [↩] [↩]
- Logical [↩]
- Debug [↩]
- Build [↩]
- Release [↩]
- Crash [↩]
- Function [↩]
- Stack [↩]
- Stack frame [↩]
- Return address [↩]
- نوعی سرریز میانگیر (Buffer overflow) [↩]
- البته این رفتار سانحهآمیز (!) بستگی به همگردان داره، ولی همگردانهای جدید مثل GCCهای ۴ به بعد (و شاید بعضی قدیمیترها)، به علت داشتن روشهایی برای مقابله با خرابی پشته، عمداً برنامهای تولید میکنند که در این شرایط دچار سانحه بشه [↩]
- Heap [↩]
- Debian [↩]
- Allocated [↩]
- Source code [↩]
- Process [↩]
۰۱ ۱۵م, ۱۳۸۸
این مطلب در مورد زنده کردن لوحهای سختشدهٔ دلهامون به کمک موعظه یا چیزی شبیه اون نیست! (البته متأسفانه)
لوح سختی0 دارم که خراب شده بود. وقتی رایانه1 رو روشن میکردم، هی سعی میکرد راه بیفته ولی موفق نمیشد و اصلاً دور بر نمیداشت2. در نتیجه رایانه هم همون اول کار، توی مرحلهٔ شناسایی لوحهای سخت، گیر میکرد و راهاندازی3 نمیشد. تقریباً ازش قطع امید کرده بودم.
طبق معمول قبل از هر گونه تصمیمگیری، سری به اینترنت زدم تا ببینم ملت در این باره چی گفتهاند. بعد از کمی جستوجو، با راهکاری غیرمنتظره مواجه شدم: لوح سخت رو بگذارید تو یخزن4! بله، گفته بودند که بعد از سرد کردن لوح سخت، باید بلافاصله اون رو به رایانه بزنید. اگر قسمت باشه5 چند دقیقه فرصت دارید تا دادههای مهمترتون رو بر دارید.
از اون جا که چیز زیادی برای از دست دادن نداشتم، و در ضمن از این جور انگولککردنها خوشم میآد، تصمیم گرفتم این کار رو بکنم. لوح سخت رو توی یک کیسه پیچیدم تا برفک نزنه و خیس نشه و بعد داخل یخزن گذاشتم. بعد از چند ساعت که لوح سخت قصهٔ ما حسابی یخ زد، درش آوردم و سریع به رایانه وصلش کردم.
رایانه رو روشن کردم و در کمال ناباوری دیدم داره کار میکنه! با نرمافزار PowerMax آزمون سریع SMART6 رو انجام دادم و دیدم واقعاً سالمه7. با خودم گفتم شاید اگر در حالت خاموش گرم بشه، دوباره به همون روز بیفته. برای همین، رایانه رو خاموش نکردم و گذاشتم لوح سخت دائماً کار کنه تا در حال چرخش گرم بشه، باشد که سالم بمونه. نمیدونم کار این فکر بود یا نه، ولی هر چی بود جواب داد! برای این که مطمئن بشم که لوح سخت برای طولانیمدت سالم شده، رایانه رو خاموش کردم تا لوح کمی خنک بشه و بعد دوباره روشنش کردم. دوباره آزمودمش و دیدم که سالمه.
برای اطمینان از سلامت دادهها، از توی سامانهٔ عامل8، چند تا پرونده9 رو خوندم و بررسی کردم که شکر خدا سالم بودند.
سپاس خدای راست
۱۲ ۱۳م, ۱۳۸۷
یکی از روشهای نسبتاً متفاوت همگردان0ها برای بهینهسازی1، روش «بهینهسازی هدایتشده با سابقه»2 است که گاهی اون رو کوتاهانه3 PGO هم میگویند.
مؤثر بودن این روش رو میشه به چشم دید. مثلاً اگر از مرورگر Firefox هم در لینوکس4 و هم در ویندوز5 استفاده کرده باشید، حتماً تفاوت فاحش کارآیی6 رو حس کردهاید. دلیل این تفاوت فاحش، استفاده از PGO در ساخت7 پروندهٔ8 اجرایی9 ویندوز و عدم استفاده ازش در ساخت اجرایی لینوکس بیان شده.
خوب، حالا که به اندازهٔ کافی انگیزوندمتون (!)، ببینیم این روش اصلاً چی هست و چهجوری میتونیم ازش استفاده کنیم.
اساس این روش، استفاده از اطلاعات کسبشده در زمان اجرا10ی برنامه، برای بهینهسازی در زمان همگردانی11 هست. پس همگردانی با این روش، سه مرحله خواهد داشت:
۱. همگردانی برای تولید اجرایی مخصوص جمعآوری اطلاعات مورد نیاز برای بهینهسازی
۲. اجرای برنامهٔ تولیدشده در مرحلهٔ ۱ و استفاده از موارد کاربرد12 مورد نظر برای بهینهسازی
۳. همگردانی با بهینهسازی به کمک اطلاعات جمعآوریشده در مرحلهٔ ۲
همگردانها برای مراحل ۱ و ۳ گزینههای مخصوصی دارند که اینجا فقط GCC رو میبینیم.
درGCC برای مرحلهٔ ۱ از گزینهٔ -fprofile-generate و برای مرحلهٔ ۳ از گزینهٔ -fprofile-use استفاده میکنیم. دقت کنید که گزینهٔ -fprofile-generate رو هم باید برای همگردانی13 بدید، و هم برای پیوند14.
من که معمولاً از CMake به عنوان سامانهٔ ساخت15 استفاده میکنم، برای به کارگیری این روش، این چند خط رو به CMakeLists.txt اضافه میکنم:
# profile::generate
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --profile-generate")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --profile-generate")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --profile-generate")
# profile::use
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --profile-use")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --profile-use")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --profile-use")برای مرحلهٔ ۱، سه تا set اول و برای مرحلهٔ ۳، سه تا set دوم رو فعال میکنم. راستی دقت کردید که به جای -fprofile-folan میشه از -profile-folan- استفاده کرد؟!
۱۲ ۱۲م, ۱۳۸۷
با پرهیز از Java و .NET (در مقابل C++ و C) در روند گرمایش زمین سهم کمتری داشته باشیم.
۱۱ ۱۰م, ۱۳۸۷
دبیَن0 سامانهٔ عامل1 محبوب من هست.
موج زدن روح آزادی نرمافزار رو توی دبین حس میکنم.
بخش عظیمی از همهٔ نرمافزارهای آزاد دنیا رو میشه توی مخزن2های دبین، آمادهٔ نصب پیدا کرد؛ الان حدود ۲۰۰۰۰ تا بسته3 در مخازن رسمی4 هست.
سامانهٔ مدیریت بستههاش5، به نام APT (مخفف «ابزار بستهبندی پیشرفته»6)، بسیار تمیز و خوشدست هست.
اگر سامانهای حقیقتاً پایدار7 بخوام، دبین stable رو نصب میکنم. اگر پایداری برام کمتر از بهروز8 بودن نرمافزارها مهم باشه (مثلاً برای رایانهٔ منزل)، دبین unstable + experimental رو نصب میکنم. اگر روی سامانهٔ پایدارم آخرین نسخهٔ بستههای خاصی رو بخوام هم به سادگی میتونم بستههای deb رو جدا بگیرم و نصب کنم، یا با تنظیم /etc/apt/preferences کار رو به دست استاد APT بسپارم (به man apt_preferences مراجعه کنید).
قبل از این که خبر انتشار KDE 4.2 توی پایگاه رسمی KDE اعلام بشه، بستههای KDE 4.2 توی مخزن دبین experimental بود و داشتم نصبش میکردم!
نرمافزارهای دوستداشتنیای مثل VirtualBox رو که هم ویرایش آزاد دارند، و هم ویرایش غیرآزاد، و البته گاهی در پایگاه9 رسمیشون ایران عزیز رو تحریم کردهاند، از مخازن دبین به راحتی نصب میکنم بدون این که نگران تحریم و یا آلوده شدن رایانهام به نسخهٔ غیرآزادشون باشم. همهٔ نرمافزارهای بخش10 main، آزاد هستند و به هیچ نرمافزار غیرآزادی وابستگی11 ندارند.
بعضی نرمافزارهای غیرآزاد مثل افزونهٔ12 Flash، که ناچار از استفادهشون هستم رو میتونم بدون به هم زدن یکپارچگی سامانهام نصب کنم. بخشهای contrib و non-free حاوی بعضی از نرمافزارهای غیرآزاد پراستفاده هستند.
یادم نره بگم که دبین برای تعداد زیادی معماری13 مختلف رایانه وجود داره. از x86 و AMD64 بگیر تا MIPS و ARM و PowerPC و SPARC و Alpha و … (برای فهرست کاملش http://www.debian.org/ports/ رو ببینید)
با دبین به هستهٔ لینوکس14 محدود نیستم؛ میشه دبین رو با هستهٔ15 FreeBSD16 یا NetBSD17 یا Hurd18 یا OpenSolaris19 یا حتی20 Darwin/XNU (هستهٔ Mac OS X) استفاده کرد! شما رو نمیدونم، ولی این برای من خیلی مهم و همچنین خیلی هیجانانگیزه! مثلاً به این فکر کنید که میتونید توی محیط آشنا و دوستداشتنی دبین، به کاملترین پیادهسازی سامانهٔ پروندهٔ21 قدرتمند ZFS که توی OpenSolaris هست دسترسی داشته باشید.
راستی یادمون نره که اوبونتو22 بچهٔ دبین هست.
اینجوریهاست که بهش میگم حضرت دبین!
- Debian [↩]
- Operating System [↩]
- Repository [↩]
- Package [↩]
- Official [↩]
- Package Management [↩]
- Advanced Packaging Tool [↩]
- Stable [↩]
- Up-to-date [↩]
- Site [↩]
- Section [↩]
- Dependency [↩]
- Plugin [↩]
- Architecture [↩]
- Linux [↩]
- Kernel [↩]
- Debian GNU/kFreeBSD [↩]
- Debian GNU/NetBSD [↩]
- Debian GNU/Hurd [↩]
- Nexenta [↩]
- GNU-Darwin [↩]
- File System [↩]
- Ubuntu [↩]
۰۹ ۲۴م, ۱۳۸۷
از وقتی با Greasemonkey آشنا شدهام توقعم از تار1 خیلی بالا رفته.
با این افزونهٔ Firefox میشه در طرف مخدوم2، به هر صفحهای کدهای JavaScript دلخواه رو اضافه کرد. علاوه بر کلی دستنوشتهٔ3 آماده که توی http://userscripts.org براش وجود داره، خودمون هم میتونیم به راحتی براش دستنوشته بنویسیم. حالا اگر قدرت JavaScript رو بیاری تو ذهنت میبینی که چه کارها میشه به کمک این میمون چرب (!) کرد.
برای شروع ماجراجویی، کمی از http://diveintogreasemonkey.org رو خوندم. نقطهٔ شروع بسیار خوبی بود.
یک دستنوشته به نام فارسنده یا Persianizer که به شیوهٔ بِکَن-بچسبون4 ساختهام، همهٔ رقمهای انگلیسی و «ي» و «ك»های عربی توی صفحهٔ بارگذاری شده رو به معادل فارسیشون تبدیل میکنه. بس که اون صفحههای فارسی پر از «ي» حالم رو بد میکنه! فقط موقع استفاده حواست باشه پایگاههایی که قراره فارسنده روشون کار بکنه رو خوب انتخاب کنی؛ پیشفرضهاش خوب نیستند.
البته یکی از شرایط من (و خیلیهای دیگر) برای استفاده از یک فنآوری، انحصاری نبودنش هست که خوشبختانه افزونههایی سازگار با Greasemonkey برای مرورگرهای مهم دیگه (از جمله Konqueror) هم وجود داره.
نکتهٔ پایانی این که اگر کاربر Greasemonkey باشی، افزونهٔ Greasefire هم احتمالاً برات جالب هست.
این هم فهرست دستنوشتههای محبوب من:
http://userscripts.org/users/73214/favorites
۰۹ ۳م, ۱۳۸۷
اگر برنامهنویس C++ باشی، حتماً تا حالا مورد آزار و اذیت پیغامهای خطای سرسامآور همگردان0 در مورد STL قرار گرفتهای. مثلاً این:
testmm2.cpp:31: error: no matching function for call to ‘std::multimap<int*, int, std::less<int*>, std::allocator<std::pair<int* const, int> > >::insert(int)’ /usr/include/c++/4.3/bits/stl_multimap.h:407: note: candidates are: typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const_Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator std::multimap<_Key, _Tp, _Compare, _Alloc>::insert(const std::pair<const _Key, _Tp>&) [with _Key = int*, _Tp = int, _Compare = std::less<int*>, _Alloc = std::allocator<std::pair<int* const, int> >] /usr/include/c++/4.3/bits/stl_multimap.h:431: note: typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator std::multimap<_Key, _Tp, _Compare, _Alloc>::insert(typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, const std::pair<const _Key, _Tp>&) [with _Key = int*, _Tp = int, _Compare = std::less<int*>, _Alloc = std::allocator<std::pair<int* const, int> >]
حالا این یکی بود. وقتی ده-پونزده تا از اینها یکجا رخ میده، خیلی افتضاح میشه.
مدتها با این مشکل کنار اومده بودم و دردش رو تحمل میکردم. اما اخیراً یک راه نجات یافتم: ابزاری به نام STLFilt. یک نرمافزار آزاد هست که خروجی همگردان C++ رو میگیره و اون پیغامهای وحشتناک رو مهربون میکنه. مثلاً پیغام بالا رو تبدیل میکنه به این:
testmm2.cpp:31: error: No match for ‘multimap<int *, int>::insert(int)’
stl_multimap.h:407: candidates are: map<int *, int>::iter multimap<int *, int>
::insert(const pair<const int *, int> &)
stl_multimap.h:431: map<
int *, int
>::iter multimap<
int *, int
>::insert(map<int *, int>::iter, const pair<const int *, int> &)آخش! حالا میشه خوندش!
برای استفاده از STLFilt اول باید بستهٔ مربوط به همگردانت رو بگیری. مثلاً gstlfilt.zip برای G++. توش کلی پرونده هست که همه رو بیخیال میشی به جز gSTLFilt.pl که اصل کار رو انجام میده. از stdin میخونه و توی stdout مینویسه. کاری که من کردم این بود1:
sudo su F=/usr/local/bin/stlfilt.pl echo '#!/usr/bin/perl' >$F cat gSTLFilt.pl >>$F chmod +x $F
و برای استفاده:
make 2>&1 | stlfilt.pl
برای این که هر بار نخوام stderr دستور ساخت2 (مثلاً make) رو به stdoutش redirect3 کنم، دستنوشتهٔ4 /usr/local/bin/stlfilt رو با این محتوا ساختم:
#!/bin/sh "$@" 2>&1 | stlfilt.pl
و chmod +x هم یادم نره. ![]()
حالا اینجوری استفاده میشه:
stlfilt makeبا دستنوشتهها زندگی زیباتره!
ویرایش: امید آن میرود که با معرفی «مفهوم»5ها در C++0x (به زودی!) کل مشکل از بیخ حل بشه و دیگه نیازی به ابزار اضافی نباشه.