میزبانی خودی (Self-hosting) در کامپایلرها

Self-hosting (compilers)
📅 31 خرداد 1405 📄 738 کلمه 🔗 منبع اصلی

چکیده

میزبانی خودی (Self-hosting) در برنامه‌نویسی یعنی نرم‌افزاری بتواند نسخه‌های جدید خودش را تولید کند؛ مانند کامپایلری که کد منبع خود را ترجمه می‌کند. این مفهوم که در سیستم‌عامل‌ها و کامپایلرها کاربرد دارد، با مشکل مرغ و تخم‌مرغ روبه‌روست اما راه‌حل‌هایی مثل کامپایلر متقاطع و بوت‌استرپ دارد.

میزبانی خودی (Self-hosting) چیست؟

در برنامه‌نویسی کامپیوتر، زمانی می‌گوییم یک برنامه «میزبانی خودی» دارد که آن برنامه بخشی از زنجیره ابزار یا سیستم‌عاملی باشد که نسخه‌های جدید همان برنامه را تولید می‌کند؛ مثلاً کامپایلری که بتواند کد منبع خودش را کامپایل کند. نرم‌افزارهای با میزبانی خودی در کامپیوترهای شخصی و سیستم‌های بزرگ امری کاملاً رایج است. سایر برنامه‌هایی که معمولاً این ویژگی را دارند، شامل هسته سیستم‌عامل، اسمبلرها، مفسرهای خط فرمان و نرم‌افزارهای کنترل نسخه می‌شوند.

سیستم‌عامل‌ها

زمانی یک سیستم‌عامل میزبان خودش محسوب می‌شود که زنجیره ابزار ساخت آن سیستم‌عامل، روی همان سیستم‌عامل اجرا شود. به عنوان مثال، ویندوز را می‌توان روی کامپیوتری که خودش ویندوز اجرا می‌کند، بیلد کرد.

پیش از آنکه سیستمی بتواند خودمیزبان شود، به سیستم دیگری برای توسعه نیاز دارد تا زمانی که به مرحله‌ای برسد که میزبانی خودی برایش ممکن گردد. هنگام توسعه برای یک کامپیوتر یا سیستم‌عامل جدید، سیستمی برای اجرای نرم‌افزارهای توسعه لازم است، اما خود نرم‌افزارهای توسعه برای نوشتن و بیلد کردن سیستم‌عامل نیز ضروری‌اند. این دور باطل، «مشکل بوت‌استرپ» (Bootstrapping) یا به بیان عمومی‌تر، «معضل مرغ و تخم‌مرغ» نامیده می‌شود.

راه‌حل این مشکل، استفاده از کامپایلر متقاطع (یا اسمبلر متقاطع برای زبان اسمبلی) است. کامپایلر متقاطع اجازه می‌دهد کد منبع در یک پلتفرم، برای ماشین یا سیستم‌عامل متفاوتی کامپایل شود و این امکان را فراهم می‌آورد تا سیستم‌عاملی برای ماشینی بسازید که هنوز کامپایلر خودمیزبانی برایش وجود ندارد. پس از نوشتن، نرم‌افزار با استفاده از روش‌هایی چون EPROM، فلاپی‌دیسک، حافظه فلش (مثل فلش USB) یا دستگاه JTAG روی سیستم هدف مستقر می‌شود. این روش شبیه روشی است که برای نوشتن نرم‌افزار کنسول‌های بازی یا دستگاه‌های دستی مانند گوشی‌های موبایل و تبلت‌ها استفاده می‌شود؛ دستگاه‌هایی که ابزارهای توسعه خودشان را میزبانی نمی‌کنند.

به محض آنکه سیستم به بلوغ کافی برای کامپایل کد خودش برسد، وابستگی به توسعه متقاطع پایان می‌یابد. در این نقطه، می‌گوییم سیستم‌عامل خودمیزبان شده است.

کامپایلرها

توسعه نرم‌افزار با استفاده از کامپایلرها یا مفسرها نیز زمانی خودمیزبان است که کامپایلر قادر به کامپایل کردن خودش باشد.

از آنجا که کامپایلرهای خودمیزبان نیز دچار همان مشکلات بوت‌استرپِ سیستم‌عامل‌ها هستند، کامپایلر یک زبان برنامه‌نویسی جدید باید به زبان موجود دیگری نوشته شود. بنابراین، توسعه‌دهنده ممکن است از زبان اسمبلی، C/C++ یا حتی زبان‌های اسکریپتی مانند پایتون یا لوآ برای ساخت نسخه اولیه کامپایلر استفاده کند. وقتی زبان به اندازه کافی بالغ شد، توسعه کامپایلر می‌تواند به زبان بومی خود کامپایلر منتقل شود و در نتیجه، کامپایلر خودش را بیلد کند.

