Tuesday, December 09, 2008

"vi" & vim" - Programmers view #2

به نام دوست

vi برای برنامه نویسان امکانات گوناگونی فراهم میکند که بیشک رافع مشکلات برنامه نویس در هنگام نوشتن برنامه میباشند. در این نوشته طریقه استفاده از tagها به عنوان یک راه حال مناسب جهت دستیابی سریع به تعارف فانکشنها و کلا شناسه ها بیان خواهد شد. همچنین مواردی جهت جابه جایی در کد نیز در ادامه خواهد آمد.

1- Tag چیست؟
معمولا از پردردسرترین موضوعات در هنگام کار با سورس کرنل یافتن تعریف فانکشنهای مورد استفاده است. جستجو در درون فایلها راه حل اول است:
find /path/to/kernel | xargs grep yourfunction_name

اما تکرار این موضوع خسته کننده است. استفاده از tagها مناسبترین راه حل است.
Tag مشخص کننده جایگاه تعریف یک شناسه (identifier) است. به عنوان مثال تعریف یک فانکشن در c را میتوان در نظر گرفت. Tagها در درون فایلی با نام tags نگهداری میشوند و در درون vi با استفاده از محتویات آن برنامه نویس میتواند مستقیما به محل تعریف شناسه پرش کند. این روش سریعترین و مناسبترین روش برای دستیابی به تعاریف فانکشنها و یا متغیرها است.
تولید tags فایل توسط برنامه ای با نام ctags انجام میشود که در تمام linuxها نسخه ای از آن وجود دارد. طریقه استفاده از آن نیز به این شکل است:
ctags *.c *.h
که در درون تمام فایلهای c. و h. به دنبال تعاریف خواهد گشت و فایلی به نام tags را به صورت پیش فرض ایجاد خواهد کرد.. چنانچه سورس برنامه دارای شاخه ها و زیرشاخه هایی باشد به دوصورت میتوان فایل tags را تولید کرد:
  • برای هر شاخه یک فایل tag مجزا تعریف شود که این امر مستلزم اجرای دستور ctags در هر شاخه به صورت مجزا میباشد. معمولا این امر هنگامی که سورس برنامه از Makefile ها برای ساخت استفاده میکند، به صورت بازگشتی دستور ctags برای تمام زیرشاخه ها اجرا میشود.
  • البته روش سریعتر این است که برای تمام زیرشاخه ها یک فایل ctags ساخته شود. برای این کار میتوان از دستور زیر استفاده کرد:
    ctags -R
