لوله‌کشی (یونیکس)

در سیستم‌عامل‌های شبه یونیکس یک خط لوله معادل همان مفهوم کلی خط لوله نرم‌افزار است. این مفهوم به تعدادی پروسه اشاره دارد که به صورت زنجیر وار به یکدیگر متصل هستند. به این صورت که خروجی استاندارد یک فرآیند به ورودی استاندارد فرآیندی بعدی متصل است و به همین ترتیب. هر اتصال به وسیله یک لوله ناشناس پیاده‌سازی شده‌است. این ایده در ابتدا توسط داگلاس مکیلروی ابداع شد و در حقیقت تمثیلی از یک خط لوله انتقال واقعیست. این ایده به سرعت پذیرفته شد و جزء یکی از ارکان اصلی فلسفه یونیکس درآمد. مفهوم لوله‌کشی فرآیند‌ها در زیر نشان داده شده‌است. خط اول حالت کلی و انتزاعی را نشان می‌دهد و خط دوم هم یک مثال واقعی از اتصال سه برنامه ls, grep و more را نشان می‌دهد.

program1 | program2 | program3 
ls -l | grep key | more
یک لوله‌کشی از سه برنامه که بر روی یک ترمینال متنی اجرا می‌شوند.

همانطور که مشاهده می‌کنید، اتصالات از طریق کاراکتر لوله (|) برقرار شده‌است. در مثال بالا، ابتدا ls لیست تمام فایل‌های موجود را از طریق خروجی استاندارد برای دستور grep ارسال می‌کند. دستور grep هم اطلاعات را از ورودی استاندارد خود می‌خواند و تمام خطوطی که حاوی کلمه key هستند را پیدا کرده و برای دستور more ارسال می‌کند. دستور more هم خروجی را صفحه‌بندی کرده و در خروجی استاندارد خود (نمایشگر) چاپ می‌کند.

خط لوله یونیکس را می‌توان مشابه عبارات میانوندی در نظر گرفت که عملوند‌ها برنامه‌ها هستند و عملگر‌ها هم کاراکتر | (pipe) هستند.

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

فراخوان‌های سیستمی

ویرایش

لوله‌ها توسط فراخوان سیستمی pipe()‎ ایجاد می‌شوند. این فراخوان در فایل سرآیند unistd.h به شکل زیر تعریف شده است:

int pipe(int fildes[2]);

این فراخوان سیستمی، یک آرایه دو عنصری از اعداد صحیح دریافت می‌کند و هر عنصر را به یک توصیف‌گر فایل اختصاص می‌دهد. عنصر اول آرایه مخصوص خواندن اطلاعات و عنصر دوم آرایه مخصوص نوشتن اطلاعات است. هر چیزی که در عنصر دوم نوشته شود در عنصر اول قابل خواندن است. معمولاً کار با لوله‌ها به این صورت انجام می‌شود که یک فرآیند به کمک fork()‎ انشعابی از خودش ایجاد می‌کند و به این صورت دو فرآیند مشابه ایجاد می‌شود که یکی فرزند و دیگری والد است و این فرآیند‌ها هر کدام ورودی/خروجی استاندارد خود را دارند. حال یکی از فرآیند‌ها ورودی استاندارد و فرآیند دیگر خروجی استاندارد خود را می‌بندند و سپس انتقال اطلاعات از طریق ورودی/خروجی دیگر صورت می‌پذیرد. مثال زیر این موضوع را نشان می‌دهد:

int pdes[2];
 
pipe(pdes);

if ( fork() == 0 )
{
        /* child */

        close(pdes[1]); /* close standard output */

        read( pdes[0]); /* read from parent */
}       
else    
{       
        /* parent */

        close(pdes[0]); /* close standard input */

        write( pdes[1]); /* write to child */
}

منابع

ویرایش

http://www.cs.cf.ac.uk/Dave/C/node23.html#SECTION002330000000000000000

مشارکت‌کنندگان ویکی‌پدیا. «Pipeline (Unix)». در دانشنامهٔ ویکی‌پدیای انگلیسی، بازبینی‌شده در ۱۴ ژوئیه ۲۰۱۳.