از کنترل مستقیم سختافزار تا تولد کرنل
امروزه روشن کردن یک رایانه و اجرای همزمان چندین نرمافزار، عملی کاملاً عادی به نظر میرسد.
کاربر مرورگر وب را اجرا میکند، همزمان موسیقی پخش میشود، یک فایل در حال دانلود است و شاید یک ماشین مجازی نیز در پسزمینه مشغول اجرا باشد. تمامی این فعالیتها بدون آنکه کاربر درگیر مدیریت پردازنده، حافظه یا تجهیزات سختافزاری شود انجام میشوند.
این تجربه باعث شده است بسیاری تصور کنند که رایانه ذاتاً توانایی انجام چنین وظایفی را دارد. اما حقیقت آن است که سختافزار هیچ درکی از «برنامه»، «کاربر»، «اولویت»، «امنیت» یا «چندوظیفگی» ندارد.
پردازنده تنها دنبالهای از دستورالعملهای ماشین را اجرا میکند.
حافظه تنها بیتها را ذخیره میکند. دیسک صرفاً بلوکهایی از داده را میخواند و مینویسد.
هیچیک از این اجزا نمیدانند کدام برنامه باید زودتر اجرا شود؛
کدام فرایند اجازه دسترسی به حافظه را دارد یا اگر دو برنامه همزمان بخواهند از یک چاپگر استفاده کنند چه باید کرد.
درک این موضوع، نقطه آغاز مطالعه معماری سیستمعامل است.
برای فهم اینکه «کرنل چیست»، ابتدا باید بدانیم چه مشکلاتی وجود داشت که انسان را به طراحی چنین سازوکاری واداشت.
فصل اول ( بخش اول)
در نخستین نسل رایانههای الکترونیکی، چیزی به نام سیستمعامل وجود نداشت.
هر برنامه مستقیماً روی سختافزار اجرا میشد و تمام مسئولیت کنترل سیستم بر عهده برنامهنویس بود.
در چنین سامانهای، اجرای یک برنامه تقریباً به این معنا بود که کل رایانه در اختیار همان برنامه قرار گیرد.
به بیان ساده، معماری به شکل زیر بود:
+----------------------+ | Program | +----------------------+ | Hardware | +----------------------+
هیچ واسطهای میان نرمافزار و سختافزار وجود نداشت.
اگر برنامه نیاز داشت دادهای از حافظه بخواند، باید آدرس دقیق آن را میدانست.
اگر لازم بود از چاپگر استفاده کند، باید مستقیماً رجیسترهای سختافزار چاپگر را کنترل میکرد.
اگر برنامهای اشتباه میکرد، هیچ مکانیزمی برای جلوگیری از آسیب به کل سیستم وجود نداشت.
در واقع، برنامه نهتنها مسئول انجام محاسبات خود بود، بلکه باید نقش مدیریت کل رایانه را نیز بر عهده میگرفت.
در دهههای ۱۹۴۰ و اوایل ۱۹۵۰، برنامهنویسی با آنچه امروز میشناسیم تفاوت بنیادین داشت.
امروزه یک برنامهنویس میتواند تنها با چند خط کد، فایلی را باز کند یا اطلاعاتی را از شبکه دریافت کند.
اما در نخستین رایانهها، چنین مفاهیمی وجود نداشت.
برنامهنویس باید موارد زیر را شخصاً مدیریت میکرد:
محل قرارگیری برنامه در حافظه
آدرس دقیق دادهها
نحوه انتقال اطلاعات بین حافظه و پردازنده
کنترل تجهیزات ورودی و خروجی
ترتیب اجرای دستورالعملها
مدیریت خطاهای سختافزاری
به همین دلیل، توسعه حتی سادهترین برنامهها نیز فرآیندی زمانبر و مستعد خطا بود.
نبود یک لایه مدیریتی میان برنامه و سختافزار مشکلات متعددی ایجاد میکرد که برخی از مهمترین آنها عبارت بودند از:
الف) استفاده ناکارآمد از پردازنده
پردازنده میلیونها دستور را در هر ثانیه اجرا میکرد، اما تجهیزات ورودی و خروجی بسیار کندتر بودند.
فرض کنید برنامهای قصد خواندن یک کارت پانچ یا نوشتن روی یک نوار مغناطیسی را داشت.
در زمان انتظار برای تکمیل عملیات، پردازنده عملاً بیکار میماند، زیرا هیچ برنامه دیگری برای اجرا وجود نداشت.
در نتیجه، گرانترین بخش رایانه، یعنی CPU، بخش بزرگی از زمان خود را بدون انجام کار مفید سپری میکرد.
ب) نبود حفاظت از حافظه
تمام حافظه متعلق به همان برنامه در حال اجرا بود.
اگر برنامه به اشتباه در آدرسی نامعتبر مینوشت، ممکن بود:
دادههای خود را تخریب کند.
بخشی از کد اجرایی را بازنویسی کند.
موجب توقف کامل سیستم شود.
در آن زمان، مفهومی به نام Memory Protection هنوز وجود نداشت.
ج) نبود اشتراک منابع
اگر یک چاپگر به رایانه متصل بود، تنها همان برنامه میتوانست از آن استفاده کند.
هیچ مکانیزمی برای صفبندی درخواستها یا اشتراک تجهیزات وجود نداشت.
د) نبود مدیریت خطا
در صورت بروز خطا، معمولاً کل رایانه باید متوقف و دوباره راهاندازی میشد.
هیچ لایهای برای تشخیص، مدیریت یا بازیابی خطا وجود نداشت.
هـ) اجرای تنها یک برنامه
مهمترین محدودیت رایانههای اولیه آن بود که تنها یک برنامه میتوانست اجرا شود.
امروزه مفهوم «چندوظیفگی» بدیهی است، اما در آن زمان اساساً وجود نداشت.
با گسترش استفاده از رایانهها در مراکز تحقیقاتی، دانشگاهها و صنایع، هزینه خرید و نگهداری این سامانهها بسیار بالا بود.
از سوی دیگر، بررسیها نشان میداد که پردازنده در بخش قابل توجهی از زمان خود منتظر پایان عملیات ورودی و خروجی است و از توان واقعی آن استفاده نمیشود.
این وضعیت یک پرسش اساسی ایجاد کرد:
آیا میتوان به جای آنکه پردازنده منتظر یک برنامه بماند، در زمان انتظار، برنامه دیگری را اجرا کرد؟
این پرسش، نقطه آغاز یکی از مهمترین تحولات تاریخ علوم رایانه بود.
پاسخ به این سؤال، تنها یک بهینهسازی ساده نبود؛ بلکه به تولد مفاهیمی انجامید که امروزه آنها را با نامهای زمانبندی پردازشها (Scheduling)، مدیریت حافظه (Memory Management)، حفاظت از منابع (Resource Protection) و در نهایت سیستمعامل میشناسیم.
در این بخش مشاهده کردیم که رایانههای اولیه فاقد هرگونه سیستمعامل بودند و برنامهها مستقیماً با سختافزار ارتباط برقرار میکردند. این شیوه، اگرچه برای سامانههای ساده کافی بود، اما با افزایش توان پردازندهها و پیچیدهتر شدن کاربردهای رایانه، محدودیتهای جدی خود را نشان داد.
عدم مدیریت منابع، نبود حفاظت از حافظه، استفاده ناکارآمد از پردازنده و ناتوانی در اجرای همزمان چند برنامه، همگی نشان میدادند که رایانه به یک لایه مدیریتی نیاز دارد؛ لایهای که مسئولیت تخصیص منابع و هماهنگی میان برنامهها و سختافزار را بر عهده بگیرد.
اما نخستین تلاش برای حل این مشکل، هنوز سیستمعاملهای مدرن نبود. پیش از تولد کرنلهای امروزی، نسل جدیدی از سامانهها با عنوان Batch Processing Systems معرفی شدند؛ سامانههایی که اولین گام جدی در مسیر شکلگیری سیستمعاملهای مدرن محسوب میشوند.
بخش دوم فصل اول به بررسی این تحول، محدودیتهای آن و پیدایش مفاهیم Multiprogramming و Time-Sharing اختصاص دارد؛ مفاهیمی که پایههای نظری و عملی سیستمعاملهای امروزی را شکل دادند.
فصل اول (بخش دوم)
مروری به گذشته ؛
در بخش نخست این فصل دیدیم که رایانههای اولیه فاقد هرگونه سیستمعامل بودند و هر برنامه بهطور مستقیم با سختافزار ارتباط برقرار میکرد. این روش اگرچه برای انجام محاسبات ساده قابل استفاده بود، اما با گسترش کاربرد رایانهها بهسرعت ناکارآمدی خود را نشان داد.
مهمترین مشکل، قدرت پایین پردازنده نبود؛ بلکه نبود سازوکاری برای مدیریت زمان و منابع بود. پردازنده بخش عمدهای از زمان خود را در انتظار عملیات ورودی و خروجی سپری میکرد، در حالی که کاربران برای استفاده از رایانه باید ساعتها یا حتی روزها منتظر میماندند. از دیدگاه اقتصادی نیز این وضعیت قابل قبول نبود؛ زیرا رایانهها در دهههای ۱۹۵۰ و اوایل ۱۹۶۰ از گرانترین تجهیزات علمی و صنعتی محسوب میشدند و بلااستفاده ماندن آنها به معنای اتلاف سرمایه بود.
نیاز به افزایش بهرهوری از سختافزار، نخستین تحول بزرگ در معماری سیستمهای رایانهای را رقم زد؛ تحولی که به تولد سیستمهای پردازش دستهای (Batch Processing Systems) انجامید.
برای درک اهمیت سیستمهای Batch، ابتدا باید شیوه استفاده از رایانه در آن دوران را بشناسیم.
در دهه ۱۹۵۰، رایانهها برخلاف امروز در اختیار کاربران عادی قرار نداشتند. ابعاد آنها گاه به اندازه یک اتاق کامل بود و تنها مراکز نظامی، دانشگاهها، آزمایشگاههای تحقیقاتی و سازمانهای بزرگ توانایی تهیه و نگهداری آنها را داشتند.
کاربر مستقیماً پشت رایانه نمینشست. فرآیند اجرای یک برنامه معمولاً شامل مراحل زیر بود:
برنامهنویس کد خود را روی کاغذ طراحی میکرد.
کد توسط دستگاه پانچ، روی کارتهای پانچ (Punch Cards) ثبت میشد.
دسته کارتها به اپراتور مرکز رایانه تحویل داده میشد.
اپراتور در زمان مناسب کارتها را وارد دستگاه میکرد.
رایانه برنامه را اجرا میکرد.
خروجی روی چاپگر یا کارتهای جدید تولید میشد.
نتیجه مجدداً به برنامهنویس تحویل داده میشد.
گاهی فاصله بین تحویل برنامه و دریافت خروجی چندین ساعت یا حتی یک روز کامل بود.
به بیان دیگر، رایانه یک وسیله تعاملی نبود، بلکه مانند یک مرکز پردازش عمل میکرد که کاربران درخواستهای خود را در صف قرار میدادند.
در نگاه نخست ممکن است تصور شود که رایانه دائماً در حال پردازش بوده است، اما بررسی عملکرد این سامانهها تصویر دیگری ارائه میدهد.
فرض کنید اجرای یک برنامه شامل مراحل زیر باشد:
خواندن اطلاعات از کارت پانچ
انجام محاسبات
چاپ نتیجه
در عمل، مدت زمان خواندن کارتها و چاپ خروجی چندین برابر زمان انجام محاسبات بود.
این موضوع باعث میشد چرخهای مشابه شکل زیر ایجاد شود:
CPU فعال │ ▼ درخواست خواندن داده از کارت پانچ │ ▼ CPU منتظر پایان عملیات I/O │ ▼ هیچ برنامه دیگری برای اجرا وجود ندارد │ ▼ اتلاف زمان پردازنده
مشکل اصلی این نبود که پردازنده کند بود؛ بلکه پردازنده مجبور بود منتظر تجهیزاتی بماند که ذاتاً بسیار کندتر از خود او بودند.
این تفاوت سرعت، بعدها به یکی از مهمترین عوامل طراحی سیستمعاملهای مدرن تبدیل شد.
برای کاهش این اتلاف، مهندسان رویکرد جدیدی پیشنهاد کردند.
به جای آنکه هر کاربر در زمان دلخواه خود از رایانه استفاده کند، برنامهها بهصورت مجموعهای از درخواستهای پردازشی جمعآوری شدند.
به هر درخواست مستقل، Job گفته میشد.
یک Job معمولاً شامل سه بخش بود:
دادههای ورودی
برنامه اجرایی
اطلاعات مربوط به نحوه اجرای برنامه
اپراتور دهها یا صدها Job را جمعآوری میکرد و آنها را بهترتیب برای اجرا آماده میساخت.
به این ترتیب، رایانه بهجای اجرای پراکنده برنامهها، مجموعهای از Jobها را پشت سر هم پردازش میکرد.
همین ایده، نام Batch Processing را شکل داد؛ زیرا برنامهها بهصورت «دستهای» (Batch) اجرا میشدند.
در سادهترین شکل، معماری یک سیستم Batch به صورت زیر بود:
کاربران │ ▼ ایجاد Job │ ▼ اپراتور │ ▼ صف Jobها │ ▼ رایانه │ ▼ اجرای متوالی برنامهها
در این معماری، رایانه پس از پایان هر Job، بدون دخالت کاربر، Job بعدی را اجرا میکرد.
در نتیجه:
زمان آمادهسازی کاهش یافت.
استفاده از پردازنده افزایش پیدا کرد.
تعداد بیشتری از کاربران توانستند از یک رایانه استفاده کنند.
این نخستین بار بود که ایده «مدیریت اجرای برنامهها» بهصورت مستقل از خود برنامه مطرح شد؛ ایدهای که بعدها به یکی از وظایف اصلی سیستمعامل تبدیل شد.
اجرای دستی هر Job همچنان نیازمند حضور اپراتور بود. برای حذف این وابستگی، برنامهای کوچک طراحی شد که پیش از اجرای هر Job در حافظه قرار میگرفت.
این برنامه که Monitor Program نام داشت، وظایف زیر را انجام میداد:
بارگذاری Job بعدی در حافظه
آغاز اجرای آن
تشخیص پایان برنامه
پاکسازی حافظه
آمادهسازی سیستم برای Job بعدی
معماری به شکل زیر تغییر کرد:
+-----------------------------+ | User Job | +-----------------------------+ | Monitor Program | +-----------------------------+ | Hardware | +-----------------------------+
از دیدگاه تاریخی، بسیاری از پژوهشگران Monitor Program را نخستین گام عملی در شکلگیری سیستمعامل میدانند؛ زیرا برای اولین بار، بخشی از نرمافزار مسئول کنترل اجرای سایر برنامهها شد، نه انجام یک وظیفه کاربردی.
البته این برنامه هنوز یک سیستمعامل به معنای امروزی نبود، اما ایدهای را معرفی کرد که بعدها به قلب تمام سیستمعاملها تبدیل شد: وجود یک نرمافزار دائماً مقیم که اجرای سایر برنامهها را مدیریت میکند.
سیستمهای Batch چند تحول اساسی ایجاد کردند:
حذف بسیاری از مداخلات دستی اپراتور
کاهش زمان بیکاری پردازنده
استاندارد شدن فرآیند اجرای برنامهها
افزایش تعداد برنامههای قابل اجرا در یک بازه زمانی
ایجاد مفهوم صف پردازش (Job Queue)
شکلگیری نخستین سازوکارهای مدیریت اجرای برنامه
این دستاوردها زمینه را برای طراحی سیستمعاملهای پیچیدهتر فراهم کردند.
با وجود تمام مزایا، سیستمهای Batch هنوز با مشکلات جدی مواجه بودند.
نبود تعامل با کاربر
پس از شروع اجرای یک Job، کاربر دیگر هیچ کنترلی بر آن نداشت. اگر برنامه دچار خطا میشد، معمولاً تا پایان اجرا یا توقف سیستم، امکان اصلاح وجود نداشت.
استفاده ناقص از پردازنده
اگر یک Job منتظر عملیات ورودی و خروجی میماند، پردازنده همچنان بیکار میماند؛ زیرا تنها همان Job در حافظه حضور داشت.
نبود مدیریت حافظه
هر بار فقط یک برنامه در حافظه قرار میگرفت. هنوز مفهومی به نام اشتراک حافظه یا حفاظت از فضای آدرس وجود نداشت.
نبود چندوظیفگی
تمامی Jobها بهترتیب اجرا میشدند و امکان اجرای همزمان چند برنامه وجود نداشت.
وابستگی به اپراتور
اگرچه نقش اپراتور کاهش یافته بود، اما حذف نشده بود و بسیاری از مراحل همچنان به مدیریت انسانی نیاز داشت.
سیستمهای Batch توانستند زمان از دسترفته بین اجرای برنامهها را کاهش دهند، اما نتوانستند مشکل بنیادی را حل کنند.
اگر برنامهای برای خواندن اطلاعات از دیسک یا نوار مغناطیسی متوقف میشد، پردازنده همچنان منتظر میماند.
در اینجا مهندسان با پرسشی روبهرو شدند که مسیر آینده سیستمعاملها را تعیین کرد:
چرا هنگامی که یک برنامه منتظر عملیات ورودی و خروجی است، برنامه دیگری اجرا نشود؟
پاسخ به این سؤال، انقلابی در معماری سیستمعامل ایجاد کرد؛ زیرا برای نخستین بار مفاهیمی مانند نگهداری همزمان چند برنامه در حافظه، تخصیص هوشمند پردازنده و جابهجایی کنترل بین برنامهها مطرح شد.
این ایده، در دهه ۱۹۶۰ به تولد Multiprogramming انجامید؛ مفهومی که نهتنها بهرهوری پردازنده را افزایش داد، بلکه سنگبنای کرنلهای مدرن و سیستمعاملهای امروزی را بنا نهاد.
سیستمهای Batch نخستین تلاش جدی برای سازماندهی اجرای برنامهها بودند. آنها رایانه را از ماشینی که تنها یک برنامه را با دخالت مستقیم انسان اجرا میکرد، به سامانهای تبدیل کردند که میتوانست مجموعهای از Jobها را بهصورت خودکار پردازش کند.
با این حال، Batch هنوز یک راهحل کامل نبود. این سیستم تنها ترتیب اجرای برنامهها را بهبود داد و نتوانست مشکل اصلی، یعنی استفاده ناکارآمد از پردازنده در زمان انتظار عملیات ورودی و خروجی را برطرف کند.
همین محدودیت، مهندسان را به سوی طراحی معماری جدیدی هدایت کرد که در آن چندین برنامه بهطور همزمان در حافظه قرار میگرفتند و پردازنده میتوانست میان آنها جابهجا شود. این تحول، آغاز عصر Multiprogramming و نخستین گام واقعی به سوی کرنلهای مدرن بود.
فصل اول (بخش سوم)
در پایان بخش پیشین مشاهده کردیم که سیستمهای Batch توانستند بخشی از مشکلات رایانههای اولیه را برطرف کنند. اجرای خودکار مجموعهای از Jobها، کاهش دخالت اپراتور و افزایش بهرهوری نسبت به نسل نخست رایانهها، گام مهمی در تکامل سیستمهای محاسباتی محسوب میشد.
با این حال، این موفقیت تنها بخشی از مسئله را حل کرده بود.
مشکل اصلی همچنان پابرجا بود: پردازنده هنوز بخش قابل توجهی از زمان خود را در انتظار تجهیزات ورودی و خروجی سپری میکرد.
در نگاه نخست، این موضوع ممکن است چندان مهم به نظر نرسد؛ اما از دیدگاه مهندسی رایانه، این وضعیت به معنای آن بود که گرانترین و سریعترین مؤلفه سیستم، یعنی پردازنده، بخش بزرگی از زمان خود را بدون انجام هیچ محاسبهای از دست میدهد.
برای درک علت شکلگیری Multiprogramming، ابتدا باید ماهیت این اتلاف را بررسی کنیم.
یکی از بنیادیترین ویژگیهای معماری رایانه، تفاوت سرعت اجزای مختلف آن است.
پردازنده با سرعتی بسیار بیشتر از حافظههای جانبی و تجهیزات ورودی و خروجی کار میکند. این اختلاف صرفاً یک تفاوت عددی نیست؛ بلکه اختلافی چندین مرتبه بزرگتر (Orders of Magnitude) است.
بهعنوان نمونه، حتی در رایانههای امروزی نیز مدت زمان لازم برای انجام یک محاسبه ساده در پردازنده، در مقایسه با زمان مورد نیاز برای خواندن داده از دیسک، ناچیز است.
این ویژگی از نخستین نسل رایانهها نیز وجود داشت.
فرض کنید برنامهای مراحل زیر را انجام دهد:
خواندن اطلاعات از یک دستگاه ذخیرهسازی.
انجام محاسبات.
ذخیره نتیجه.
اگر مرحله نخست چندین برابر مرحله دوم زمان ببرد، پردازنده تا پایان عملیات ورودی منتظر خواهد ماند.
در سیستمهای Batch، هیچ برنامه دیگری برای اجرا وجود نداشت و در نتیجه، پردازنده عملاً بیکار میماند.
از نگاه مهندسی، این وضعیت CPU Idle Time یا «زمان بیکاری پردازنده» نامیده میشود.
اما مسئله واقعی چه بود؟
در بسیاری از منابع، گفته میشود:
«پردازنده منتظر عملیات I/O میماند.»
این جمله صحیح است، اما علت اصلی را بیان نمیکند.
واقعیت این است که پردازنده ذاتاً منتظر چیزی نمیماند.
آنچه باعث بیکاری CPU میشد، نبود برنامهای دیگر برای اجرا بود.
به بیان دیگر:
اگر تنها یک برنامه در حافظه وجود داشته باشد و آن برنامه منتظر دریافت داده باشد ، پردازنده هیچ دستور دیگری برای اجرا در اختیار نخواهد داشت.
در نتیجه، زمان پردازنده از دست میرود.
این تفاوت ظاهراً کوچک، یکی از مهمترین مفاهیم طراحی سیستمعامل است.
مسئله اصلی، کند بودن تجهیزات ورودی و خروجی نبود؛ مسئله این بود که سیستم نمیتوانست از زمان انتظار یک برنامه برای اجرای برنامهای دیگر استفاده کند.
مهندسان در اوایل دهه ۱۹۶۰ پرسشی مطرح کردند که امروزه بدیهی به نظر میرسد، اما در زمان خود انقلابی بود:
اگر برنامه A منتظر دریافت داده است، چرا برنامه B اجرا نشود؟
ظاهر این سؤال بسیار ساده است، اما اجرای آن مستلزم حل مجموعهای از مسائل پیچیده بود.
اگر قرار بود چند برنامه همزمان در یک سیستم حضور داشته باشند، باید به پرسشهای زیر پاسخ داده میشد:
هر برنامه در کدام بخش حافظه قرار گیرد؟
چگونه از تداخل برنامهها جلوگیری شود؟
پس از توقف یک برنامه، پردازنده چگونه بداند اجرای کدام برنامه را ادامه دهد؟
وضعیت هر برنامه در زمان توقف چگونه ذخیره شود؟
چه کسی تصمیم بگیرد که نوبت اجرای هر برنامه چه زمانی است؟
پاسخ به این پرسشها، آغاز تولد معماری نوینی بود که بعدها «Multiprogramming» نام گرفت.
Multiprogramming روشی در طراحی سیستمهای عامل است که در آن چندین برنامه بهطور همزمان در حافظه اصلی (Main Memory) نگهداری میشوند تا هر زمان یکی از آنها به دلیل انتظار برای عملیات ورودی یا خروجی متوقف شد، پردازنده بتواند اجرای برنامه دیگری را آغاز کند.
نکته مهم این است که در این معماری، پردازنده هنوز در هر لحظه تنها یک برنامه را اجرا میکند.
بنابراین، Multiprogramming به معنای اجرای همزمان واقعی چند برنامه نیست، بلکه به معنای استفاده حداکثری از زمان پردازنده از طریق جابهجایی هوشمند بین برنامهها است.
این تفاوت، یکی از رایجترین سوءبرداشتها درباره مفهوم چندبرنامگی است.
معماری Multiprogramming .
با معرفی این ایده، ساختار سیستم تغییر کرد.
در سیستمهای Batch:
Memory ────────────── Program A ──────────────
اما در معماری جدید:
Memory ──────────────────── Program A ──────────────────── Program B ──────────────────── Program C ──────────────────── Operating System ────────────────────
اکنون اگر Program A منتظر عملیات ورودی و خروجی باشد، پردازنده میتواند Program B را اجرا کند.
اگر Program B نیز متوقف شود، Program C اجرا خواهد شد.
در نتیجه، زمان بیکاری پردازنده به میزان قابل توجهی کاهش مییابد.
با ورود چندین برنامه به حافظه، مسئلهای کاملاً جدید به وجود آمد.
اگر سه یا چهار برنامه آماده اجرا باشند، چه کسی تصمیم میگیرد کدامیک ابتدا اجرا شود؟
در معماریهای اولیه، چنین تصمیمی اساساً وجود نداشت، زیرا تنها یک برنامه در سیستم حضور داشت.
اما اکنون نیاز به مؤلفهای احساس میشد که بتواند میان برنامههای مختلف تصمیمگیری کند.
این مؤلفه بعدها Scheduler یا «زمانبند» نام گرفت.
Scheduler مسئول انتخاب برنامه بعدی برای استفاده از پردازنده است.
در این مرحله از تاریخ سیستمعامل، زمانبند هنوز بسیار ساده بود، اما همین ایده بعدها به یکی از پیچیدهترین بخشهای هر کرنل مدرن تبدیل شد.
تا اینجا، تغییری اساسی در نقش نرمافزار مدیریت سیستم رخ داده بود.
در سیستمهای Batch ، Monitor Program تنها برنامهها را بهترتیب اجرا میکرد.
اما در معماری Multiprogramming، این نرمافزار باید وظایف بسیار پیچیدهتری را بر عهده میگرفت:
نگهداری اطلاعات هر برنامه
مدیریت حافظه
انتخاب برنامه بعدی
کنترل پردازنده
هماهنگی عملیات ورودی و خروجی
جلوگیری از تداخل برنامهها
به بیان دیگر، نرمافزار مدیریت سیستم دیگر صرفاً یک اجراکننده نبود؛ بلکه به مدیر منابع سیستم تبدیل شده بود.
در همین نقطه است که میتوان نخستین نشانههای چیزی را مشاهده کرد که بعدها «کرنل» نام گرفت.
اگر بخواهیم مهمترین دستاورد Multiprogramming را تنها در یک جمله خلاصه کنیم، باید بگوییم:
Multiprogramming برای نخستین بار پردازنده را از یک مجری منفعل به منبعی تبدیل کرد که باید بهصورت هوشمند میان چند برنامه مدیریت شود.
این تغییر، تنها یک بهبود عملکرد نبود؛ بلکه تغییری بنیادین در فلسفه طراحی سیستمهای عامل محسوب میشد.
از این لحظه به بعد، مسئله اصلی دیگر «اجرای یک برنامه» نبود، بلکه «مدیریت همزمان چندین برنامه و منابع مشترک» بود.
تقریباً تمامی قابلیتهایی که امروزه در کرنلهای مدرن مشاهده میکنیم—از زمانبندی پردازشها گرفته تا مدیریت حافظه، حفاظت از منابع و مدیریت وقفهها—ریشه در همین تغییر نگرش دارند.
Batch Processing ترتیب اجرای برنامهها را بهبود بخشید، اما مشکل بیکاری پردازنده را حل نکرد.
عامل اصلی اتلاف زمان CPU، نبود برنامهای دیگر برای اجرا در هنگام انتظار عملیات I/O بود.
Multiprogramming چند برنامه را بهطور همزمان در حافظه نگهداری میکرد، اما پردازنده همچنان در هر لحظه فقط یک برنامه را اجرا میکرد.
ظهور چندبرنامگی، نیاز به مدیریت حافظه، زمانبندی و حفاظت از منابع را ایجاد کرد.
در همین مرحله، نقش نرمافزار مدیریتی از یک «اجراکننده برنامه» به «مدیر منابع» تغییر یافت؛ تغییری که زمینهساز شکلگیری کرنلهای مدرن شد.
Multiprogramming توانست بهرهوری پردازنده را به شکل چشمگیری افزایش دهد، اما هنوز یک محدودیت اساسی باقی مانده بود.
کاربران همچنان نمیتوانستند بهصورت تعاملی با رایانه کار کنند. اجرای برنامهها ماهیتی غیرتعاملی داشت و پاسخگویی سیستم برای استفاده روزمره مناسب نبود.
این محدودیت، نسل بعدی سیستمهای عامل را به وجود آورد؛ سامانههایی که برای نخستین بار مفهوم «اشتراک زمانی پردازنده» (Time Sharing) را معرفی کردند و مسیر را برای رایانههای تعاملی و چندکاربره هموار ساختند.
فصل اول (بخش چهارم)
در بخش قبل با مفهوم Multiprogramming آشنا شدیم؛ روشی که با نگهداری همزمان چند برنامه در حافظه اصلی، امکان استفاده بهتر از پردازنده را فراهم میکرد. این تحول، نقطه عطفی در تاریخ سیستمهای عامل بود، زیرا برای نخستین بار رایانه دیگر تنها به اجرای یک برنامه محدود نبود.
اما هر راهحل مهندسی، در کنار مزایای خود، مسائل جدیدی نیز ایجاد میکند.
تا زمانی که تنها یک برنامه در حافظه وجود داشت، بسیاری از مشکلات اساساً مطرح نبودند. هیچ رقابتی برای استفاده از پردازنده وجود نداشت، هیچ برنامهای نمیتوانست حافظه برنامه دیگری را تخریب کند و نیازی به تصمیمگیری درباره ترتیب اجرای برنامهها احساس نمیشد.
با ورود چندین برنامه به حافظه، رایانه وارد مرحلهای کاملاً جدید شد؛ مرحلهای که در آن دیگر مسئله اصلی «اجرای برنامه» نبود، بلکه مدیریت منابع مشترک بود.
در همین نقطه، پایههای معماری کرنلهای مدرن شکل گرفت.
فرض کنید سه برنامه در حافظه قرار دارند.
+-------------------------+ | Program A | +-------------------------+ | Program B | +-------------------------+ | Program C | +-------------------------+
هر سه برنامه آماده اجرا هستند.
اکنون سؤال سادهای مطرح میشود:
کدام برنامه باید از پردازنده استفاده کند؟
در نگاه اول ممکن است پاسخ بدیهی به نظر برسد، اما پردازنده قادر به تصمیمگیری نیست.
CPU تنها دستورالعملها را اجرا میکند؛ این پردازنده نه مفهوم «اولویت» را میشناسد، نه «کاربر» را، نه «عدالت» را و نه «زمان».
اگر هیچ سازوکاری برای تصمیمگیری وجود نداشته باشد، یکی از برنامهها ممکن است تمام زمان پردازنده را در اختیار بگیرد و سایر برنامهها هرگز اجرا نشوند.
در علوم سیستمعامل، این مشکل را CPU Scheduling Problem مینامند.
زمانبندی پردازنده (CPU Scheduling)
زمانبندی پردازنده به فرآیند انتخاب برنامهای گفته میشود که در لحظه بعدی اجازه استفاده از CPU را خواهد داشت.
این وظیفه بعدها بر عهده مؤلفهای قرار گرفت که امروزه آن را Scheduler مینامیم.
Scheduler یکی از مهمترین اجزای هر کرنل مدرن است و تقریباً تمام سیستمعاملهای امروزی، صرفنظر از معماری خود، نوعی زمانبند دارند.
در فصلهای آینده، Scheduler را از دید الگوریتمهای زمانبندی، ساختمان دادهها و پیادهسازی در کرنل لینوکس بررسی خواهیم کرد.
حال فرض کنید Program A به اشتباه دادهای را در محدوده حافظه Program B بنویسد.
در معماریهای اولیه، چنین اتفاقی بهراحتی ممکن بود.
نتیجه میتوانست یکی از موارد زیر باشد:
تخریب دادههای برنامه دوم
از کار افتادن برنامه دوم
ایجاد رفتارهای غیرقابل پیشبینی
توقف کامل سیستم
بنابراین، تنها نگهداری چند برنامه در حافظه کافی نبود.
سیستم باید بتواند مرزی میان فضای حافظه هر برنامه ایجاد کند.
از اینجا مفهوم Memory Protection یا حفاظت از حافظه متولد شد.
حفاظت از حافظه (Memory Protection)
حفاظت از حافظه به مجموعه مکانیزمهایی گفته میشود که مانع دسترسی غیرمجاز یک برنامه به فضای حافظه برنامهای دیگر میشوند.
در سیستمهای مدرن، این وظیفه با همکاری سختافزار (بهویژه MMU) و کرنل انجام میشود.
بدون حفاظت از حافظه، هیچ سیستمعامل چندوظیفهای پایداری امکانپذیر نخواهد بود.
اکنون فرض کنید Program A در حال چاپ یک سند است.
همزمان Program B نیز فرمان چاپ ارسال میکند.
چاپگر تنها میتواند یک جریان داده را در هر لحظه پردازش کند.
اگر هر دو برنامه مستقیماً با چاپگر ارتباط برقرار کنند، خروجی بههم خواهد ریخت.
همین مسئله برای دیسک، کارت شبکه، صفحهکلید و تقریباً تمام تجهیزات سختافزاری نیز وجود دارد.
بنابراین، وجود نهادی برای مدیریت تجهیزات اجتنابناپذیر بود.
این وظیفه بعدها به بخشی از کرنل و درایورهای دستگاه واگذار شد.
در علوم سیستمعامل، هر مؤلفهای که بهصورت محدود میان چند برنامه به اشتراک گذاشته شود، منبع (Resource) نامیده میشود.
پردازنده، حافظه، چاپگر، دیسک، کارت شبکه و حتی فایلها، همگی نمونههایی از منابع سیستم هستند.
یکی از مهمترین مسئولیتهای کرنل، تخصیص عادلانه، کنترلشده و ایمن این منابع است.
فرض کنید Program A در حال اجراست.
در میانه کار، سیستم تصمیم میگیرد Program B را اجرا کند.
پرسش مهم این است:
اطلاعات مربوط به وضعیت Program A کجا ذخیره میشود؟
اگر این اطلاعات از بین بروند، برنامه هرگز قادر نخواهد بود از همان نقطه به اجرای خود ادامه دهد.
بنابراین، سیستم باید وضعیت پردازنده، رجیسترها، شمارنده دستورالعمل و سایر اطلاعات لازم را ذخیره کند.
این ایده بعدها به مفاهیمی مانند Process Context و Context Switch منجر شد.
Context مجموعهای از اطلاعاتی است که وضعیت فعلی یک برنامه را توصیف میکند.
این اطلاعات شامل رجیسترهای پردازنده، شمارنده برنامه (Program Counter)، اشارهگر پشته (Stack Pointer) و سایر دادههایی است که برای ادامه اجرای برنامه ضروری هستند.
کرنل هنگام جابهجایی بین برنامهها، Context برنامه در حال اجرا را ذخیره و Context برنامه بعدی را بازیابی میکند.
این فرآیند، Context Switching نام دارد.
تا اینجا مجموعهای از مشکلات جدید ظاهر شده است:
انتخاب برنامه بعدی
مدیریت حافظه
حفاظت از دادهها
کنترل تجهیزات سختافزاری
نگهداری وضعیت برنامهها
اگر برای هر مشکل یک برنامه مستقل وجود داشته باشد، چه کسی هماهنگی میان آنها را انجام میدهد؟
پاسخ این پرسش، معماری سیستمعامل را برای همیشه تغییر داد.
تمام این مسئولیتها باید در اختیار یک بخش مرکزی قرار میگرفت؛ بخشی که همواره در حافظه حضور داشته باشد، به تمام منابع سیستم دسترسی داشته باشد و بتواند درباره نحوه استفاده از آنها تصمیم بگیرد.
این بخش، همان چیزی است که بعدها «Kernel» نام گرفت.
اگر بخواهیم نقطه تولد واقعی کرنل را مشخص کنیم، باید آن را نه در تاریخ معرفی یک سیستمعامل خاص، بلکه در لحظهای بدانیم که رایانه برای نخستین بار نیازمند مدیریت همزمان منابع مشترک شد.
کرنل برای اجرای برنامهها ایجاد نشد؛ برنامهها پیش از آن نیز اجرا میشدند.
کرنل زمانی ضرورت پیدا کرد که چندین برنامه خواستند از یک پردازنده، یک حافظه و مجموعهای از تجهیزات مشترک استفاده کنند.
به همین دلیل، میتوان کرنل را «مدیر منابع سیستم» دانست، نه صرفاً «اجراکننده برنامهها».
این تغییر نگاه، یکی از بنیادیترین مفاهیم معماری سیستمعامل است و درک آن برای فهم تمام فصلهای بعدی ضروری خواهد بود.
Multiprogramming مسئله مدیریت منابع را ایجاد کرد.
Scheduler پاسخی به رقابت برنامهها برای استفاده از CPU بود.
Memory Protection برای جلوگیری از تخریب فضای حافظه برنامهها به وجود آمد.
مدیریت تجهیزات سختافزاری برای اشتراک ایمن منابع ضروری شد.
Context امکان توقف و ادامه اجرای برنامهها را فراهم کرد.
مجموعه این نیازها، به شکلگیری هستهای مرکزی برای کنترل کل سیستم انجامید؛ هستهای که امروزه آن را کرنل مینامیم.
تا اینجا، سیستم میتوانست چند برنامه را در حافظه نگهداری کند و میان آنها جابهجا شود. اما هنوز یک محدودیت مهم باقی مانده بود: کاربران همچنان تعامل مستقیمی با رایانه نداشتند.
در بخش بعد خواهیم دید که چگونه مفهوم Time-Sharing، رایانه را از یک ماشین پردازش دستهای به سامانهای تعاملی و چندکاربره تبدیل کرد؛ تحولی که تأثیر آن تا امروز در تمام سیستمعاملهای مدرن باقی مانده است.
فصل اول (بخش پنجم)
مقدمه
در پایان بخش قبل، به نقطهای رسیدیم که سیستم میتوانست چندین برنامه را همزمان در حافظه نگهداری کند. همچنین مشاهده کردیم که برای مدیریت این برنامهها، مفاهیمی مانند زمانبندی پردازنده، حفاظت از حافظه و مدیریت منابع شکل گرفتند.
اما یک پرسش بنیادین همچنان بیپاسخ مانده بود.
فرض کنید Program A در حال اجرا است و سیستم تصمیم گرفته است اکنون Program B اجرا شود.
این تصمیم چگونه عملی میشود؟
چه کسی به پردازنده اطلاع میدهد که اجرای Program A را متوقف کند؟
چه سازوکاری باعث میشود کنترل از برنامه در حال اجرا گرفته شود و به کرنل منتقل گردد؟
پاسخ این پرسش، یکی از بنیادیترین مفاهیم معماری رایانه و سیستمعامل است: Interrupt یا «وقفه».
برای درک اهمیت Interrupt، ابتدا فرض کنید چنین مکانیزمی اصلاً وجود ندارد.
در این حالت، پردازنده پس از آغاز اجرای یک برنامه، تنها دو راه برای توقف دارد:
برنامه خودش به پایان برسد.
برنامه خودش تصمیم بگیرد کنترل را واگذار کند.
در نگاه اول، این روش منطقی به نظر میرسد، اما یک مشکل اساسی دارد.
فرض کنید برنامهای وارد یک حلقه بینهایت شود:
while (1) { }
اگر هیچ مکانیزمی برای توقف اجباری وجود نداشته باشد، این برنامه میتواند برای همیشه پردازنده را در اختیار بگیرد.
در چنین شرایطی:
هیچ برنامه دیگری اجرا نمیشود.
سیستم قادر به پاسخگویی به کاربر نیست.
حتی خود سیستمعامل نیز امکان بازپسگیری کنترل را نخواهد داشت.
بنابراین،کنترل کامل پردازنده نباید در اختیار برنامهها باشد.
در اینجا با یکی از مهمترین مسائل طراحی سیستمعامل روبهرو میشویم:
چگونه میتوان پردازنده را بدون اجازه برنامه متوقف کرد؟
این پرسش، مستقیماً به طراحی سختافزار مربوط میشود.
اگر سختافزار نتواند پردازنده را متوقف کند، هیچ سیستمعامل چندوظیفهای قابل پیادهسازی نخواهد بود.
Interrupt مکانیزمی در معماری رایانه است که به پردازنده اعلام میکند:
«اجرای فعلی را موقتاً متوقف کن؛ اتفاق مهمتری رخ داده است.»
پس از دریافت Interrupt، پردازنده اجرای برنامه فعلی را متوقف میکند، وضعیت آن را ذخیره میکند و کنترل را به کدی از پیش تعیینشده واگذار میکند.
در سیستمعاملهای مدرن، این کد معمولاً بخشی از Kernel است.
به بیان ساده:
Program │ ▼ CPU │ Interrupt │ ▼ Kernel
برای اولین بار، سختافزار میتواند کنترل را از یک برنامه گرفته و به کرنل منتقل کند.
این یکی از بنیادیترین ویژگیهای معماری رایانههای مدرن است.
تفاوت Interrupt با توقف عادی برنامه :
اگر برنامهای تابعی را فراخوانی کند، خودش تصمیم گرفته است که مسیر اجرای خود را تغییر دهد.
اما Interrupt تصمیم برنامه نیست. Interrupt از بیرون برنامه اتفاق میافتد. به همین دلیل، برنامه از قبل نمیداند چه زمانی رخ خواهد داد. از دید برنامه، Interrupt یک رویداد ناگهانی است. همین ویژگی باعث میشود سیستمعامل بتواند بدون وابستگی به همکاری برنامه، کنترل سیستم را در اختیار بگیرد.
Interrupt همیشه به یک دلیل واحد ایجاد نمیشود.
در معماری رایانه، منابع مختلفی میتوانند درخواست Interrupt ارسال کنند.
مهمترین آنها عبارتاند از:
۱- تایمر سختافزاری (Hardware Timer)
پس از گذشت مدت زمانی مشخص، تایمر به پردازنده اعلام میکند که زمان اجرای برنامه فعلی به پایان رسیده است.
این همان مکانیزمی است که بعدها امکان Time Sharing را فراهم میکند.
۲- دستگاههای ورودی و خروجی
فرض کنید دیسک خواندن اطلاعات را تمام کرده است. اگر پردازنده دائماً وضعیت دیسک را بررسی کند، زمان زیادی هدر خواهد رفت. در عوض، دیسک پس از پایان عملیات، یک Interrupt ارسال میکند و کرنل مطلع میشود که داده آماده است.
۳- صفحهکلید
هنگامی که کاربر کلیدی را فشار میدهد، صفحهکلید Interrupt ارسال میکند.
در نتیجه، سیستمعامل از ورود اطلاعات آگاه میشود.
۴- کارت شبکه
پس از دریافت یک بسته شبکه، کارت شبکه کرنل را از طریق Interrupt مطلع میکند.
۵- خطاهای پردازنده
برخی رویدادها مانند تقسیم بر صفر یا دسترسی نامعتبر به حافظه نیز باعث انتقال کنترل به کرنل میشوند. این رویدادها از نظر فنی در دستهی Exceptions قرار میگیرند که در فصلهای بعد تفاوت آنها با Interrupt را بهطور دقیق بررسی خواهیم کرد.
در ظاهر، وقوع Interrupt تنها یک رویداد کوتاه است؛ اما در پشت صحنه، پردازنده مجموعهای از عملیات دقیق را انجام میدهد.
بهصورت ساده، روند کار چنین است:
Program A در حال اجرا │ ▼ Interrupt رخ میدهد │ ▼ CPU اجرای دستور فعلی را کامل میکند │ ▼ ذخیره وضعیت اجرای برنامه │ ▼ ورود به حالت کرنل │ ▼ اجرای Interrupt Handler │ ▼ بازگشت به برنامه قبلی یا انتخاب برنامه جدید
در این فرآیند، پردازنده ابتدا وضعیت برنامه جاری را حفظ میکند تا بعداً بتواند اجرای آن را دقیقاً از همان نقطه ادامه دهد.
Interrupt Handler چیست؟
هر Interrupt به قطعه کدی نیاز دارد که مشخص کند پس از وقوع آن چه عملی باید انجام شود.
این قطعه کد را Interrupt Handler یا Interrupt Service Routine (ISR) مینامند.
برای مثال:
Interrupt صفحهکلید، اطلاعات کلید فشردهشده را دریافت میکند.
Interrupt دیسک، عملیات خواندن داده را تکمیل میکند.
Interrupt تایمر، Scheduler را فعال میکند.
در بیشتر سیستمعاملها، Handlerها بخشی از کرنل هستند.
اکنون میتوانیم زنجیرهای را که در بخشهای قبل ناقص بود، کامل کنیم.
فرض کنید تایمر هر ۱۰ میلیثانیه یک Interrupt ایجاد کند.
در هر Interrupt:
برنامه فعلی متوقف میشود.
کرنل کنترل را در اختیار میگیرد.
Scheduler اجرا میشود.
تصمیم میگیرد برنامه فعلی ادامه یابد یا برنامه دیگری اجرا شود.
Context مناسب بازیابی میشود.
پردازنده اجرای برنامه منتخب را آغاز میکند.
در نتیجه، چیزی که از دید کاربر «اجرای همزمان چند برنامه» به نظر میرسد، در واقع حاصل هزاران Interrupt و تصمیمگیری سریع کرنل است.
Interrupt را نباید صرفاً یک «سیگنال سختافزاری» دانست.
از دیدگاه معماری سیستم، Interrupt مرز میان دنیای برنامههای کاربردی و دنیای کنترل سیستم است.
بدون Interrupt:
کرنل نمیتواند کنترل را از برنامهها بازپس بگیرد.
زمانبندی پیشدستانه (Preemptive Scheduling) ممکن نیست.
پاسخگویی سریع به تجهیزات ورودی و خروجی از بین میرود.
سیستمعامل عملاً به همکاری کامل برنامهها وابسته خواهد شد.
به همین دلیل، تقریباً تمام سیستمعاملهای مدرن بر پایه Interrupt طراحی شدهاند.
پینوشت:
فرض کنید هیچ مکانیزم Interrupt در سختافزار وجود نداشت.
آیا میتوانستیم همچنان یک سیستمعامل چندوظیفهای بسازیم؟
پاسخ کوتاه این است: بله، اما با محدودیتهای جدی.
در این حالت، تنها راه ممکن این بود که برنامهها داوطلبانه کنترل را به سیستمعامل بازگردانند. این مدل را بعدها Cooperative Multitasking نامیدند.
مشکل آن روشن است: اگر یک برنامه هرگز کنترل را واگذار نکند، کل سیستم متوقف میشود. تجربهٔ سیستمعاملهای قدیمی نشان داد که این رویکرد برای محیطهای عمومی و چندکاربره قابل اعتماد نیست.به همین دلیل، معماری رایانه به سمت استفاده از Interruptهای سختافزاری و زمانبندی پیشدستانه حرکت کرد؛ تغییری که پایداری و پاسخگویی سیستمهای مدرن را ممکن ساخت.
Interrupt سازوکاری سختافزاری برای انتقال کنترل از برنامه به کرنل است.
برنامهها زمان وقوع Interrupt را تعیین نمیکنند.
تایمر، تجهیزات ورودی/خروجی و برخی رویدادهای پردازنده میتوانند Interrupt ایجاد کنند.
هنگام وقوع Interrupt، وضعیت برنامه ذخیره میشود و کرنل اجرای Handler مناسب را آغاز میکند.
Scheduler برای پیادهسازی زمانبندی پیشدستانه به Interruptهای تایمر وابسته است.
بدون Interrupt، سیستمعاملهای مدرن با قابلیت چندوظیفگی و پاسخگویی مناسب قابل پیادهسازی نبودند.
اکنون تقریباً تمام اجزای لازم برای یک سیستم تعاملی در اختیار ما قرار گرفته است:
چندین برنامه میتوانند همزمان در حافظه حضور داشته باشند.
کرنل قادر است منابع را مدیریت کند.
Interrupt امکان بازپسگیری کنترل از برنامهها را فراهم میکند.
Scheduler میتواند درباره برنامه بعدی تصمیم بگیرد.
اما هنوز یک پرسش باقی مانده است:
چگونه میتوان این قابلیتها را به گونهای به کار گرفت که دهها کاربر احساس کنند هر کدام رایانهای اختصاصی در اختیار دارند؟
پاسخ این پرسش، ما را به یکی از مهمترین نوآوریهای تاریخ سیستمعامل میرساند: Time-Sharing Systems.
از ماشین محاسباتی تا سامانه مدیریت منابع
در نخستین نسل رایانهها، چیزی به نام سیستمعامل وجود نداشت. هر برنامه مستقیماً با سختافزار ارتباط برقرار میکرد و تمام مسئولیت مدیریت پردازنده، حافظه و تجهیزات جانبی بر عهده خود برنامه بود. این مدل برای رایانههایی که تنها یک برنامه را در هر لحظه اجرا میکردند کافی به نظر میرسید، اما با گسترش کاربرد رایانهها، محدودیتهای آن بهسرعت آشکار شد.
مهمترین این محدودیتها، استفاده ناکارآمد از منابع سختافزاری بود. پردازنده، که گرانترین و سریعترین مؤلفه سیستم محسوب میشد، در زمان انتظار برای عملیات ورودی و خروجی عملاً بدون استفاده باقی میماند. نبود حفاظت از حافظه، ناتوانی در اشتراکگذاری تجهیزات و وابستگی کامل اجرای برنامهها به اپراتور انسانی نیز از دیگر مشکلات اساسی این نسل از سامانهها بود.
این محدودیتها نشان دادند که رایانه تنها به قدرت پردازشی بیشتر نیاز ندارد؛ بلکه به مدیریت هوشمند منابع نیازمند است.
نخستین پاسخ؛ Batch Processing
اولین تلاش برای حل این مسئله، سیستمهای Batch بود. در این معماری، برنامهها بهصورت مجموعهای از Jobها جمعآوری و بدون دخالت مستقیم کاربر اجرا میشدند. وجود Monitor Program نخستین گام در جداسازی مدیریت اجرای برنامهها از خود برنامهها بود.
با این حال، Batch تنها ترتیب اجرای برنامهها را بهبود داد و نتوانست مشکل اصلی، یعنی بیکار ماندن پردازنده در زمان انتظار عملیات ورودی و خروجی را برطرف کند.
دومین تحول؛ Multiprogramming
راهحل بعدی این بود که بهجای نگهداری یک برنامه در حافظه، چندین برنامه بهطور همزمان در حافظه قرار گیرند. اگر یکی از آنها منتظر عملیات ورودی و خروجی بود، پردازنده بتواند برنامه دیگری را اجرا کند.
این ایده، که با نام Multiprogramming شناخته میشود، نقطه عطفی در تاریخ سیستمعاملها بود. از این مرحله به بعد، مسئله دیگر اجرای یک برنامه نبود، بلکه مدیریت همزمان چند برنامه و منابع مشترک بود.
آغاز مشکلات جدید؛ تولد مدیریت منابع
ورود چند برنامه به حافظه، مجموعهای از پرسشهای جدید را مطرح کرد:
اگر چند برنامه همزمان پردازنده را بخواهند، چه کسی تصمیم میگیرد؟
اگر یک برنامه حافظه برنامه دیگری را تغییر دهد، چگونه از آن جلوگیری میشود؟
اگر چند برنامه همزمان از یک چاپگر یا دیسک استفاده کنند، چه سازوکاری تعارض را مدیریت میکند؟
اگر اجرای یک برنامه متوقف شود، وضعیت آن چگونه حفظ میشود تا بعداً از همان نقطه ادامه پیدا کند؟
پاسخ به این پرسشها، مفاهیمی را به وجود آورد که امروزه ستونهای اصلی هر کرنل مدرن محسوب میشوند؛ از جمله زمانبندی پردازنده، حفاظت از حافظه، مدیریت منابع و مدیریت وضعیت برنامهها.
در این مرحله، نرمافزار مدیریت سیستم از یک اجراکننده ساده به مدیر مرکزی منابع سیستم تبدیل شد.
Interrupt؛ پلی میان سختافزار و کرنل
با وجود همه این پیشرفتها، هنوز یک پرسش اساسی باقی مانده بود: اگر یک برنامه حاضر نباشد کنترل پردازنده را واگذار کند، سیستمعامل چگونه میتواند دوباره کنترل را در دست بگیرد؟
پاسخ این پرسش، مکانیزمی به نام Interrupt بود.
Interrupt این امکان را فراهم کرد که سختافزار، مستقل از برنامه در حال اجرا، کنترل را به کرنل منتقل کند. این قابلیت نهتنها پاسخگویی سریع به رویدادهای خارجی را ممکن ساخت، بلکه زمینه را برای زمانبندی پیشدستانه، مدیریت مؤثر منابع و اجرای تعاملی برنامهها فراهم کرد.
بدون Interrupt، بسیاری از قابلیتهایی که امروز بدیهی به نظر میرسند، از جمله چندوظیفگی پایدار و پاسخگویی سریع سیستم، امکانپذیر نبودند.
اگر بخواهیم تمام مطالب این فصل را تنها در یک جمله خلاصه کنیم، میتوان گفت:
کرنل برای اجرای برنامهها به وجود نیامد؛ برنامهها پیش از آن نیز اجرا میشدند. کرنل زمانی متولد شد که چندین برنامه خواستند منابع محدود یک رایانه را بهطور همزمان، ایمن و کارآمد با یکدیگر به اشتراک بگذارند.
پایان بخش پنجم.