برای رجوع(پرش) به تعاریف شناسه ها میتوان از دستور tag در حالت خط فرمان استفاده کرد:
:tag identifier
و یا اینکه اگر نام فانکشن در زیر مکان نما قرار دارد از ترکیب [+ctrl استفاده کرد. برای بازگشت به عقب نیز میتوان از ترکیب ctrl+T استفاده کرد.
دستور tags: لیست تمامی تگهایی که مرور شده است را با ترتب LIFO نشان خواهد داد. دستور tag: موجب پرش به تگ ابتدای لیست خواهد شد و همچنین 2tag: موجب پرش به عنصر دوم از بالای لیست خواهد شد.

1-1: تقسیم(split) پنجره:
استفاده از دستور tag: و یا [+ctrl باعث جایگزینی پنجره فعلی میشود. برای نمایش تگ همزمان با نمایش کد فعلی که در حال فعالیت بر روی آن هستید باید پنجره به دو تیکه تقسیم(split) شود. برای اینکار میتوان از دستور split: و به دنبال آن دستور tag: استفاده کرد.
دستور مناسبتر برای این منظور دستور stag است:
:stag identifier_name
در صورتیکه نام شناسه در زیر مکان نما باشد میتوان از ترکیب کلیدهای زیر استفاده کرد:
ctrl+w ]

1-2: استفاده از فایلهای tags گوناگون:
در حالت معمول vi در شاخه جاری به دنبال فایل tags میگردد. در صورتیکه tags فایلهای گوناگونی داشته باشید میبایست همه آنها را مشخص کنید. برای اینکار میبایست پارامتر tags را مقدار دهی کرد. به عنوان مثال:
:set tags=./tags,./../tags,./*/tags
این دستور باعث میشود که tags فایل موجود در مسیر جاری، یک شاخه بالاتر و همچنین تمام زیرشاخه های موجود در شاخه جاری مورد نظر قرار گیرند.
همچنین ارجاع به تمام زیرشاخه ها در یک شاخه میتواند به این صورت انجام شود:
:set tags=~/projs/**/tags
که باعث میشود تمام فایلهای تگ با نام tags در ساختار درختی زیر مجموعه projs (که در شاخه home کاربر قرار دارد)، مورد استفاده قرار گیرد.
البته همانطور که در قبل آمد میتوان یک فایل تگ برای یک شاخه و زیرشاخه های آن ساخت که به این طریق دسترسی به تگها سریعتر خواهد شد(دستور ctags -R ).

3-1: تطابقهای گوناگون:
در صورتیکه شناسه در مکانهای مختلف تعریف شده باشد هنگام استفاده از دستور tag: به اولین تطابق پرش خواهد شد. برای رفتن به تطابق بعدی میبایست از دستور:
:tnext
استفاده کرد. همچنین
:2tnext
باعث پرش دوگام به جلو در لیست تطابقها میشود.
همچنین دستور
:tselect identifier
لیست تمامی تطابقها را نشان خواهد داد که با زدن شماره آن میتوان به آن تطابق پرش کرد. دیگر کامندها برای جابه جایی بین تطابقها به این شرح هستند:
:tfirst پرش به اولین تطابق
:tlast پرش به آخرین تطابق
:tprevious پرش به قبل شبیه به دستور tnext

1-4: حدس نام تگ:
استفاده از کلید tab در پوسته لینوکس امری رایج است. جالب اینجاست که در هنگام استفاده از دستورات tag (که در قبل آمد) به هنگام تایپ شناسه میتوان از tab استفاده کرد.
در حالت معمول میتوان قسمتی از آن را تایپ کرد و به دنبال آن کلید tab را زد:
:tag iden
همچنین در صورتیکه به دنبال تگی میگردید که شامل کلمه ای مانند block باشد میتوان به اینصورت عمل کرد:
:tag /block
استفاده از / بدان معنی است که دنباله دستور tag یک شناسه نیست بلکه یک الگو (pattern) است. بنابراین جستجو در تگهایی که شامل عبارت block است باید انجام شود.
همچنین استفاده از «^» در ابتدای عبارت بدان معنی است که الگو در ابتدای تگ باشد و همچنین استفاده از «$» در انتهای الگو بدان معنی است که الگو در انتهای تگ باشد. برای آگاهی از طریقه مشخص کردن الگو از دستور help pattern استفاده کنید.

1-5: پنجره پیش نمایش:
در پاره ای از مواقع خواستار نمایش تعاریف بدون ترک پنجره فعلی هستیم. به عنوان مثال شما میخواهید تعریف فانکشن را ببینید تا از کاربرد پارامترها مطمئن شوید. برای این منظور میتوان از دستور ptag استفاده کرد:
:ptag identifier
همچنین اگر نام شناسه در زیر مکان نما باشد میتوان از ترکیب کلیدهای
ctrl+w }
استفاده کرد.
جهت بستن پنجره پیش نمایش باید از دستور pclose: استفاده کرد.
برای ویرایش یک فایل در پنجره پیش نمایش میتوان از دستور pedit استفاده کرد:
pedit yourfile.h
همچنین جهت جستجوی یک کلمه در فایل جاری و تمام فایلهای includeشده و نمایش در پنجره پیش نمایش میبایست از
:psearch word
استفاده کرد.

2: جابه جایی در یک برنامه
در هنگام برنامه نویسی خیلی اوقات اتفاق می افتد که برنامه نویس نیاز به پرش به ابتدای بلاک، پرانتز متناظر و ... جهت اطمینان از صحت برنامه پیدا میکند.
استفاده از «%» در حالت فرمان، باعث جابه جایی بین براکتهای متناظر میشود. به عنوان اگر بر روی یکی از کاراکترهای {}و ()و [] قرار گرفته و % را بزنید مکان نما به مکان کاراکتر متناظر خواهد رفت. استفاده جالبتر آن برای بلاکهای ifdef# در ماکروها هست که باعث جابه جایی بین ابتدا و انتهای بلاک if و همچنین در صورت وجود else باعث حرکت به صورت منظم بین آنها خواهد شد.
در صورتیکه در بین کد نوشته شده برای یک ماکرو هستید و نیازمند به پرش به ابتدای تعریف ماکرو هستید میتوانید از ترکیب #] بار پرش به ابتدا و همچنین برای پرش به انتها از #[ استفاده کنید.

