منبع اصلی نوشتار زیر در این لینک قرار دارد

همه چیز در یونیکس، یک فایل است

نویسنده مهمان، امیررضا قادری: به کرّات شنیده‌اید که گفته می‌شود «همه چیز در یونیکس، یک فایل است». خیلی‌ها از چنین عبارتی استفاده می‌کنند بدون اینکه دقیقا با معنی آن آشنا باشند. خیلی‌ها حتی شاید ندانند که وقتی از «فایل» صحبت می‌کنیم، منظورمان چه چیزی است! در این نوشته سعی خواهیم کرد با زبانی ساده این مفاهیم را توضیح دهیم و شما را با میزان اهمیت فلسفه‌ی «همه چیز در یونیکس، یک فایل است» آشنا کنیم.

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

عملیات I/O چیست؟ ارتباط CPU‌ با دنیای خارج. همین!

منظورمان از دنیای خارج، هر منبعی است که CPU‌ به آن دسترسی مستقیم نداشته باشد. برای مثال هارد دیسک، ماوس، کیبورد، مودم، پرینتر، کارت شبکه، و… خود واژه‌ی I/O‌ هم کوتاه شده‌ی واژه‌ی Input/Output یا «ورودی/خروجی» است. از آنجایی که CPU به حافظه‌ی اصلی (RAM) و حافظه‌های داخلی خودش مانند Register ها و حافظه‌ی Cache دسترسی مستقیم دارد، ارتباط با این منابع جزو I/O به حساب نمی‌آید.

فایل چیست؟

فایل در واقع چیزی بیشتر از یک مجموعه Byte های کنار هم چیده شده نیست. «قالب» (Format) یک فایل، مشخص کننده شیوه‌ی چینش این Byte ها در کنار یکدیگر است. به زبانی دیگر، Format یک فایل مشخص می‌کند که Byte ها باید چگونه در کنار یکدیگر قرار گیرند تا بیانگر داده‌ایی قابل فهم و به دردبخور باشند. مثلا Format یک فایل متنی با یک فایل صوتی متفاوت است. با اینکه هردوی آن ها چیزی بیشتر از Byte‌ های کنار هم چیده شده نیستند، اما برنامه‌ی ویرایشگر متن شما نمی‌تواند با یک فایل صوتی کار کند چرا که Format آن برایش قابل فهم نیست. ارتباط با یک فایل (خواندن و نوشتن روی آن)، از طریق برقراری جریانی از Byte ها با آن فایل صورت می‌گیرد.

منظورمان از «جریان» چیست؟

در مطالب مربوط به برنامه‌نویسی، ‌از عبارت «جریان» ( Stream ) بسیار استفاده ‌می‌شود: جریانی از بایت‌ها، جریانی از اعداد، جریانی از رشته‌ها، جریانی از کدها و … در اغلب اوقات می‌توانید مفهوم عبارت «جریان» در این گونه از مطالب را همانند جریان آبی که در یک رود جاریست در نظر بگیرید! بنابراین وقتی می‌گوییم جریانی از Byte‌ ها، عین این که مسیری مانند یک رود داشته باشیم ولی به جای جاری کردن آب در آن، دنباله‌ایی از Byte‌ ها را در آن جاری کنیم.

مثلا وقتی قرار است داده ایی را به یک فایل منتقل کنیم، میتوانید در ذهن خود اینطور تصور کنید که مسیری را به طرف آن فایل ایجاد نموده‌ایم و Byte ها را به طور متوالی و پشت سرهم در این مسیر سرازیر کرده‌ایم. آن فایل هم همانند حوضچه‌ای است که در انتهای این مسیر قرار دارد و با جریانی از Byte‌ ها که به طرف آن سرازیر کرده‌ایم در حال پر شدن است.

Everything-is-a-file

همه چیز در یونیکس، یک فایل است!

هر سیستم عامل باید به نحوی امکان انجام I/O را برای پروسه‌ها فراهم نماید. تا قبل از یونیکس، روش واحدی برای انجام I/O‌ در سیستم عامل‌ها وجود نداشت. هر منبع I/O ممکن بود به رابط (API) خاص خودش برای ارتباط با سیستم عامل نیاز داشته باشد. برای مثال، ممکن بود نوشتن یک فایل متنی در هارد دیسک و فرستادن همان فایل به پرینتر به دو رابط کاملا متفاوت نیاز داشته باشد.

یونیکس برای یکپارچه‌سازی و ساده‌تر شدن اعمال I/O،  تمام منابع I/O را به صورت فایل در نظر میگیرد. به این ترتیب می توان از یک مجموعه API  واحد برای ارتباط با طیف وسیعی از منابع I/O بهره برد. در این شیوه، کرنل منابع مختلف I/O مانند هارد دیسک، کیبورد، پرینتر، و… را به شکل یک فایل،  و از طریق «توصیف‌گر فایل» (File descriptor) در اختیار پروسه‌ها قرار می‌دهد.

