سلام!
اگر تا حالا فکر کردید یک برنامه مثل ترمینال یا CMD چطور کار میکند، این مقاله را از دست ندهید. من یک شل ساده نوشتم که میخواهم به زبان خیلی ساده و خودمانی به شما بگویم این برنامه چطور کار میکند و چطور با چند خط کد، یک شل کوچک و کاربردی ساختم. این پروژه بیشتر برای یادگیری و تمرین خودم بود، ولی فکر کردم بد نیست تجربهام را با شما هم به اشتراک بگذارم. اگر علاقه دارید میتوانید کد را در گیتهاب من هم ببینید و از آن استفاده کنید.
در این مقاله قدم به قدم مراحل نوشتن شل را توضیح میدهم و نکاتی که در این مسیر یاد گرفتم را با شما درمیون میگذارم.

شل در واقع برنامهای است که ما با آن ارتباط برقرار میکنیم تا سیستمعامل دستورات ما را اجرا کند. وقتی در ترمینال دستور مینویسیم، مثلاً ls یا cd، این دستور به شل میرسد، شل آن را میفهمد، به سیستمعامل میگوید اجرا کند و بعد نتیجه را به ما نمایش میدهد.
شل مثل یک مترجم بین ما و سیستمعامل عمل میکند. علاوه بر این، شل میتواند برنامههای مختلف را با هم ترکیب کند، دستورات پیچیدهتر بسازد و کارهای دیگری مثل مدیریت فایلها و فرآیندها را آسانتر کند.cmd در ویندوز و bash در لینوکس نمونههای معروف شل هستند.
شاید بپرسید، خب خیلی شل آماده هست، چرا من رفتم و یک شل ساده خودم نوشتم؟ راستش هدف اصلی من یادگیری بود. میخواستم بفهمم برنامههای خط فرمان چطور کار میکنند و چطور دستورات را اجرا میکنند.
علاوه بر این، ساختن یک شل ساده تمرین خوبی برای یادگیری مفاهیم مهمی مثل فرآیندها، فراخوانیهای سیستمی و مدیریت ورودی و خروجی در سیستمعامل بود. ضمن اینکه این شل را با زبان C نوشتم که به سیستم نزدیکتر است و دسترسی کاملتری به منابع سیستم دارد.
حالا باید بدانیم اصلاً چه چیزی میخواهیم بسازیم و چه تفاوتهایی دارد؟ شل ما سادهترین نوع شل است که برای سیستمعاملهای یونیکسی (UNIX-like) مانند لینوکس و BSD نوشته شده است.
برای زبان برنامهنویسی، من از C استفاده کردم چون اولاً معروفترین زبان برنامهنویسی سیستمی دنیاست و سازگاری خیلی خوبی با UNIX دارد. اکثر شلهای دیگر هم با C نوشته شدهاند.
زبان C برخلاف بسیاری از زبانهای برنامهنویسی، چندین کامپایلر دارد؛ یعنی هر تیم یا گروهی ممکن است یک کامپایلر C اختصاصی ساخته باشد. تیم گنو (GNU) کامپایلر خودشان به نام gcc را ساخت که برای محیطهای UNIX بهینه شده است.
ما هم از همان کامپایلر استفاده میکنیم.
برای نصب آن در اوبونتو میتوانید از دستور زیر استفاده کنید:
sudo apt install gcc
خب، بریم کدنویسی را شروع کنیم. ابتدا باید هدر فایلهای مورد نیاز را include کنیم و تابع main را بنویسیم.

سه include اول مربوط به کتابخانههای پایه زبان C هستند که توابعی مثل کار با رشتهها، ورودی/خروجی و توابع کمکی را فراهم میکنند. دو هدر بعدی unistd.h و sys/wait.h مربوط به سیستمکالهای لینوکس برای ارتباط با سیستمعامل و مدیریت وقفهها هستند که در ادامه بیشتر با آنها آشنا میشویم.
گفتیم که شل دستورات ما را اجرا میکند، اما چطور؟ شل نام هر برنامهای که مینویسید را پیدا میکند و به عنوان یک فرآیند فرزند (subprocess) خودش اجرا میکند.
پس اولین قدم این است که یاد بگیریم چطور یک فرآیند فرزند ایجاد کنیم.

تابع fork برنامه را به دو شاخه پدر و فرزند تقسیم میکند. در شاخه فرزند عدد صفر برمیگرداند. شرط بررسی میکند اگر عدد صفر بود، با استفاده از تابع execvp برنامهای که به شل داده شده را اجرا میکند. تابع wait در انتهای کد باعث میشود شاخه پدر منتظر پایان کار فرزند بماند.
برای اینکه بتوانیم نام برنامه را دریافت و پردازش کنیم باید یک حلقه بینهایت بنویسیم که در نهایت کد برنامه چیزی شبیه به این میشه:

برای دیدن پروژه ی کامل هم میتونید به گیت هاب پروژه ی من به آدرس https://github.com/mohammadmehdisharife/bare-shell.git مراجعه کنید. ❤️