2-1: حرکت در بلاکی از کد
با توجه به اینکه بلاکهای c در بین {} قرار دارند برای جابه جایی بین ابتدا و انتهای بلاکی که در آن قرار داریم میتوانیم از ترکیب }] برای پرش به ابتدای بلاک جاری و همچنین }[ برای پرش به انتهای بلاک جاری استفاده کنیم.
همچنین ترکیب )] و ([ رفتاری مشابه ترکیب کلمات قبلی برای جابه جایی در ابتدا و انتهای عبارت محصور شده با () دارد. ترکیب /] و /[ برای جابه جایی در ابتدا و انتهای توضیحات (comment) استفاده میشود.
m] و m[ برای جابه جایی به ابتدا و انتهای یک فانکشن استفاده میشود.

2-2: یافتن شناسه عمومی (Global):
در صورت نیاز به دیدن تعریف یک شناسه ای که به صورت global تعریف شده از ترکیب I] میتوان استفاده کرد. فشردن این کلیدها لیست تمامی تطابقها با جستجو در فایل جاری و فایلهای include شده را نمایش خواهد شد.

2-3: تعیین مکان فایلهای include شده:
فایلهای include شده در مسیرهایی که توسط متغیر path تعیین شده اند جتسجو خواهند شد که طریقه تنظیم آن در قسمت قبلی توضیح داده شد.
استفاده از دستور
:checkpath
فایلهای include را که در مسیرهای مشخص شده توسط path پیدا نشده اند را لیست خواهد کرد. همچنین استفاده از دستور
:checkpath!
لیست فایلهای include که قابل دستیابی در مسیرهای path هستند مشخص خواهند شد.
تنظیم پارامتر path میتواند به اینصورت نیز انجام شود:
:set path+=/projs/**/include
که باعث خواهد شد تمام شاخه های include در تمام زیرشاخه های projs به path اضافه شوند.

2-4: پرش به یک تطابق:
استفاده از I] لیست تمام تطابق را نشان خواهد داد. استفاده از ترکیب:
[tab
باعث پرش به اولین تطابق میشود(منظور از tab فشردن کلید tab است). در لیست نشان داده شده توسط I] هر عنصر لیست با یک شماره مشخص میشود. از آن شماره میتوان جهت پرش به تطابق مرود نظر استفاده کرد. به عنوان مثال:
3[tab
باعث پرش به تطابق با شماره 3 میشود. به جای کلید tab میتوان از ترکیب ctrl+I نیز استفاده کرد. در تمام موارد برای بازگشت به مکانی که از آنجا پرش به تطابق را آغاز کرده اید از ترکیب ctrl+o باید استفاده کرد.
همچنین ترکیبات زیر نیز مفید هستند:
[i تنها اولین تطابق را لیست میکند
]I آیتمهایی را که در زیر مکان نما هستند را نمایش خواهد داد
]i اولین تطابق در زیر مکان نما را لیست خواهد کرد.

2-5: یافتن شناسه ای که به صورت ماکرو تعریف شده است:
استفاه از I] تمام شناسه هایی را که به هر طریق تعریف شده است نمایش خواهد داد. در صورتیکه به دنبال تعریف شناسه ای هستید که به صورت ماکرو تعریف شده است باید از ترکیب D] استفاده کنید. پارامتر «defined» مشخص میکند که کدام خطوط مربوط به تعریف ماکرو هستند که میتواند برای زبانهای گوناگون تنظیم شود(البته به صورت پیش فرض برای c تعرف شده است).
ترکیبات زیر نیز مفید هستند:
[d اولین تطابق را لیست خواهد کرد
]D تنها آیتمهای زیر مکان نما را لیست خواهد کرد
]d تنها اولین تطابق زیر مکان نما را لیست خواهد کرد

2-6: یافتن شناسه های محلی
جهت جستجوی شناسه تنها در فایل جاری میتوان از ترکیب gD استفاده کرد. همچنین برای جستجو در فانکشن جاری از ترکیب gd استفاده میشود.


منبع: اینجا

Thursday, December 04, 2008

Some Features of "vi" and "vim"... #1

به نام دوست

ویرایشگر vi و vim (که نسخه گسترش داده شده vi) است جزو ویرایشگرهای محبوب در بین لینوکس کاران است. البته در رویارویی اولیه شاید ظاهر بدون منو کمی مشکل آفرین باشد اما آشنا شدن با دستورات این ویرایشگر قدرت زیادی به یک برنامه نویس میدهد.

vi سه وضعیت کاری (mode) دارد:
حالت فرمان: در ابتدای ورود به vi این ویرایشگر در این حالت قرار دارد. فشردن هر کلید به منزله صدور دستوری برای vi است. توجه داشته باشید که بزرگ و کوچیک بودن حروف در اجرای نوع دستور موثر است.
حالت ویرایش(درج): با فشردن کلید "i" در «حالت فرمان»، vi به مود ویرایش خواهد رفت. البته میتوان با زدن کلیدهای "o" و "O" نیز به این حالت رفت که اولی خطی جدید در پایین مکان فعلی و دومی خطی جدید در بالای مکان فعلی باز کده و به حالت ویرایش میرود. با زدن کلید «Esc» میتوان از «حالت ویرایش» خارج و به «حالت فرمان» رفت.
حالت خط فرمان: در این وضعیت که با زدن کلید «:» در «حالت فرمان» در آن قرار میگیریم، دستورات خط فرمان vi قابل تایپ هستند که در ادامه با آنها آشنا خواهیم شد. با دوبار فشردن «Esc» میتوان به «حالت فرمان» رفت.

توجه: در تمام مواردی که در پایین ذکر شده است مورد زیر صادق است:
۱- اگر ctrl+x ذکر شده به معنای فشردن کلید ctrl به همراه x است
۲- اگر حروفی پشت سر هم ذکر شده اند مانند dd به معنای فشردن دو بار حرف d است
۳- در تمام توضیحاتی که در زیر آمده است، از جدا کننده بین کلید(فرمان) و توضیح به جهت جلوگیری از اشتباه استفاده نشده است
۴- در تایپ عبارات انگلیسی سعی شده ترتیب حروف مطابق با ترتیب اعمال آنها به نظر رسد. با توجه به اینکه صفحه راستچین است کپی-پیست کردن این عبارت توصیه نمیشود.

جابه‌جایی مکان نما (حالت فرمان):
ctrl+d جابه‌جایی به میزان نصف صفحه به سمت پایین
ctrl+u جابه‌جایی به میزان نصف صفحه به سمت بالا
ctrl+f جابه‌جایی به میزان تمام صفحه به سمت پایین
ctrl+b جابه‌جایی به میزان تمام صفحه به سمت بالا
M جابه‌جایی مکان نما به وسط صفحه
H جابه‌جایی مکان نما به ابتدای صفحه
L جابه‌جایی مکان نما به انتهای صفحه
0 صفر، جابه‌جایی مکان نما به ابتدای خط
$ انتقال به انتهای خط
G انتقال به انتهای فایل
% انتقال به براکت متناظر.. با قرار گرفتن بر روی حروف {} [] () زده میشود. حاصل انتقال به براکت متناظر است. در برنامه نویسی برای اطمینان از صحت تناظر کاربرد خوبی دارد.

دستورات ویرایش (حالت فرمان):
u بازگرداندن آخرین ویرایش undo
U بازگرداندن تمامی تغییرات در خط جاری
dd حذف خط جاری
3dd حذف ۳ خط از مکان جاری
D حذف محتوای خط از محل مکان‌نما تا انتهای خط
C شبیه به D البته وارد حالت ویرایش میشود
x حذف کاراکتر زیر مکان نما
r جابه‌جایی replcae کاراکتر زیر مکان نما
s حذف کاراکتر فعلی و ورود به حالت ویرایش
S حذف خط فعلی و ورود به حالت ویرایش
~ تغییر case (بزرگ و کوچیک) کاراکتر در محل مکان نما
ctrl+a افزایش عدد زیر مکان نما به میزان یک واحد
ctrl+x کاهش عدد زیر مکان نما به میزان یک واحد
string/ جستجوی string به سمت انتهای فایل (بعد از تایپ باید کلید enter زده شود)
string? جستجوی string به سمت ابتدای فایل (بعد از تایپ باید کلید enter زده شود)
<\string>\/ جستجو برای یافتن string. در این جستجو کلماتی که string جزو آنها باشد تطابق محسوب نمیشوند.
n پیدا کردن رخداد بعدی کلمه مورد جستجو
N پیدا کردن رخداد قبلی کلمه مورد جستجو
. تکرار آخرین فرمان

نشانه دار کردن یک خط (mark) (در حالت فرمان): برای نشانه دار کردن یک خط کافی است کلید m را بزنید و به دنبال آن یک حرف به عنوان یک نشانه برای آن خط. جهت رجوع به آن خط باید ابتدای کاراکتر «'» را و به دنبال آن حرفی که به عنوان نشانه در نظر گرفته شده زد.

کپی-پیست کردن (copy-paste):
برای کپی کردن (انتقال متن به حافظه):
yy خط جاری را کپی خواهد کرد
3yy از خط جاری ۳ خط را کپی خواهد کرد (با خط جاری دو خط دیگر)
y't از خط جاری تا خطی را که با حرف t نشانه‌دار شده است کپی خواهد کرد
برای پیست کردن (انتقال متن از حافظه):
p متن درون حافظه را بعد از خط جاری پیست خواهد کرد.

انتقال متن به چپ یا راست (شبیه به فشردن tab):
>> انتقال به سمت چپ
<< انتقال به سمت راست
اما انتقال یک بلاک مختص vim است که نیاز به آشنایی مختصری با حالت ویژوال دارد... در حالت ویژوال این امکان وجود دارد که قسمتی از متن انتخاب (high light) شود. در این صورت مواردی که به صورت تکی بر روی کاراکتر یا خط قابل اعمال بوده به صورت گروهی بر روی تمام متن انتخاب شده قابل اجرا است (مانند کپی-پیست کردن)