توصیف‌گر فایل یا File descriptor چیست؟

هنگامی که پروسه‌ای‌ خواهان ارتباط با یک فایل است، کرنل آن فایل را در حافظه باز میکند و توسط یک File descriptor ارتباط بین پروسه‌ی درخواست کننده و فایل مورد نظر را برقرار می‌سازد. File descriptor (که از این بعد با نام FD ‌از آن یاد خواهیم کرد) ‌ در واقع یک عدد صحیح (integer)  است که به عنوان یک شاخص، برای هر یک از فایل‌های باز شده در حافظه در نظر گرفته می‌شود.

در هر لحظه ممکن است هزاران فایل باز شده وجود داشته باشند که هر کدام به کمک FD خاص خودشان قابل شناسایی هستند. کرنل از FD برای انتقال اطلاعات استفاده میکند: وقتی پروسه‌ایی میخواهد اطلاعات جدیدی را در یک فایل بنویسد، این اطلاعات را به FD آن فایل (شماره مخصوص آن فایل) ارسال میکند. کرنل از روی این شماره تشخیص میدهد که باید این اطلاعات را به کدام فایل منتقل نماید. همچنین هنگام خواندن از فایل نیز، کرنل از طریق همین شماره تشخیص میدهد که اطلاعات خوانده شده از فایل را باید به کدام پروسه منتقل کند. به این ترتیب کرنل تصمیم میگیرد که جریان Byte ها را به کدام سمت سرازیر نماید.

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

FD های استاندارد

جدای از اینکه یک پروسه در طول پردازش خود درخواست I/O داشته باشد یا نه، کرنل به طور پیش‌فرض سه FD با شماره‌های زیر را برای تمام پروسه‌ها ایجاد خواهد کرد:

    شماره‌ی ۰ :  ورودی استاندارد (stdin)

    شماره‌ی ۱ : خروجی استاندارد (stdout)

    شماره‌ی ۲ : جریان استاندارد خطا (stderr)

این سه FD استاندارد که برای تمام پروسه‌ها ساخته می‌شوند، این امکان را فراهم می‌کنند که بتوانیم با خود پروسه‌ها نیز همانند فایل برخورد کنیم! مثلا اگر می‌خواهیم اطلاعاتی را به آن پروسه انتقال دهیم، باید داده‌ها را به ورودی استاندارد آن پروسه ارسال کنیم. بنابراین «پروسه‌ی ارسال‌کننده»، می‌تواند «پروسه‌ی مقصد» را درست همانند یک فایل تصور ‌کند. با داشتن چنین قابلیتی، می‌توان علاوه بر منابع I/O، با منابع نرم افزاری مانند Socket ها، Pipe ها و بسیاری از منابع دیگر هم همانند یک فایل برخورد کنیم.

 جمع بندی

جدای از اینکه فلسفه‌ی «همه چیز در یونیکس یک فایل است» به میزان زیادی به پکپارچه شدن و ساده تر شدن سیستم کمک کرده، یک قابلیت دیگر را هم با خودش به ارمغان آورده است. در یونیکس، می‌توان به شیوه‌ی موثری برای فایل‌ها حق دسترسی تعیین کرد. میدانم که دارید به چه چیزی فکر میکنید:  وقتی با سیستمی روبرو هستید که همه چیز در آن یک فایل است، پس می‌توان برای همه چیز حق دسترسی تعیین کرد! درست است، شهرت امنیتی یونیکس روی همین ایده‌ی ساده بنا شده است. شاید سیستم امنیتی یونیکس را چیزی فراتر از ذهن یا ضریب هوشی خود تصور می‌کردید، ولی در واقعیت می‌بینید که این سیستم ساده تر از آن چیزی است که فکر میکنید.

دقت کنید در حالی که چنین ایده ایی از دهه‌ی هفتاد (میلادی) در یونیکس وجود داشته، امروزه سیستم عامل هایی هستند( !؟ ) که هنوز هم نتوانستند چنین امکاناتی را به درستی پیاده سازی کنند. جالب تر این است که منشا این قابلیت‌ها تماما از سادگی یونیکس ناشی می‌شود. اینکه چطور توانسته‌اند با این ایده های ساده و کنار هم قرار دادن برنامه‌های کوچک، چنین سیستم‌ای را خلق کنند، چیزی است که باید پاسخ آن را در ذهن «کن تامپسون» و «دنیس ریچی» و بقیه دوستانشان در آزمایشگاه‌های Bell جستجو کرد!

ــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــ اگر این مطلب را دوست داشتید می‌توانید مشترک خوراک‌خوان رایت‌وب شوید تا از این پس همه مطالب بعدی رایت‌وب را در خبرخوانتان،بخوانید... ــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــ



برچسب ها : , ,