# برنامه نویسی سطح پایین > توسعه‌ی هسته‌ی سیستم عامل >  فرایندهای پدر و فرزند

## pswin.pooya

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

سوال من اینه که اگر فرایند پدر به هر دلیلی متوقف بشه فرایندهای فرزند هم باید متوقف بشن؟

اگر متوقف بشن:
فرض کنید برنامه اکسپلورر ویندوز در اثر یه باگ و یا اشتباه کاربر خاتمه پیدا کنه حالا باید همه برنامه هایی که پدرشون اکسپلورر هست متوقف بشن (مثل برنامه word که کاربر یه سند رو داخلش داشت تایپ می کرد) 

اگر خاتمه پیدا نکنند:
خب حالا فرض کنید که یه برنامه لینوکس سه چهار تا برنامه دیگه رو به عنوان فرزند ایجاد میکنه تا کاری رو براش انجام بدن (مثل برنامه گرفتن شماره تلفن - مسخره ترین مثال ممکن بود-‌) حالا اگر برنامه در اثر یه باگ متوقف بشه تکلیف فرزندها چیه؟ اگر فرزندها بمونن که بیخودی منابع مصرف میشه و بدتر از اون اینه که معلوم نیست پدرشون کیه و نتایج باید به کی ارسال بشه (همون حالت زامبی داخل لینوکس).

من خیلی دوست دارم نظرتون رو بدونم و اگر ایده ای هم دارید به من بگید تا اعمالش کنم.

----------


## Felony

هر 2 سناریو درسته ، از یه طرف اگر همه فرزندها بسته بشن ممکنه بعضی از زحمات کاربر به فنا بره و اطرف دیگه اگر به درستی مدیریت و تفکیک نشن منابع سیستم به فنا میره ... !

نمیدونم آراکس چقدر منعطف طراحی شده و آیا براتون امکان داره مکانیزمی به صورت زیر پیاده کنید :

با متوقف شدن پروسه والد پروسه های فرزندی که به پروسه والد پیغام میفرستادن و باهاش در ارتباط بودن آزاد بشن و پروسه هایی که فقط فرزند پروسه والد بودن و کاری باهاش نداشتن به حیاتشون ادامه بدن ، مثلا من دارم با Word کار میکنم و قرار هم نیست اطلاعاتی بین Word و Explorer تبادل بشه پس بسته شدن هر کدوم به دیگری آسیبی نمیرسونه پس با به وجود اومدن مشکل برای Explorer که پروسه والد هست فقط خودش باید آزاد بشه نه پروسه فرزند که کاری باهاش نداره ( در اینجا Word ) ؛ حالا یک پروسه دیگه که نیاز به ارتباط با Explorer ( پروسه والد ) رو داره در نظر میگیریم ( مثلا پروسه Indexing فایل ها ! ) خوب در این سناریو این پروسه قراره به Explorer اطلاعات Indexing فایل ها رو بده ، با بسته شدن Explorer به هر دلیل دیگه والدی وجود نداره تا اطلاعات بهش ارسال بشه پس پروسه فرزند الکی حافظه رو اشغال کرده و باید بسته بشه .

این سناریو دوم رو میتونید با اختصاص یک زمان Timeout پیاده کنید ، مثلا اگر پروسه والد به ازای دریافت هر پیغامی از پروسه فرزند در مدت زمان Timeout جوابی به اون پروسه فرزند نداد پروسه فرزند توسط Kernel آزاد بشه ، البته راه های دیگه ای هم به ذهنم میرسه که باید کمی روشون وقت گذاشت و امکان سنجیشون کرد .

در مورد پیاده سازی این سناریو هم ایده ای در ذهنم هست ، اگر سناریو به دردتون خورد و مایل بودید بگید تا توضیح بدم .

یا حق .

----------


## pswin.pooya

خب این ایده آل ترین حالت ممکنه که بتونیم تشخیص بدیم چی به درد میخوره و چی به درد نمی خوره. مساله اینه که چه شکلی این مورد باید تشخیص داده بشه. چیزی که به ذهن من میرسه اینه که خود فرایند پدر مشخص کنه که به فرایند فرزند نیازمند هست یا نه (از طریق ارسال یه پرچم تو زمان ایجاد فرایند فرزند.) اما مشکل به همین جا ختم نمیشه همنطور که گفتم مشکل فیلد ppid ساختار داده فرایند هم هست که شماره شناسایی فرایند پدر رو مشخص میکنه:

