دستور fork در لینوکس برای ایجاد child process

Fork in linux

در سیستم عامل به هر برنامه ی در حال اجرا یک process می گویند. فرض کنید که برنامه ی شما در حال اجرا شدن است و می خواهید یک پروسه ی جدید را توسط برنامه ی در جال اجرایتان ایجاد کنید، برای این کار در لینوکس از دستور fork می توان استفاده کرد، دستور fork دقیقا یک کپی از برنامه در حال اجرایی که آن را فرامی خواند ایجاد می کند ولی با این تفاوت که کپی ایجاد شده  یک فضای جدید در حافظه می گیرد. وقتی می گوییم یک کپی از process ایجاد می شود یعنی همه چیز به طور کامل کپی می شود، محتوای متغییر ها ، کد برنامه و … . بعد از اجرای دستور fork برنامه ی در حال اجرا که fork را فراخوانده و همین طور پروسس کپی شده ی جدید هر دو، خط بعد از fork را اجرا می کنند. همین طور process پدر و فرزند (کپی ایجاد شده از پدر) هیچ گونه ارتباطی با هم دیگر ندارند در واقع به عبارتی دیگر تاثیری روی هم نمی گذارند چون فضای حافظه ی آن ها جداست. همین طور هر دو پروسس ایجاد شده به صورت هم زمان اجرا می شوند.

برنامه ی زیر یک برنامه ی ساده ی سی پلاس پلاس است که یک متغییر را که مقدارش برابر با ۵ است را چاپ می کند:

خروجی برنامه برابر با

می شود. حالا از دستور fork استفاده می کنیم و آن را به برنامه اضافه می کنیم:

 

برنامه اجرا می شود و در اولین خط متغییر i برابر با ۵ قرار می شود. در خط بعد کلمه ی Hello! بر روی صفحه ی نمایش چاپ می شود تا اینجا همه چیز عادی است. در خط بعدی برنامه از دستور fork استفاده شده است، وقتی که دستور fork اجرا می شود یک پروسس جدید از روی همین پروسس (پروسس پدر) ایجاد می شود که به آن پروسس فرزند می گوییم. که پروسس فرزند کاملا کپی پروسس پدر است که یعنی کد، مقدار متغییر ها، مقادیر رجیستر های پردازنده و …. برای هر دو یکسان است با این تفاوت که پروسس جدید در یک جای متفاوت حافظه ایجاد می شود و ارتباطی با پروسس پدر ندارد و از نظر حافظه مستقل است.

بعد از اجرا fork هر دو پروسس پدر و فرزند خط بعد از دستور fork را اجرا می کنند. از آن جایی که هر دو پروسس کاملا کپی هم هستند مقدار متغییر i در هر دو پنج است. دستور بعدی که توسط هر دو پروسس اجرا می شود دستور پرینت مقدار  i بر روی صفحه ی نمایش است، هر کدام از آن ها با رسیدن به دستور پرینت مقدار i را چاپ می کنند در نتیجه خروجی برنامه به شکل زیر خواهد بود:

اگر هنوز کارکرد fork نامفهوم است می توانید با مقایسه دو کد بالا متوجه ی نحوه ی کارکرد fork شوید.

ولی این مدل استفاده از fork کارکرد خاصی ندارد. وقتی fork و ایجاد پروسه ی جدید به درد می خورد که بتوان بعد از اجرای fork دو چیز متفاوت را اجرا کرد و نه این که دقیقا همان کد قبلی را اجرا کرد! برای اینکه بتوان بعد اجرای fork  دو چیز متفاوت را اجرا کرد یک راه بیشتر نداریم و استفاده از خروجی دستور fork است. دستور fork  در پروسس پدر و پروسس فرزند دو خروجی متفاوت ایجاد می کند.  هر پروسس در سیستم عامل یک شماره ی مخصوص به خود دارد که سیستم عامل برای شناسایی و کار با پروسس ها از آن استفاده می کند که به آن process id یا PID می گویند. در پروسس پدر خروجی fork شناسه ی (PID) پروسس فرزند است در حالی که خروجی fork در پروسس جدید ایجاد شده برایر با صفر است به این ترتیب با استفاده از تفاوت خروجی fork در پروسس فرزندو پدر می توان کاری کرد که پروسس فرزند و پروسس پدر بعد از دستور fork کارهای متفاوتی کنند که در ادامه مثالی از این مورد می بینیم. اگر خروجی fork  یک عدد منفی بود یعنی برنامه موفق به ایجاد یک پروسس جدید نشده است.

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