حالت ویژوال:
ورود به حالت ویژوال از حالت فرمان به سه طریق امکان پذیر است:
v که اصطلاحا حالت ویژوال کاراکتری است. انتخاب کاراکتر به کاراکتر خواهد بود. با زدن این حرف میتوانید با حرکت با کلیدهای جهت نما گستره انتخاب خود را تعیین کنید
V باعث ورود به حالت ویژوال خطی میشود. در این حالت کوچکترین واحد انتخابی خط خواهد بود.
ctrl+v حالت ویژوال بلاکی. میتوانید یک مستطیل از فایل را انتخاب کنید.
برای خروج از حالت ویژوال نیز میتوان از Esc استفاده کرد. البته در هر حالت ویژوال با فشردن مجدد کلید فرمان مربوط به همان حالت ویژوال از حالت ویژوال خارج خواهید شد.
با فشردن gv به حالت ویژوال قبلی خواهید رفت و همچنین متن انتخاب شده(در حالت قبل) نیز مجددا ظاهر خواهد شد. همچنین فشردن کلید o (او) در حالت ویژوال باعث جابه‌جایی بین ابتدا و انتهای بلاک انتخاب شده خواهد شد.

حالت دیگری هم به نام حالت انتخاب (select mode) وجود دارد که شبیه به ویژوال است. برای ورود به این حالت باید gh را بزنید. در این حالت میتوانید متنی را انتخاب کنید در صورتیکه شروع به فشردن حروف کنید متن انتخاب شده حذف و مواردی که شما تایپ میکنید جایگزین آن خواهد شد.
کلید h به معنای انتخاب کاراکتری است و همچنین H حالت انتخاب خطی و ctrl+h انتخاب بلاکی (مستطیلی) است.

حالت خط فرمان:
در این حالت که بازدن «:» (دو نقطه) از حالت فرمان به آن وارد میشویم دستورات خط فرمان vi صادر میشود.

help topic: متن کمک در مورد topic ظاهر خواهد شد. در صورتیکه متن کامل topic مجهول باشد میتوان با زدن tab بین کلمات محتمل چرخی زد. همچنین اگر بعد از تایپ help کلید ctrl+d را بزنید تمامی تاپیکها ظاهر میشوند.

set all: تمامی تنظیمات vi نمایش داده خواهد شد.
set SET_COMMAND: بر اساس SET_COMMAND پارامتری از vi ست میشود. به عنوان مثال set ic: باعث میشود که بزرگ و کوچیک بودن حروف در جستجو در نظر گرفته نشود (case Insensitive) . بعضی از تنظیمات به شرح زیر هستند:

حالت کامل
متناظر کوتاه
توصیف
autoindent/noautoindent ai/noai با زدن enter در انتهای خط، مکان نما در محل indent خط قبل قرار میگیرد. این تنظیم در هنگام برنامه نویسی خیلی مفید است
autowrite/noautowrite aw/noaw در قسمت tag توضیح داده شده
errorbells/noerrorbells eb/noeb حذف beep (صدای کوتاه خطا) در هنگام بروز خطا
flash/noflash fl/nofl در هنگام بروز خطا صفحه flash میشود.
tabstop=8 ts تعداد فضای خالی tab را مشخص میکند
ignorecase/noignorecase ic/noic در نظر نگرفتن بزرگ و کوچیک بودن حروف در جستجو
number/nonumber nu/nonu نشان دادن شماره خطها
showmatch/noshowmatch no abbreviations براکت متناظر را نشان میدهد
showmode/noshowmode no abbreviations نشان دادن وضعیت کاری ویرایشگر
taglength tl تعداد کاراکتر موثر در نشان دادن تگ، به صورت پیش فرض صفر است.
closepunct='".,;)]}
کارکترهایی که کارکاتر متناظر آنها با زدن % نشان داده میشود
linelimit=1048560
حداکثر طول فایل
wrapscan/nowrapscan ws/nows شکستن خطوط در صورتیکه خیلی طولانی باشند
wrapmargin=0/nowrapmargin wm/nowm فضای قبل از خط شکسته شده را مشخص میکند
list/nolist
انتهای خط و همچنین تبها را به صورت مشخص نشان میدهد
bg=dark
bg=light

شیوه رنگ صفحه را مشخص میکند. سفید روی سیاه و یا سیاه روی سفید

چنانچه خواستار انجام تنظیمات برای تمام فعالیتهای ویرایشی خود باشید میتوانید از فایل vimrc. در home folder خود استفاده کنید. چنانچه این فایل موجود نیست میتوانید فایلی با این نام بسازید. هر مرتبه که vi اجرا میشود ابتدا دستورات موجود دراین فایل اجرا خواهد شد. به عنوان مثال عبارات زیر را به عنوان محتوای فایل vimrc. در نظر بگیرید:
" set auto indent
:set ai
باعث میشود که auto indent به صورت پیش فرض تنظیم شود.
توجه: کامنتها در این فایل با " شروع میشوند.

