File Descriptor
های باز هم در جریان پایان Process بسته میشن
ولی این به معنی این نیست که خود Resource حتماً همون لحظه نابود میشه
مثلا ممکنه یه File توسط Process دیگه هم باز باشه
پس File Descriptor با خود Resource یکی نیست
این موضوع رو بعدا توی Linux Internals خیلی بیشتر میبینیم
یه نکته جالب درباره return و exit
مثلا اگه بنویسیم:
int main() {
return 0;
}
وقتی main تموم بشه برنامه هم به شکل عادی به پایان میرسه
اما اگه بنویسیم
exit(0);
اینجا مستقیما درخواست پایان برنامه داده شده
برای همین توی برنامه های ساده ممکنه نتیجه هر دو تقریبا یکی به نظر برسه
ولی از نظر مسیر اجرای برنامه دقیقا یکی نیستن
یه تفاوت جالب دیگه هم داریم
exit
با exit_ یکی نیست
توی C تابع exit قبل از پایان برنامه میتونه بعضی Cleanupهای مربوط به User Space رو انجام بده
مثلا Handlerهایی که با atexit ثبت شدن رو اجرا کنه
یا Bufferهای stdio رو Flush کنه
ولی exit_ این Cleanup های User Space رو انجام نمیده و مستقیم تر Process رو terminate میکنه
این تفاوت وقتی وارد بحث fork و Buffering و System Call بشیم خیلی مهم میشه
یه نکته دیگه هم اینه که Process فقط با exit تموم نمیشه
ممکنه Process در اثر Signal هم terminate بشه
پس داستان کلی میتونه این شکلی باشه
Process
│
┌─────────┴─────────┐
│ │
exit() Signal
│ │
└─────────┬─────────┘
▼
Termination
│
▼
Exit Information
│
▼
Zombie
│
wait()
│
▼
Reaped
حالا بریم سمت Reverse Engineering
فرض کنید داری یه Binary رو بررسی میکنید
توی Trace میبینید یه اتفاقاتی شبیه این افتاده
fork()
↓
exec()
↓
Program Execution
↓
exit()
حالا دیگه میتونید یه تصویر ذهنی از چیزی که اتفاق افتاده داشته باشید
Process جدید ساخته شد
↓
Child شروع به اجرا کرد
↓
Program جدید اجرا شد
↓
Program کار خودش رو انجام داد
↓
Process تموم شد
اگه بعدش Parent رو ببینی که wait میکنه
Child
│
▼
exit()
│
▼
Zombie
│
▼
Parent
│
wait()
│
▼
Exit Status
│
▼
Reaping
میتونید بفهمید که Parent احتمالا منتظر نتیجه اجرای Child بوده
اینجاست که چیزایی که تا الان یاد گرفتیم کم کم دارن به درد Reverse Engineering میخورن
حالا یه تصویر کامل تر از چیزایی که تا اینجا یاد گرفتیم داشته باشید
Parent Process
│
fork()
│
▼
Child Process
│
exec()
│
▼
Program
│
▼
Execution
│
exit()
│
▼
Termination
│
▼
Zombie
│
wait()
│
▼
Reaping
exit
خیلی ساده یعنی
Process
میگه کار من تموم شد
بعد از اون
Process
↓
Termination
↓
Exit Status
↓
Parent با wait وضعیت رو میگیره
↓
Reaping
↓
Zombie از بین میره
پس تا اینجا چهار مفهوم خیلی مهم Process API رو داریم
fork()
↓
ساخت Child Process
exec()
↓
اجرای Program جدید داخل Process
wait()
↓
منتظر موندن یا گرفتن وضعیت Child
exit()
↓
تموم کردن اجرای Process
این چهار مفهوم رو خوب یاد بگیرید
چون وقتی بعدا وارد Linux Internals و System Call ها و Reverse Engineering باینری های ELF بشیم دوباره بارها به همین مفاهیم برمیخوریم
فقط یه نکته رو همیشه یادتون باشه
ما اینجا داریم مدل Unix و Linux رو دنبال میکنیم
جزئیات داخلی Process و PCB و Process Descriptor ممکنه توی سیستم عامل های مختلف فرق داشته باشهن
فعلا همین مدل رو مدل اصلیمون قرار میدیم
@reverseengine