برخی وسائل ممانعت دوجانبه را لازم داریم. یک روش، استفاده از "lock" است: هر تعداد از پردازشها میتوانند به طور همزمان برای بدست آوردن قفل تلاش نمایند، اما فقط یکی از آنها موفق میشود.
چگونه میتوانیم این کار را با استفاده از اسکریپتهای شل انجام بدهیم؟ بعضی افراد ایجاد یک فایل قفل، و کنترل وجود آنرا پیشنهاد میدهند:
# مثال قفل کردن -- اشتباه lockfile=/tmp/myscript.lock if [ -f "$lockfile" ] then # قفل قبلاً تصرف شده echo >&2 "cannot acquire lock, giving up: $lockfile" exit 0 else # هیچ کس مالک قفل نیست > "$lockfile" # ایجاد فایل قفل #...ادامه اسکریپت fi
این مثال کار نمیکند، زیرا این یک Race Condition میباشد: یک دریچه زمانی بین بررسی و ایجاد فایل در مدتی که سایر برنامهها ممکن است عمل کنند. فرض کنید دو پردازش در یک زمان در حال اجرای این کُد میباشند. هر دو وجود فایل قفل را بررسی میکنند، و هر دو نتیجه میگیرند که فایل وجود ندارد. حالا هر دو پردازش گمان میکنند که قفل را به دست آوردهاند -- ناکامی در شرف وقوع است. ما به یک روند بررسی و ایجاد اتمی(تجزیه ناپذیر) نیاز داریم ، و خوشبختانه یکی وجود دارد: mkdir، فرمان ایجاد یک دایرکتوری:
# مثال قفل کردن -- صحیح # Bourne lockdir=/tmp/myscript.lock if mkdir "$lockdir" then # دایرکتوری موجود نبود بنابراین با موفقیت ایجاد شد echo >&2 "successfully acquired lock: $lockdir" # ادامه اسکریپت else echo >&2 "cannot acquire lock, giving up on $lockdir" exit 0 fi
در اینجا حتی وقتی دو پردازش در یک زمان فرمان mkdir را احضار کنند، فقط حداکثر یک پردازش میتواند موفق گردد. این حالت اتمی بررسی و ایجاد در سطح هسته سیستم عامل تأمین میشود.
به جای استفاده از mkdir همچنین میتوانستیم برنامه ایجاد پیوند نمادین را به کار ببریم، ln -s. سومین امکان، داشتن برنامه حذفِ فایل قفلِ از قبل موجود با rm است. قفل با ایجاد مجدد فایل هنگام خروج آزاد میشود.
توجه نمایید که نمیتوانیم از mkdir -p برای ایجاد خودکار اجزا غایب تشکیل دهنده مسیر استفاده کنیم: اگر دایرکتوری از قبل موجود باشد mkdir -p خطایی صادر نمیکند، بلکه خصیصهایست که ما برای اطمینان از ممانعت دوطرفه به آن استناد میکنیم(مترجم: مقصود، ویژگی اتمی بودن mkdir است).
حالا بیایید خودکار نمودن حذف قفل هنگام خاتمه یافتن اسکریپت را چاشنی این مثال کنیم:
# POSIX (maybe Bourne?) lockdir=/tmp/myscript.lock if mkdir "$lockdir" then echo >&2 "successfully acquired lock" # حذف میشود lockdir ،وقتی اسکریپت به پایان میرسد یا موقع دریافت سیگنال trap 'rm -rf "$lockdir"' 0 # حذف دایرکتوری موقع پایان یافتن اسکریپت # به طور اختیاری فایلهای موقتی در این دایرکتوری ایجاد میشود، چون # .آنها به طور خودکار حذف خواهند شد tmpfile=$lockdir/filelist else echo >&2 "cannot acquire lock, giving up on $lockdir" exit 0 fi
این مثال خیلی بهتر است. بازهم این مشکل وجود دارد که وقتی اسکریپت بدون یک سیگنال اخذ شده،(یا سیگنال 9، SIGKILL) خاتمه مییابد، قفل کهنهای میتواند باقی بماند، یا میتواند توسط کاربری(به طور تصادفی یا بدخواهانه) ایجاد بشود، اما این قدم خوبی در جهت ممانعت دوطرفه است. Charles Duffy یک مثال همکارانه دارد که ممکن است مشکل «قفل کهنه» را برطرف نماید .
اگر در لینوکس هستید، میتوانید از کاربرد flock(1) نیز بهرهمند شوید. flock(1) توصیفگر فایلی را به یک فایل قفل متصل میکند. روشهای چندگانهای برای استفاده از آن موجود است، یک امکان حل مشکلِ نمونههای چندتایی، این است:
exec 9>/path/to/lock/file if ! flock -n 9 ; then echo "another instance is running"; exit 1 fi # اکنون این تحت قفل تا بسته شدن 9 اجرا میشود # (وقتی اسکریپت تمام میشود 9 به طور خودکار بسته میشود)
flock همچنین میتواند فقط قسمتی از اسکریپت شما را محافظت کند، برای اطلاعات بیشتر صفحه manرا ببینید.
من معتقدم استفاده از if (set -C; >$lockfile); then ... کاملاً مطمئن است، اگر که مطمئنتر نباشد. منبع Bash از open(filename, flags|O_EXCL, mode); استفاده میکند که تقریباً در تمام سکوها تجزیهناپذیر خواهد بود(به استثنای برخی نگارشهای NFS که در آنها mkdir هم نمیتواند اتمی باشد). من نه مسیر نشانههای متغیر را دنبال کردهام، که باید محتوی O_CREAT باشند، نه در پوستههای دیگر رسیدگی نمودهام. من استفاده از این را تا موقعی که شخص دیگری ادعای مرا پشتیبانی نکند، پیشنهاد نمیکنم. --Andy753421
شما اطمینان دارید که mkdir با اتمی شدن روی NFS مشکل دارد؟ من گمان میکنم فقط تحت تأثیر بازشدن واقع شده، اما واقعاً مطمئن نیستم. -- BeJonas 2008-07-24 01:22:59
برای مباحثه بیشتر در باره این موضوعات، مدیریت پردازش را ببینید.
پرسش و پاسخ 45 (آخرین ویرایش 2013-01-11 20:08:22 توسط GreyCat)