جستجو(Find) و جابه‌جایی(Replace):
معمولا از رایج‌ترین نیازهای استفاده‌کننده از ویرایشگرها جستجو در متن میباشد. چگونگی انجام جستجو قبلا توضیح داده شده است. اما جابه‌جایی در vi در حالت خط فرمان انجام میشود. مثالهایی جهت روشن شدن موضوع:
/s/ffff/rrrr: اولین رخداد از ffff را در یک خط با rrrr جایگزین میکند
/s/ffff/rrrr%: و یا s/ffff/rrrr/g%: تمام رخدادهای ffff را در یک فایل با rrrr جایگزین خواهد کرد.
s/ffff/rrrr/gc%: تمام رخدادهای ffff با rrrr جایگزین خواهد شد اما با سوالی هر جایگزینی از طرف کاربر تایید خواهد شد.
s/ffff/rrrr/i: اولین رخداد از ffff را با rrrr جایگزین میشود البته بدن احتساب کوچیک یا بزرگ بودن حروف.
توجه: ترکیب i با دستورات قبلی نیز مجاز و موجب جستجوی case insensitive میشود.

تعیین محدوده جستجو (تعیین محدوده):
نکته: کارکتر % قبل از s به معنای تمام فایل میباشد. به عبارتی قبل از s محدوده مورد جستجو مشخص میشود. به عنوان مثال از آن جهت که آخرین خط یک فایل در vi با $ مشخص میشود، میتوان به جای % از عبارت $,1 استفاده که همان مفهوم % را خواهد رساند.
همچنین کارکتر نقطه «.» به معنای خط جاری است. بنابراین واژه .,t' محدوده خط نشانه‌دار شده با حرف t تا خط جاری را مشخص میکند.
همچنین جهت تعیین یک بلاک برای جستجو هم میتوان از شماره خطوط استفاده کرد: مثلا 5,23 که محدوده خطوط ۵ تا ۲۳ مورد جستجو قرار میگیرد و همچنین چنانچه خطوطی نشانه‌دار(mark) شده باشند میتوان از نشانه‌ها بدینصورت استفاده کرد: a,‍'b' (کاراکتر قبل از a و b تک کوتیشن است)که حدفاصل خطوطی که با a و b نشانه‌دار شده‌اند مورد جستجو قرار میگیرد.

اجرای دستورات unix در vi و تعامل با آنها:
command!: دستور command اجرا خواهد شد. به عنوان مثال pwd!: باعث اجرای pwd (چاپ شاخه جاری) خواهد شد.
r !command: موجب میشود که خروجی در یک خط جدید در پایین مکان‌نما قرار گیرد. به عنوان مثال r !date: باعث چاپ تاریخ در خط جدید در زیر مکان فعلی میشود.

همچنین این امکان وجود دارد که خطوطی به عنوان ورودی به دستورات داده شود و خروجی آن دستور جای آنها را بگیرد. به عنوان مثال؛
:1,$ !sort
باعث مرتب شدن تمامی خطوط فایل میشود (تعیین محدوده میتواند به طرقی که در قبل آمد انجام شود)
و همچنین مثال دیگر که اندکی جالب است:
:'a,'b !awk '{ $1 " " $2 " " $3 " " }'
اتفاقی به شرح زیر رخ خواهد داد






aaa bbb ccc
ccc bbb aaa
ddd eee fff تبدیل میشود به: > fff eee ddd
ggg hhh iii
iii hhh ggg
با استفاده از awk توانسته‌ایم جای ستونها را عوض کنیم.

پرش به فایل include شده:
در بسیاری از موارد هنگامی که در حال برنامه‌نویسی هستید شاید نیاز به چک محتوای فایل include شده داشته باشید. در vi میتوانید مکان نما را بر روی فایل داخل عبارت include قرار بدهید و کلیدهای gf که کوتاه شده go to file است را فشار دهید. فایل مورد نظر باز خواهد شد.
البته استفاده از gf عام است. یعنی هر جا که مسیر فایلی در متن تایپ شده باشد و gf فشار داده شود فایل باز خواهد شد. جستجو در مسیرهایی که توسط path مشخص شده‌اند انجام خواهد شد. دستور زیر را امتخان کنید:
:set path

محتوای path نمایش داده خواهد شد. جهت تغییر آن میتوانید به عنوان مثال به این صورت عمل کنید:
set path=/home,/,,

