قسمت نهم آموزش لینوکس - Data Flow and File Descriptors
by amin Ghorbani
آشنایی با مفاهیم و کاربرد File Description , Data Flow در لینوکس
ما در لینوکس Streamهای زیادی میبینیم مثلا وقتی شما در Terminal یک دستور اجرا میکنید، یک Stream خروجی خط به خط رو در صفحه Terminalخودتون میبینید. در این بخش قصد داریم کمی بیش از مباحث LPIC به سراغ این مفاهیم بریم و درباره شون یاد بگیریم. این بحث شامل مفاهیم متعددی است که در این قسمت و قسمت 10 به آن ها خواهیم پرداخت مواردی مثل : Streams, File Descriptor, Data Flow, PIPE , Redirection ![[standard-stream.png]] در این قسمت اول میریم سراغ File Descriptorها
File Descriptors
اول از همه بیاید یه مثال ساده بزنیم، “ترمینال لینوکس خودتون رو باز کنید، یه دستور را با کیبورد وارد کنید و سپس دکمه Enter را بزنید، خروجی رو میتونید ببیند که در صفحه ترمینال نمایش داده میشود.” در یونیکس، ورودی بهصورت پیشفرض به کیبورد ترمینال و خروجی به نمایشگر ترمینال وصل است. یونیکس به این معروف است که تقریباً همه چیز را بهصورت یک فایل مدلسازی میکند، از جمله کیبورد و مانیتور. بنابراین نوشتن روی “نمایشگر” در واقع نوشتن روی فایلی است که مدیریت نمایش دادهها روی صفحه را بر عهده دارد. به همین ترتیب، خواندن داده از کیبورد یعنی خواندن داده از فایلی که به عنوان نماینده کیبورد است. در اینجا منظور ما از ورودی (input) و خروجی (output) در حقیقت متنی است که وارد یک process میشود یا از آن خارج میشود. Data Stream یا همان جریان داده در حقیقت Stream ها انجام میشود که بایتها (همان صفر و یک های معروف در کامپیوتر) را از یک بخش به بخش دیگر منتقل میکنند. در اینجا سه جریان داده پیش فرض وجود دارد که عبارتند از:
- stdin
- stdout
- stderr
هر کدام از این سه data stream یک شماره (File Descriptor) دارند که به ترتیب برابر است با 0 و 1 و 2 ، این File Descriptorها در یک جدول به نام file descriptor table ذخیره میشوند، و هر Process جدول خودش را دارد. هر Stream هیچ ایدهای ندارد که دادهای که از طریق File Descriptor ارسال یا خوانده میشود از کجا میآید یا به کجا میرود؛ Streamها فقط با File Descriptor کار دارند، نه با منبع داده. البته به جز 0 و 1 و 2 ، process ممکن است یک File Descriptor دیگر نیز نیاز داشته و ایجاد کند. هر زمان که یک File Descriptor جدید نیاز باشد، پایینترین شمارهٔ استفادهنشده انتخاب میشود. برخی از این File Descriptor ها که در برنامه ها بنا به نیاز ممکن است ساخته شوند عبارتند از:
- File
- PIPE
- Socket
نکته جالب این است که File Descriptorهای مربوط به خطا و خروجی در unix از هم مجزا هستند این موضوع باعث میشود اگر برنامه خطا داد، بتوانید خطاها را جداگانه مدیریت کنید. مثلاً هنگام redirect کردن خروجی یک دستور یا برنامه به یک فایل، میتوانید خطاها را همچنان روی صفحه نمایش نشان دهید
Data Flow
خب حالا آمادهایم که دربارهٔ Data Flow با جزئیات بیشتری صحبت کنیم. ما وقتی در ترمینال فرمانی را اجرا میکنیم، هر ورودی و خروجی باید بهدرستی مدیریت شوند. پردازهای که بعد از وارد کردن هر فرمان ایجاد میشود باید بداند چه دادهای (در صورت وجود) را بهعنوان ورودی دریافت کند و چه دادهای را بهعنوان خروجی ارسال کند. همچنین هر دستور باید بداند این داده را از کجا دریافت کند و به کجا ارسال کند. برای نمایش جریان دادهٔ ورودی (از طریق stdin که پیشفرض آن کیبورد است) و خروجی (از طریق stdout که پیشفرض آن نمایشگر است، و stderr اگر خطایی رخ دهد)، به شکل های زیر توجه کنید:
[Keyboard]
|
v (stdin = 0)
+---------+
| Command |
+---------+
|
v (stdout = 1)
[Terminal Screen]
کیبورد data را به برنامهای که دستور را اجرا میکند میفرستد (از دید برنامه این دادهها از طریق stdin دریافت میشوند)، و برنامه نیز خروجی را از طریق stdout به ترمینال شما میفرستد.
همونطوری که میدونید ما دو DataFlow برای output یا همان خروجی داریم یکی stdout و دیگری stderr برای مثال دستور زیر را در نظر بگیرید:
$ ls dir_x
ls: cannot access 'dir_x': No such file or directory
اینجا خط دوم که خروجی دستور ls است از طریق stderr نمایش داده میشود نه sdtout، چرا؟ چون برنامه ls با Error مواجه شده است و File Descriptor شماره 2 یا همان stderr مسئول خروج پیغام آن از process است. در شکل زیر به خوبی میتوان دید که وقتی شما دستوری را در Terminal وارد میکنید یک Data Flow برای Input و دو Data Flow برای خروجی وجود دارد.
[Keyboard]
|
v
+---------+
| Command |
+---------+
| \
| \
v v
stdout stderr
(1) (2)
| |
v v
Terminal Terminal (error)
برای درک بهتر این موضوع بهتره چند مثال بزنیم:
۱) دستور ls — بدون ورودی، با خروجی
$ ls
dir1
file1
file2
[Keyboard] (only for typing command)
|
v
+---------+
| ls |
+---------+
|
v stdout
[Terminal listing]
۲) mv — بدون ورودی و بدون خروجی (در حالت موفق)
$ mv a b
+---------+
| mv |
+---------+
(no stdin used)
(no stdout)
(no stderr)
دستور mv در صورتی که با خطا روبرو شود
$ mv
mv: missing file operand
+---------+
| mv |
+---------+
|
v stderr
[Terminal error]
ما در لینوکس سه File Descriptor اصلی و پیش فرض داریم، زمانی که شما در ترمینال لینوکس خودتون یه دستور وارد میکنید که قراره یک Process ایجاد کنه ورودی این Process را از طریق stdin بهش منتقل میکنید، Process بعد از انجام کارش وقتی میخواد خروجی رو اصطلاحا Return کنه دو حالت داره در صورتی که به صورت موفق اجرا شده باشه از طریق stdout این خروجی رو بر میگرداند و در صورتی که به هر دلیلی با خطا مواجه شده باشد خطا از طریق stderr باز خواهد گشت.
خب تا اینجا یکم درباره Data Flowها و File Descriptorها آشنا شدیم. در قسمت 10 به سراغ PIPE و Redirection ها میریم که با یادگرفتنشون مطالب این قسمت هم بهتر برامون جا خواهد افتاد.
tags: linux - stdin - stdout - stderr - file_descriptor - data_stream