اگر زمان خاتمه فرایند پدر فرایند فرزند مقدار ppid فرایند پدرش رو بگیره احتمال این داره که منجر به یه رخنه امنیتی یا چیزی مثل اون بشه فکر کنید که فرایند توسط یه سرویس که دارای الویت بالا هست و توسط هسته ایجاد شده باشه (ppid صفر داشته باشه) حالا اگر فرایند پدر خاتمه پیدا کنه فرایند فرزند ppid اون رو میگیره و احتمال داره که کرنل اون رو اشتباه بگیره و یا با یه هک ساده فرایند به الویت بالاتر برسه. اگر ppid رو هم تغییر ندیم مقدار برگشتی فرایند فرزند باید به کی داده بشه؟! 

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

شاید بگید این موارد رو میشه به راحتی با چند تا if کنترل کرد اما مشکل اینجاست که همون if ها منجر به از دست رفتن انعطاف پذیری و مشکلات دیگه میشن.




> نمیدونم آراکس چقدر منعطف طراحی شده


آراکس میتونه جایزه تمشک طلایی توی طراحی OS رو بگیره  :بامزه:  خب مساله اینجاست که فعلا خبری از انعطاف نیست اما از اونجا که خودم دارم مینویسمش میتونم هر نوع تغییری رو داخلش ایجاد کنم.




> این سناریو دوم رو میتونید با اختصاص یک زمان Timeout پیاده کنید


فکر نکنم زمان معیار خوبی باشه چون ممکنه سر پردازنده بنا به هردلیلی شلوغ باشه و یا اصلا یک فرایند قرار باشه بعد از یک سال جواب بده. بهتره مواردی مثل dead line و ... رو خود فرایند پدر کنترل کنه. 




> در مورد پیاده سازی این سناریو هم ایده ای در ذهنم هست ، اگر سناریو به دردتون خورد و مایل بودید بگید تا توضیح بدم .


دوست عزیز من به شدت بدنبال ساختار شکنی داخل آراکس هستم و میخوام مشکلات رو حل کنم (البته در حد و توان خودم)‌ خیلی خوشحال میشم که هر ایده ای رو که دارید مطرح کنید شاید همون ایده بتونه پایه گذار یه مورد جدید باشه.

در پابان از همکاریتون کمال تشکر رو دارم.

----------


## Felony

من همچین چیزی در ذهنم بود :

Untitled.png
یک Relation Table که حاوی فیلدهای ProcessID , Parent ProcessID , Relation که Relation همون پرچمی هست که جناب عالی گفتی ؛ حالا با باز شدن هر پروسه مشخصات پروسه (ID پروسه ، ID پروسه والد ، پرچم ) به Relation Table اضافه میشه ، با هر تغییری در وضعیت پروسه ها روی رکوردهای موجود تو جدول Relation Table حرکت میکنیم و فیلد Parent Process ID اون ها رو بررسی میکنیم اگر در قید حیات بود که هیچ ، اگر نه Process ID متناظر از اون رکورد رو میخونیم که میشه پروسه فرزند و میبندیمش .

اون Flag هم مشخص میکنه که اگر Parent Process ID در قید حیات نبود آیا باید پروسه فرزند بسته بشه ( مثلا پروسه Indexing ) یا پروسه فرزند به والد وابسته نیست ( مثل Word Pad ) و نباید بسته بشه .

----------


## pswin.pooya

خب پیاده سازی این ایده کاملا عملی هست.  حالا فقط یه مشکل میمونه و اون هم فرایند پدر در صورت خاتمه پیدا کردن هست. با خاتمه بعضی از فرایندها همانند init تمام فرایندهای فرزند هم باید خاتمه ‍پیدا بکنن. مثلا اگر فرایند init شل سیستم رو related اعلام کنه و شل word رو غیر وابسته اعلام کنه. تکلیف چیه؟ تو حالت اول با خاتمه init شل خاتمه پیدا میکنه اما با خاتمه شل word روی هوا معلق میمونه. بدتر از همه هم اینه که word با مجوز کاربری داره کار میکنه که اعتبار نداره. همین میتونه پایه گذار یه کرم باشه:

کرم میاد و کاری میکنه که init کرش کنه حالا خیلی راحت میتونه بدون اینکه هیچ فرایند و کس دیگه ای بتونه بهش دسترسی داشته باشه فعالیت کنه. و از اونم بدتر اینه که زمانی که مدیر میخواد پروسسها رو لیست کنه متوجه فرایندی میشه که تعلق به کاربر خاصی نداره و یا اینکه خود برنامه ای که لیست میکنه زمان گرفتن نام کاربر برنامه کرم کرش کنه.

----------


## Felony

درسته ، باید سناریو رو به صورت زیر تغییر داد :

Senario.PNG
کاربرد فیلد های جدول بالا :

- Process ID               : پروسه جاری
- Reference Count      : تعداد فرزندان
- Parent process ID     : پروسه پدر
- Relation                   : رابطه بین این پروسه و پدرش ؟ ( True \ False )
- Terminate All Child   : بسته شدن تمام فرزندان این پروسه در هنگام بسته شدن این پروسه

