مقدمه ای بر سیستم عامل ها
هی هابر! من می خواهم توجه شما را به مجموعه ای از مقالات - ترجمه های یک ادبیات جالب به نظر من - OSTEP جلب کنم. این مطالب عمیقاً کار سیستمهای عامل یونیکس مانند را مورد بحث قرار میدهد، یعنی کار با فرآیندها، زمانبندیهای مختلف، حافظه و سایر اجزای مشابه که یک سیستمعامل مدرن را تشکیل میدهند. شما می توانید اصل تمام مواد را اینجا ببینید
کارهای آزمایشگاهی در مورد این موضوع را می توان در اینجا یافت:
قسمت های دیگر:
شما همچنین می توانید از کانال من دیدن کنید
زنگ خطر. هشدار! یک آزمایشگاه برای این سخنرانی وجود دارد! نگاه کن
API پردازش
بیایید به مثالی از ایجاد یک فرآیند در یک سیستم یونیکس نگاه کنیم. از طریق دو تماس سیستمی اتفاق می افتد چنگال() и exec().
فراخوانی ()
برنامه ای را در نظر بگیرید که فراخوانی fork() را ایجاد می کند. نتیجه اجرای آن به شرح زیر خواهد بود.
ابتدا تابع main() را وارد کرده و رشته را روی صفحه چاپ می کنیم. خط حاوی شناسه فرآیند است که در اصل نامیده می شود PID یا شناسه فرآیند این شناسه در یونیکس برای اشاره به یک فرآیند استفاده می شود. دستور بعدی fork() را فراخوانی می کند. در این مرحله، یک کپی تقریباً دقیق از فرآیند ایجاد می شود. برای سیستم عامل، به نظر می رسد که 2 نسخه از همان برنامه در حال اجرا در سیستم وجود دارد که به نوبه خود از تابع fork() خارج می شود. فرآیند فرزند تازه ایجاد شده (در رابطه با فرآیند والد که آن را ایجاد کرده است) دیگر اجرا نخواهد شد و از تابع main() شروع می شود. باید به خاطر داشت که فرآیند فرزند کپی دقیقی از فرآیند والد نیست؛ به ویژه، فضای آدرس، رجیسترهای خاص خود، اشارهگر خود به دستورالعملهای اجرایی و موارد مشابه دارد. بنابراین، مقدار بازگشتی به فراخوان کننده تابع fork() متفاوت خواهد بود. به طور خاص، فرآیند والد مقدار PID پردازش فرزند را به عنوان بازگشت دریافت میکند، و فرزند مقداری برابر با 0 دریافت میکند. با استفاده از این کدهای بازگشتی، میتوانید فرآیندها را جدا کرده و هر یک از آنها را مجبور به انجام کار خود کنید. . با این حال، اجرای این برنامه به طور دقیق تعریف نشده است. پس از تقسیم به 2 فرآیند، سیستم عامل شروع به نظارت بر آنها و همچنین برنامه ریزی کار آنها می کند. اگر روی یک پردازنده تک هسته ای اجرا شود، یکی از فرآیندها، در این مورد والد، به کار خود ادامه می دهد و سپس پردازش فرزند کنترل را دریافت می کند. هنگام راه اندازی مجدد، وضعیت ممکن است متفاوت باشد.
انتظار تماس ()
برنامه زیر را در نظر بگیرید. در این برنامه به دلیل وجود تماس صبر کن() فرآیند والد همیشه منتظر می ماند تا فرآیند فرزند تکمیل شود. در این حالت، یک خروجی متن کاملاً تعریف شده روی صفحه نمایش خواهیم داشت
exec() فراخوانی کنید
چالش را در نظر بگیرید exec(). این فراخوانی سیستم زمانی مفید است که بخواهیم یک برنامه کاملا متفاوت را اجرا کنیم. در اینجا ما تماس خواهیم گرفت execvp() برای اجرای برنامه wc که یک برنامه کلمه شمار است. با فراخوانی exec() چه اتفاقی می افتد؟ این فراخوانی نام فایل اجرایی و برخی پارامترها را به عنوان آرگومان ارسال می کند. پس از آن کد و داده های استاتیک از این فایل اجرایی بارگذاری می شود و بخش خود با کد بازنویسی می شود. نواحی حافظه باقیمانده، مانند پشته و پشته، مجدداً مقداردهی اولیه می شوند. پس از آن سیستم عامل به سادگی برنامه را اجرا می کند و مجموعه ای از آرگومان ها را به آن ارسال می کند. بنابراین ما یک فرآیند جدید ایجاد نکردیم، ما به سادگی برنامه در حال اجرا را به برنامه در حال اجرا دیگری تبدیل کردیم. پس از اجرای فراخوانی exec() در نزول، به نظر می رسد که برنامه اصلی اصلا اجرا نشده است.
این پیچیدگی راه اندازی برای یک پوسته یونیکس کاملاً عادی است و به پوسته اجازه می دهد تا پس از فراخوانی کد را اجرا کند. چنگال()، اما قبل از تماس exec(). نمونه ای از این کدها، تنظیم محیط پوسته با نیازهای برنامه در حال راه اندازی، قبل از راه اندازی آن است.
صدف - فقط یک برنامه کاربر او خط دعوت را به شما نشان می دهد و منتظر می ماند تا چیزی در آن بنویسید. در بیشتر موارد، اگر نام برنامه ای را در آنجا بنویسید، پوسته مکان آن را پیدا می کند، متد fork() را فراخوانی می کند و سپس نوعی از exec() را فراخوانی می کند تا یک فرآیند جدید ایجاد شود و منتظر بمانید تا با استفاده از یک فرآیند کامل شود. انتظار () تماس بگیرید. هنگامی که پردازش فرزند خارج می شود، پوسته از فراخوانی ()watch برمی گردد و دوباره دستور را چاپ می کند و منتظر می ماند تا دستور بعدی وارد شود.
تقسیم fork() و exec() به پوسته اجازه می دهد کارهای زیر را انجام دهد، برای مثال:
فایل wc > new_file.
در این مثال، خروجی برنامه wc به یک فایل هدایت می شود. روشی که پوسته به این امر دست می یابد بسیار ساده است - با ایجاد یک پردازش فرزند قبل از فراخوانی exec()، پوسته خروجی استاندارد را می بندد و فایل را باز می کند new_file، بنابراین، همه خروجی از برنامه در حال اجرا بیشتر است wc به جای صفحه نمایش به یک فایل هدایت می شود.
لوله یونیکس به روشی مشابه پیاده سازی می شوند، با این تفاوت که از فراخوانی pipe() استفاده می کنند. در این حالت جریان خروجی فرآیند به صف لوله ای که در هسته قرار دارد وصل می شود که جریان ورودی یک فرآیند دیگر به آن متصل می شود.
منبع: www.habr.com