همچنین میتوانید درون فایل vimrc. نیز آن را تنظیم کنید. به عنوان مثال:
set path=$PWD/**

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

کار با فایلهای متعددی که به صورت همزمان باز شده‌اند:
در بعضی مواقع ممکن است شما چند فایل به صورت همزمان باز کرده باشید. این اتفاق ممکن است به روشهای گوناگون رخ دهد: یکی از آنها در قسمت قبلی توضیح داده شد که همان gf‌ است.
روش دیگر این است که شما از دستور open استفاده کنید (در حالت خط فرمان):
:open /my/path/example.c
و همچنین ممکن است در هنگام اجرای دستور vi چند فایل را به عنوان پارامتر مشخص کرده باشید:
vi file1 file2 file3
به هر حال دستورات زیر در حالت خط فرمان میتواند به شما کمک کند:
buffers: تمام فایلهای باز را نشان خواهد داد. هر کدام با یک شماره مشخص میشوند.
# buffer: باید به جای # شماره فایل باز را که از دستور قبل بدست آورده‌اید قرار بدید. در این صورت فایل مورد نظرتان نمایش داده خواهد شد.

خروج از vi :
به هر حال در پایان کار شما نیاز به خروج از vi دارید. به روشهای زیر میتوانید این کار را انجام دهید:
فشردن ZZ (در حالت فرمان)که باعث ذخیره شده تغییرات و خروج میشود.
wq: در حالت خط فرمان باعث ذخیره و خروج میشود
w: در حالت خط فرمان باعث ذخیره تغییرات میشود
!q: خروج بدون ذخیره تغییرات
qa: خروج از تمامی فایلهای باز

منبع: اینجا

Wednesday, November 26, 2008

what is RCU? #1

به نام دوست

۱- مقدمه

RCU که مخفف Read-Copy Update میباشد یکی از روشهای ایجاد همزمانی در کنار روشهای متداول مدیریت مناطق بحرانی(حفاظت از اطلاعات به اشتراک گذاشته شده (اطلاعات بحرانی) مابین رشته‌های اجرایی) در کرنل لینوکس میباشد. مزیت اصلی این روش امکان انجام عمل read همزمان با عمل write/update میباشد. در روشهای معمول استفاده از lock ها در مدیریت مناطق بحرانی، در صورتیکه از قفلهای معمولی استفاده شود در هر زمان تنها یک پردازش قابلیت ورود به منطقه بحرانی را دارد و یا اگر از read-write lock ها استفاده شود امکان همزمانی ما بین پردازشهای خواننده وجود دارد و با ورود یک نویسنده، خواننده ها باید تا خروج او متوقف شوند.

RCU امکان رخداد یک write/update با readهای گوناگون را فراهم میسازد. بنابراین پردازشهایی که عمل read انجام میدهند به هیچ عنوان منتظر اتمام عمل write/update نخواهند شد که مزیت عمده ای به حساب می آید.

نکته ۱: چنانکه در پاراگراف قبلی نیز ذکر گردید، تنها رخداد یک نمونه از عمل write در آن واحد مجاز میباشد. بنابراین پردازشهایی که خواهان عمل write هستند میبایست با استفاده از متدهای معمول lock مانند spinlock و یا mutexlock از انجام تنها یک نمونه در یک زمان مطمئن شوند.
نکته ۲: چناچه عمل write نیازمند خواندن اطلاعات بحرانی میباشد، این عملیات نیز میبایست در محدوده محافظت شده توسط lockها قرار بگیرد.


۲- طرز کار
عمل انتساب و خواندن اشاره‌گرها در لینوکس به صورت اتمیک میباشد. بنابراین اگر تصور کنیم که پردازشی در حال خواندن یک اشاره‌گر میباشد و پردازش دیگری در همان زمان در حال نوشتن در اشاره‌گر(محتوای اشاره‌گر) می‌باشد، به عنوان نمونه وضعیت q را در مثال زیر در نظر بگیرید:
struct me{
int a, b;
} *p, *q;

A: write:
/* to now, "q" has old value */
p = kmalloc(sizeof(struct me), GFP_KERNEL);
p->a = 1;
p->b=2;
q = p;

B: read:
kprintf("%d", q->a)

هنگامی که عمل خواندن انجام میشود احتمال بروز دو حالت وجود دارد:
  1. q با آدرس قدیمی بارگذاری گردد: نظر به اینکه مقدار q تغییر کرده است نکته ای که اینجا باید رعایت شود این است که حافظه قدیمی تا اتمام عملیات خواندن توسط پردازش B نباید آزاد (free) گردد.
  2. q با آدرس جدید بارگذاری شود: در اینصورت این تضمین باید وجود داشته باشد که فیلدهای a و b از حافظه هدف، مقدار واقعی گرفته باشند. احتمال دارد که متغیر q قبل از اینکه پارامترهای حافظه هدف (p) مقدار واقعی بگیرند با آدرس حافظه هدف بارگذاری گردد. دلیل آن نیز پس و پیش شدن عملیاتهای انتساب به دلیل انجام فرآیند بهینه سازی توسط کامپایلر میباشد.
باید گفت که در یک جمله RCU میبایست این دو شرط را را برآورده کند. از شرط دوم شروع میکنیم.

۲-۱: اطمینان از وجود مقادیر واقعی
جهت اطمینان ازمقدار دهی به اشاره‌گر بعد از مقدار دهی به پارامترهای آن، میبایست از تابعی به نام rcu_assign_pointer که این موضوع را تضمین میکند استفاده گردد. بنابراین کد انتساب به اینصورت تغییر خواهد کرد:
rcu_assign_pointer(q, p);