حالا اون مشکلاتی که گفتید رو میشه حل کرد ، اگر پروسه ای مثل init ایجاد بشه کافیه که فیلد Terminate All Child رو با  True مقدار دهی کنه ، حالا هر وقت پروسه ای تغییر پیدا کرد و ما جدول رو بررسی کردیم فیلد Terminarte All Child رو بررسی میکنیم و اگر True بود به این معنی هست که تمام پروسه های فرزند باید بسته بشن ، پس پروسه های فرزند رو میبندیم ولی نباید از لیست حذف بشن ( دلیلش رو پایین میگم ) .

دلیل این که نباید از لیست حذف بشن این هست که نباید رابطه بین پروسه ها در جدول از بین بره تا بشه در هنگام مورد نظر همه پروسه ها رو بررسی کرد و به فرزندان ، فرزندان ... یک پروسه و به پدران ، پدران ... یک پروسه دسترسی داشت که این جدول همچین نیازی رو براورده میکنه .

حالا شاید بگید اگر بعد از بسته شدن پروسه از جدول حذفش نکنیم بعد از مدتی تعداد رکوردها بالا میره و حافظه رو الکی اشغال میکنه ، برای این مورد هم تو این سناریو راه حلی در نظر گرفتم ، اون فیلد Reference Count مشخص میکنه چند تا پروسه فرزند این پروسه هستند ( فرزند این پروسه - فرزند ، فرزند این پروسه - فرزند ، فرزند ، فرزند این پروسه و ... ) ؛ حالا هر باری که جدول رو بررسی میکنیم فیلد Reference Count رو هم بررسی میکنیم ، اگر مقدارش 0 بود یعنی هیچ فرزندی نداره و میتونیم تمام اون رکوردهایی که با این Relation در ارتباط بودند ( فرزندهای این پروسه ) رو از جدول حذف کنیم تا با افزونگی داده ها رو به رو نشیم .

هر پروسه ای که از طریق یه پروسه دیگه اجرا بشه ( والد داشته باشه ) باید مقدار فیلد Reference Count پروسه های والد یک واحد اضافه بشه و هر پروسه ای هم که به هر دلیل بسته شد باید مقدار Reference Count تمام پروسه های پدر یک واحد کم بشه .

اگر جاییش واضح نبود بگید بیشتر توضیح بدم .

----------


## eshpilen

اتفاقا بنده تازگی چیزایی در این ارتباط خونده بودم. ولی متاسفانه جزییاتش رو به خوبی بیاد ندارم.
ولی اجالتا از این مقالات شاید به اطلاعاتی برسید:

http://en.wikipedia.org/wiki/Child_process
http://en.wikipedia.org/wiki/Linux_boot_process
http://en.wikipedia.org/wiki/Orphan_process

منکه همینطور میرم توی این مقالات ویکیپدیا یکی باز میکنم از لینکها و سرنخ های همون میرسم به چنتای دیگه و نهایتا میرسم به 10 تا و 20 تا و حتی بیشتر مقاله و اصلا یادم میره از کجا به هرکدوم رسیدم. خودم دوباره میخوام پیداشون کنم یادم نمیاد. ولی واقعا جالبه، بخصوص دنیای لینوکس و اینا. خط فرمانش منو کشته. سیستم سیگنال هاش چقدر جالبن. سیستم فرایندهای فرزند و والد هم همینطور. سیستم بوت و init. همه چیش جالب و خیلی حساب شده و منعطف هست.

تمام اطلاعاتی که میخواید احتمالا یه جاهایی همون ورا حاضر و آماده هستن.
چرا نگاه نمیکنید مثلا لینوکس چکار میکنه و چرا؟ نمیدونید از دوتا بروبچ لینوکسی و خارجکی و اینا بپرسید خب. مثلا در فرومهای تخصصی لینوکس.
اگر لینوکس یه کاری کرده، پس 99% همون درسته.
بعدم جلوی همه چیز رو نمیشه گرفت و خیلی وقتا باید Compromise کرد.
مثلا میدونید که در سیستم عامل نمیشه جلوی تمام Deadlock ها رو گرفت (این کار غیرممکن هست یا حداقل بسیار دشوار - فکر کنم یه جایی خوندم یا شنیدم که از نظر تئوریک ثابت شده که غیرممکنه).

----------


## pswin.pooya

> مثلا میدونید که در سیستم عامل نمیشه جلوی تمام Deadlock ها رو گرفت (این  کار غیرممکن هست یا حداقل بسیار دشوار - فکر کنم یه جایی خوندم یا شنیدم که  از نظر تئوریک ثابت شده که غیرممکنه).