pid_t استراکتی است که برای نشان دادن شناسه ی پروسس ها از آن استفاده می کنیم، pid یک متغییر از جنس pid_t است که شناسه ی پروسه ی فرزندی است که پایان یافته و status نشان می دهد با چه عددی پروسس فرزند خروج پیدا کرده است.

استفاده از wait و مطمین شدن از این که پروسس فرزند زود تر از پروسس پدر پایان می یابد ضروری است اگر نه برنامه به احتمال زیاد دچار مشکل می شود.

 

حالا برویم سراغ کدی که با توجه به خروجی fork دو رفتار متفاوت در پروسس پدر و فرزند انجام می دهد. ابتدا به کد زیر توجه کنید:

 

 

برنامه تا fork اجرا می شود وقتی به fork رسید دو پروسس ایجاد می شود که مقدار pid در پروسس پدر یک عدد صحیح بزرگ تر از ۰ می شود و در پروسس فرزند pid برابر با ۰ می شود. بر همین اساس با استفاده از if مشخص می کنیم پروسس پدر و فرزند چه کارهایی را انجام بدهند. در پروسس فرزند یک شمارنده وجود دارد که اعداد منفی صد تا صفر را چاپ می کند و در پروسس پدر یک شمارنده وجود دارد که اعداد صفر تا صد را چاپ می کند در آخر پروسس پدر از تابع wait استفاده کرده ایم که باعث می شود در صورتی که پروسس فرزند تمام نشده باشد منتظر بماند تا تمام شود.در تصویر زیر نمونه ای از خروجی برنامه را مشاهده می کنید که اگر دقت کنید می بینید اعداد مثبت و منفی با هم چاپ شده اند که نشان دهنده ی اجرا شدن دو پروسس به صورت همروند است.

 

Screenshot from 2016-03-27 22-00-54

 

یک کار مهم دیگری هم که می توان انجام داد این است که به جای اینکه در پروسس فرزند کد متفاوتی که خودمان نوشته ایم اجرا شود می توان کاری کرد که یک برنامه ی دیگر که به صورت فایل اجرایی است اجرا شود برای این کار می توان از دستورات متفاوتی استفاده کرد که یکی از  آن ها دستورات دستور execvp است. این دستور کل فضای حافظه ی پروسس را پاک می کند و در آن برنامه ی جدیدی که در execvp  آمده است ریخته می شود و هیچ اثری از چیزهای قبلی از جمله کد و داده هایی که در آن بوده نمی ماند. این تابع نام آن فایل اجرای و آرگمان هایی که باید به برنامه پاس داده شود را می گیرد و آن را در فضای حافظه ی پروسس جدید بارگذاری می کند و آن را اجرا می کند.

در کد زیر یک نمونه از استفاده ی execvp  را می بینید که در آن برنامه ی ls لینوکس را اجرا می کنیم که فایل های موجود در یک  دایرکتوری را می دهند و همین طور پروسس پدر اعداد یک تا نود و نه را چاپ می کند:

 

نمونه ای از خروجی برنامه ی زیر را می بینید:

 

Screenshot from 2016-03-27 22-24-47

 

چند نمونه از کاربرد های دستور fork :

  • در command line وقتی فرمانی تایپ می کنیم و اینتر را می زنیم تا این که برنامه اجرا شود، در واقع command line برای آن برنامه با استفاده از fork یک پروسس ایجاد می کند و آن برنامه را اجرا می کند به همین دلیل می توانید با fork و execvp یک command line با ویژگی های مد نظر خود برای خودتان بسازید.
  • در کروم زمانی که Tab جدیدی باز می کنید در واقع یک پروسس جدید را توسط fork ایجاد کرده اید.
  • خیلی از زبان های اسکریپتی به طور غیر مستقیم از fork استفاده می کنند تا بعضی از دستورات را اجرا کنند.
  • برای همروند کردن و موازی سازی یک کار می توان از آن ها استفاده کرد.

 

 

Digg This
Reddit This
Stumble Now!
Buzz This
Vote on DZone
Share on Facebook
Bookmark this on Delicious
Kick It on DotNetKicks.com
Shout it
Share on LinkedIn
Bookmark this on Technorati
Post on Twitter
Google Buzz (aka. Google Reader)

۱۰ دیدگاه

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *