توصیه شده, مقاله

از نرم‌افزار تا سخت‌افزار – قسمت اول – ساختار کامپایلر

راستش خیلی وقت بود دنبال یه آموزش مناسب بودم که با دیدنش بتونم بفهمم دقیقاً چرا ما از زبون C داریم برای برنامه نویسی میکروکنترلر ها استفاده می‌کنیم و اگه زبون دیگه ای هم به کار میره خیلی از جاها به مشکلاتی میخوره که برای زبون C و ساختار کامپایلر اون اصلاً مشکل به حساب نمیاد؟! خیلی هامون شنیدیم زبون C یه زبون intermediate هستش یا به عبارتی زبونیه که نه خیلی سطح بالاس (مثه پایتون) و نه خیلی سطح پایین (مثه اسمبلی) و همین دلیل هم باعث شده بین برنامه نویس های سخت افزار (به اصطلاح مهندسای امبدد) خیلی محبوب باشه و کلی ازش استفاده شه.

ولی خب سوالی که پیش میاد اینه که این مطلب دقیقاً چه کمکی به ما تو توسعه اپلیکیشن های امبدد می کنه؟ ساختار کامپایلر به چه صورت هست؟

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

پروسه تبدیل برنامه

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

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

بالاخره هرچی باشه کاری رو که ما میتونیم انجام بدیم شاید کامپایلر نتونه به اون خوبی انجام بده!

ساختار و مراحل کامپایل توسط کامپایلر


خب بریم سراغ توضیح ساختار کامپایلر !

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

حالا اگه بخواید فایل های دیگه یا کتابخونه هایی رو هم به پروژه تون اضافه کنید یه سری فایل با پسوند ‎*.h هم خواهید داشت.

تا اینجا کاریه که شما به عنوان برنامه نویس انجام میدین. باقی مراحل رو کامپایلر نصب شده روی سیستم شما براتون انجام میده!

پردازش اولیه فایل ها

کامپایلر با گرفتن این فایل ها در قدم اول فرآیند Preprocessing رو انجام میده.

تو این فرایند یه فایل با پسوند ‎*.i تولید میشه که در واقع همون کدی هستش که شما نوشتید ولی یه سری تغییرات توش داده میشه.

مثلا اینکه شما یه ثابت رو در ابتدای کد با نام TEST تعریف کردید و تو باقی کد هرجا خواستید ازش استفاده کنید به جای نوشتن مقدار اون ثابت صرفا کلمه TEST رو نوشتید.

تو فرایند پیس پردازش تمام جاهایی که شما TEST رو نوشتید مقدار اصلیش رو قرار میده.

البته این مرحله و مراحل بعدی رو ایشالا در ادامه به صورت کاملتر توضیح خواهم داد.

تبدیل تک تک فایل ها به اسمبلی

بعد تولید این فایل کامپایلر وارد عمل میشه و کدهای موجود رو تبدیل میکنه به زبون اسمبلی (هنوز کد برای قرارگیری روی سخت افزار آماده نشده!) و یه فایل با پسوند ‎*.s تولید میکنه.

طبیعتاً برای ترجمه کدهای C به اسمبلی لازمه کامپایلر با ISA مربوط به سخت افزار مورد نظر ما کاملاً آشنا باشه تا بتونه از instruction هایی استفاده کنه که معماری اون پردازنده پشتیبانی میکنه!

خب تو این مرحله اسمبلر کارشو شروع میکنه و فایل اسمبلی ورودی رو تبدیل میکنه به یه object file با پسوند *.o که تقریبا میشه گفت مشابه چیزی هستش که روی سخت افزار پیاده میشه..

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

نهایی شدن فایل هگز

تو قسمت بعدی این سریال، کار Linker شروع میشه. در واقع هدف از وجود لینکر اینه که ما بتونیم تمام فایل‌هایی رو که داریم و این فایل‌ها بعضاً به هم ارتباط هم دارن (مثه include هایی که توی کد انجام میدیم و یه فایل دیگه رو فراخوانی می‌کنیم) تجمیع کنه و به صورت یه فایل شسته رفته بهمون تحویل بده. از اسمش هم تقریباً مشخصه که چی کارس! خروجی این مرحله یه فایل با پسوند ‎‎*.elf هستش.

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

به عبارت بهتر میاد و امکانات سخت افزار رو در اختیار کد قرار میده و فضاهای حافظه (address space) رو به بخش‌های مختلف کد اختصاص میده. خروجی این مرحله هم یه فایل اجرایی هستش که برای معماری‌های مختلف پسوندهاش متفاوت میتونه باشه (برای میکروهای AVR پسوند معروفش ‎*.hex هستش)

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

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

سعید حقیقی پور

درباره سعید حقیقی پور

تا حالا به این فکر کردین که تو یه سیستم کامپیوتری GPU چقدر کارآمد و مهمه ولی به اندازه CPU شناخته شده نیست.یه جورایی همون "مجهولون فی الارض معروفون فی السماء" که میگن! یه حسی بهم میگه کاش بتونم مثه GPU باشم :)

انتشار مطالب با ذکر نام و آدرس وب سایت سیسوگ، بلامانع است.

شما نیز میتوانید یکی از نویسندگان سیسوگ باشید.   همکاری با سیسوگ

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

12 دیدگاه در “از نرم‌افزار تا سخت‌افزار – قسمت اول – ساختار کامپایلر

  1. سجاد Sdrad سجاد گفت:

    با سلام و ممنون به خاطز این مطلب
    میخواستم بدونم دلیل اینکه کامپایلر اول کد رو به اسمبلی تبدیل میکنه و بعد اسمبلر اون کد اسمبلی رو به ماشین کد تبدیل میکنه چیه؟ چرا کامپایلر یکباره کد رو به ماشین کد تبدیل نمیکنه؟

    1. ممنون از سوال خوبتون!
      ببینید مساله ای که هست اینه که کامپایلرهای مختلف روش های مختلفی دارن برای این کار.مثلا وجود دارن کامپایلرهایی مثل MS کامپایلرها که به صورت مستقیم کد زبون ماشین رو تولید میکنند.
      شاید براتون جالب باشه حتی کامپایلرهایی هستند که در خروجی کد رو به یه زبون سطح بالای دیگه تبدیل میکنن.مثلا اولین کامپایلری که برای C++ به کار میرفت ( با نام cfront ) خروجیش کد به زبون C تولید میکرد که باز باید با یه کامپایلر C این کد رو به زبون ماشین ترجمه میکردین
      چیزی که تو این مقاله توضیح داده شده بر مبنای کامپایلر محبوب gcc هستش.

  2. احسان گفت:

    سلام امیدوارم خسته نباشید
    اگ میشه در مقالات بعدی درباره تولید image با فایل های باینری رو هم پروسشو توضیح بدید
    ممنون از مقاله خوبتون

  3. Vz گفت:

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

  4. حسین گفت:

    سلام .
    بسیار عالی .
    موضوع بسیار خوبی رو انتخاب کردید . لطفا ادامه بدید .
    سپاس.

  5. علی گفت:

    سلام ممنون مطلب عالی بود منتظر ادمه اش هستم
    تو اخر نوشه GPUمهمه اگه ممکنه در این مورد هم یه توضیحی بفرمایین
    خیلی ممنون

    1. خواهش میکنم
      راجع به GPU خب قطعا میدونید تو رایانه های امروزی به عنوان هسته پردازشی کارای گرافیکی استفاده میشه.دلیل این موضوع هم برمیگرده به توانایی این تراشه در انجام عملیات های مختلف به صورت موازی.فرض کنید چندین هزار CPU (البته با توانایی های خیلی محدود) رو کنار هم بذارید تا باهم کار کنند.کار کردن با این تراشه معمولا نیاز به برنامه نویسی موازی داره (به زبون cuda اغلب برای تراشه های شرکت NVIDIA).ایشالا تصمیم دارم یه مقاله راجع به برنامه نویسی موازی برای تراشه های GPU هم بذارم.در واقع برنامه نویسی موازی یه نوع جدید از تفکر برنامه نویسی رو میطلبه که واقعا هیجان انگیزه!

  6. Clooner82 Clooner گفت:

    سلام . ممنون از توضیحاتتون . آیا ممکنه برنامه با کد هگز رو برگردوند به c ؟ اگر نمیشه میشه دلیلش رو بگید؟ چرا ممکن نیست که نرم افزاری بتونه همین مراحل رو برگرده و فایل هگز رو به c تبدیل کنه؟

    1. معمولا تو پردازنده های مختلف از این کار جلوگیری میکنن ولی خب امکان هک کردن وجود داره.من نرم افزاری که این کار رو انجام بده ندیدم.واضح ترین دلیل این موضوع هم میتونه رعایت حق برنامه نویس به منظور حفاظت از کد باشه.حتی بعضی از تراشه های ARM امکانی رو دارند که اجازه نمیدن کدی که روی یک پردازنده اجراشده روی پردازنده دقیقا مشابه از همون خانواده اجرا بشه.

    2. zeus zeus گفت:

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

      1. Clooner82 Clooner گفت:

        ممنون . توضیحاتتون عالی و کامل بود🙏