کی گفته غیر ممکنه. الگوریتمهایی وجود دارن که میتونن جلوگیری بکنن. مشکل اینجاست که با توجه به اینکه امکان deadlock مخصوصا توی osهای جدید خیلی کم هست ارزش تحمل هزینه ها رو نداره به غیر از اون بن بست یک مشکل زیاد حاد نیست و با یه ریست حل میشه. اما مشکلی که من دارم میگم امکان داره از لحاظ امیتی مورد ساز باشه.

----------


## pswin.pooya

> چرا نگاه نمیکنید مثلا لینوکس چکار میکنه و چرا؟ نمیدونید از دوتا بروبچ  لینوکسی و خارجکی و اینا بپرسید خب. مثلا در فرومهای تخصصی لینوکس.
> اگر لینوکس یه کاری کرده، پس 99% همون درسته.


یه زمانی روی دیوایس درایورهای لینوکس کار می کردم و داخل هسته دست می بردم و به خاطر آراکس کتابهاشو می خوندم. امروز داشتم کد چند برنامه گی رو میزدم و تصمیم گرفتم قبل از هر کاری نخ های هسته رو اضافه کنم. برای همین هم ترجمه هایی رو که قبلا کرده بودم (هر متن اصلی رو که میخونم یاداشتش میکنم) رو شروع کردم خوندم و جالبه که یه جواب نسبی برای سوالم پیدا کرد. متن زیر دقیقا برگرفته از ترجمه من هست:

نخ های هسته با فراخوانی daemonize() آغاز می گردند که آن عمیات مقدار دهی و تغییر پدر به kthreadd  را انجام می دهد. هر نخ در لینوکس دارای یک پدر است که اگر نخ پدر پیش از آنکه فرزندش به خاتمه یابد بمیرد. فرزند بصورت zombie (زامبی) در می آید و منابع سیستم و پردازنده را به هدر می دهد. تعویض پدر به kthreadd از این عمل جلوگیری می کند و اطمینان می دهد که تمام منابع سیستم پاک شوند.

البته این جواب نیست اما نشون میده که خود یونیکس و لینوکس هم با این مساله درگیر هستند. توی این مورد یا نخها که میشه از پروسسها هم به عنوان نخ یاد کرد لینوکس متوصل به یک پروسس دیگه میشه و نخهایی رو که پدرشون از بین رفته رو به یک پروسس دیگه واگذار میکنه و اجازه فعالیت به اونها رو میده. توی پاراگراف بالا منظورش از زامبی وضعیت زامبی فرایندهای لینوکس هست که داخل این وضعیت فرایند فرزند خاتمه پیدا میکنه بدون اینکه فرایند پدر با دستور wait منتظر جواب ارسالی از اون باشه. زامبی شدن یک فرایند نشونه باگ برنامه نویسی (کدینگ بد) یا ایتکه مشکل سیستمی هست که پدر به نحوی با اون درگیر بوده.

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

----------


## eshpilen

> کی گفته غیر ممکنه. الگوریتمهایی وجود دارن که میتونن جلوگیری بکنن. مشکل اینجاست که با توجه به اینکه امکان deadlock مخصوصا توی osهای جدید خیلی کم هست ارزش تحمل هزینه ها رو نداره به غیر از اون بن بست یک مشکل زیاد حاد نیست و با یه ریست حل میشه. اما مشکلی که من دارم میگم امکان داره از لحاظ امیتی مورد ساز باشه.


 بله الگوریتم هایی هست، اما اینکه تمام انواع deadlock در هر شرایطی در کل یک سیستم اجتناب بشن فکر میکنم ممکن نیست یا حداقل بسیار دشواره (و خودش منابع زیادی مصرف میکنه).
بنظرم قبلا در درسهای دانشگاهی مربوطه از استاد شنیدم که جلوگیری مطلق از deadlock ممکن نیست.
در مقالهء ویکیپدیا درمورد deadlock هم جستجو کردم و عباراتی ظاهرا به همین معنا درش وجود داره. بطور مثال:

However, for many systems it is impossible to know in advance what every  process will request. This means that deadlock avoidance is often  impossible.
...
Removing the mutual exclusion condition means that no process may have  exclusive access to a resource. This proves impossible for resources  that cannot be spooled, and even with spooled resources deadlock could still occur. 
...
A "no preemption"  (lockout) condition may also be difficult or impossible to avoid as a  process has to be able to have a resource for a certain amount of time,  or the processing outcome may be inconsistent or thrashing may occur.

البته کاملا مطمئن نیستم که این گفته ها کلی باشن و بخاطر همین باید مقاله رو کامل بخونم شاید چیز بیشتری فهمیدم.

----------


## pswin.pooya

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

----------