این عمل که میبایست در سمت نویسنده انجام شود با نام publish شناخته میشود (ذکر مجدد این نکته ضروری است که در صورتیکه بیش از یک نویسنده وجود دارد این قسمت باید با اتخاذ مکانیزم lock مناسب مدیریت شود تا در هر زمان حداکثر یک نویسنده فعال باشد)
در سمت خواننده نیز جهت اطمینان از صحت اطلاعات حافظه هدف، (بدان معنی که قبل از انجام عمل publish این حافظه مورد استفاده قرار نگیرد) از تابع rcu_dereference استفاده میشود. این تابع تضمین میکند که ارجاعات بعدی به حافظه هدف، به حافظه publish شده خواهد بود. اصطلاحا به این عمل نیز subscribe گفته میشود. کد خواننده به اینصورت تغییر خواهد کرد:
rcu_read_lock();
p=rcu_dereference(q)
kprintf("%d", p->a);
rcu_read_unlock();

توابع rcu_read_lock و rcu_read_unlock برای تعیین نواحی بحرانی پردازشهای خواننده به کار میروند که جهت برآورده سازی شرط دوم استفاده از آنها ضروری میباشد. این لاکها از نوع لاکهای متداول مانند spin و mutex نیستند و بنابراین به هیچ عنوان باعث بلاک شدن خواننده و همچنین مانع از بروز عملیات نوشتن همزان با فرآیندهای خواندن نمیشود.
نکته ۳: استفاده تودرتو از این لاکها باعث بروز بن بست نمیشود و محدودیتی در استفاده تودرتو از آنها وجود ندارد.

۲-۲: تشخیص زمان حذف حافظه قدیمی
شرط دوم ماندگاری حافظه قدیم تا اتمام عمیات خواندن از آن حافظه و به عبارتی زمان مناسب برای حذف حافظه قدیم، است. دلیل استفاده از rcu_read_lock و rcu_read_unlock به جهت تشخیص فعال بودن فرآیند خواندن در حافظه قدیمی میباشد.
نکته این است که تمامی فعالیتهای خواندنی که قبل از تغییر اشاره‌گر اتفاق افتاده‌اند و هنوز در منطقه بحرانی خود به سر میبرند بر روی نسخه قدیمی درحال کار میباشند و فعالیتهای خواندنی که بعد از عملیات نوشتن شروع شده اند با حافظه جدید سروکار دارند. بنابراین زمان مناسب برای آزاد سازی حافظه قدیمی زمانی خواهد بود که تمامی پردازشهای خواننده که قبل از عمل publish شروع شده اند از ناحیه بحرانی خود خارج شوند.
به شکل روبرو نگاه کنید. در این شکل حد فاصل از دور خارج شدن حافظه قدیمی (Removal) تا آزاد سازی آن (Reclamation) با نام Grace Period مشخص شده است.
چنانکه مشاهده میشود تنها فرآیندهای خواندنی که قبل از Grace Period شروع شده‌اند و به اتمام نرسیده‌اند برای تصمیم گیری زمان حذف دخیل میباشند و فرآیندهای که در خلال این زمان اجرا شده اند به دلیلی که در بالا آمد در تصمیم گیری موثر نیستند.
تشخیص این زمان بر عهده تابع synchronize_rcu میباشد. صدازدن این تابع و به دنبال آن اتمام تابع موید اتمام Grace Period میباشد در نتیجه کد نوشتن(write) به این صورت تغییر خواهد کرد:
spin_lock(&my_lock);
/* to now, "q" has old value */
temp = q;
p = kalloc(sizeof(struct me), GFP_KERNEL);
p->a = 1;
p->b=2;
rcu_assign_pointer(q, p);
spin_unlock(&my_lock);
synchronize_rcu()
kfree(temp);
RCU با نگهداری نسخه های متعدد از یک شیء بر مسئله همزمانی فائق می آید. به عبارتی در آن واحد این امکان وجود دارد که خواننده ها بر روی نسخه های متعدد یک شیء در حال فعالیت باشند.

۳- کلام آخر در قسمت اول
فانکشنهایی که در بالا ذکر شده اند به عنوان فانکشنهای Low Level مکانیزم RCU شناخته میشوند و معمولا به صورت مستقیم مورد استفاده قرار نمیگیرند بلکه از توابع High Level استفاده میشود. نمونه کارا از این توابع توابع مدیریت لیست پیوندی در کرنل میباشد که در مسیر /include/linux/rculist.h قابل مشاهد هستند.
معمولا مکانیزم RCU برای مواقعی استفاده میشود که میزان فرآيندهای خواندن خیلی بیشتر از میزان فرآيندهای نوشتنی باشند.

موفق باشید

منبع: http://lwn.net/Articles/262464/