تاریخچه

نخستین کامپایلر خودمیزبان (به استثنای اسمبلرها) در سال ۱۹۶۲ توسط هارت و لوین در MIT برای زبان لیسپ نوشته شد. آن‌ها یک کامپایلر لیسپ را به زبان لیسپ نوشتند و آن را داخل یک مفسر لیسپِ از پیش موجود آزمایش کردند. به محض آنکه کامپایلر را تا جایی ارتقا دادند که بتواند کد منبع خودش را کامپایل کند، به حالت خودمیزبانی رسید.

این تکنیک معمولاً تنها زمانی عملی است که مفسری برای همان زبانی که قرار است کامپایل شود، از پیش وجود داشته باشد؛ هرچند از نظر انسانی ممکن است، اما کامپایل کردن دستی یک کامپایلر با خودش بسیار نادر است. این مفهوم مستقیماً از ایده گسترده‌ترِ اجرای یک برنامه با خودش به عنوان ورودی وام گرفته شده است؛ ایده‌ای که در اثبات‌های مختلف علوم کامپیوتر نظری، مانند اثبات غیرقابل تصمیم‌گیری بودن مسئله توقف (Halting problem)، نیز به کار می‌رود.

نمونه‌ها

کن تامپسون توسعه یونیکس را در سال ۱۹۶۸ با نوشتن و کامپایل برنامه‌ها روی GE-635 و انتقال آن‌ها به PDP-7 برای آزمایش آغاز کرد. پس از تکمیل هسته اولیه یونیکس، مفسر فرمان، ویرایشگر، اسمبلر و چند ابزار کاربردی، سیستم‌عامل یونیکس خودمیزبان شد؛ یعنی برنامه‌ها می‌توانستند روی خود PDP-7 نوشته و آزمایش شوند.

داگلاس مک‌ایلروی TMG (یک کامپایلر-کامپایلر) را به زبان TMG روی کاغذ نوشت و «تصمیم گرفت کاغذش را به کاغذش بدهد»؛ او خودش محاسبات را انجام داد و بدین ترتیب کامپایلر TMG را به اسمبلی تبدیل کرد، آن را تایپ کرد و روی PDP-7ِ کن تامپسون اسمبل کرد.

توسعه سیستم گنو تا حد زیادی به GCC (مجموعه کامپایلر گنو) و GNU Emacs (ویرایشگر محبوب) تکیه دارد و این امر، توسعه خودکفا، پایدار و مستمرِ نرم‌افزارهای آزاد برای پروژه گنو را ممکن ساخته است.

بسیاری از زبان‌های برنامه‌نویسی پیاده‌سازی‌های خودمیزبان دارند: کامپایلرهایی که هم به همان زبان نوشته شده‌اند و هم برای همان زبان هستند. یکی از رویکردها، بوت‌استرپ است؛ جایی که نسخه هسته زبان در ابتدا با زبان سطح بالای دیگر، اسمبلر یا حتی زبان ماشین پیاده‌سازی می‌شود؛ سپس از کامپایلرِ حاصل برای ساخت نسخه‌های توسعه‌یافته‌ترِ خودش استفاده می‌گردد.

فهرست زبان‌های دارای کامپایلر خودمیزبان

زبان‌های برنامه‌نویسی زیر دارای کامپایلرهای خودمیزبان هستند:

همچنین ببینید

  • بوت‌استرپ (کامپایلرها)
  • کامپایلر-کامپایلر
  • کامپایلر متقاطع
  • Dogfooding
  • پروجکشن فوتامورا
  • خود-مفسر
  • خود-ارجاعی
  • تغییر غیرمستقیم خود

جمع‌بندی

میزبانی خودی نقطه بلوغ یک نرم‌افزار است؛ جایی که سیستم به استقلال می‌رسد و برای ساخت نسخه‌های جدید، به ابزار بیرونی نیاز ندارد. غلبه بر مشکل بوت‌استرپ با تکنیک‌هایی چون کامپایلر متقاطع، مسیر رشد زبان‌های برنامه‌نویسی و سیستم‌عامل‌ها را هموار کرده و امروزه شاهد پیاده‌سازی‌های خودمیزبان در بسیاری از زبان‌های مدرن هستیم.