برنامه نویسی Pipe در گنو/لینوکس (قسمت اول)(850 مجموع کلمات موجود در متن) (8327 بار مطالعه شده است) برنامه
نویسی Pipe
در
گنو/لینوکس
(قسمت
اول)
pipe
یک
ابزار ارتباطی است کــه امکان ارتباطات
یک طرفه را فراهم میسازد.
داده
هایی کــه در طـــرف نــوشتنی pipe
نوشته
میشوند از طرف دیگر آن
که طرف خواندنی نامیده میشود
قابل خواندن است.
در
واقـــع شمـا باید لولـهای
را در نظـــر بگیرید که ارتباط دیتا بین
دو ناحیه از طریق آن صورت میپذیرد.
ایــن
لــولــه دادهها را
بـــه همـان ترتیبی که دریافت میکند،
به ناحیه ی دیگر منتقل میکند،
از این رو از آن به عنوان یک وسیله ی سریال
یا ترتیبی یاد میشود.
استفاده
از pipe
برای
ایجاد ارتباط بین دو نخ در یک پروسه یا
ارتباط بین پروسه پدر و پروسه فرزند ،صورت
میپذیرد.
در
پوستههای فرمان، نشانهی
| یک
pipe ایجاد
میکند.
مثلاً
دستور زیر باعـــث ایـجاد دو پروسه ی
فرزند میشود؛ یکی برای
ls و
دیگری برای less:
$ ls | less
فرمان
بالا همچنین یــک pipe
برای
مرتبط کردن خـــروجـی استاندارد پروسه
ی فرزند ls
به
ورودی استاندارد پروسه فرزند less
ایجاد
میکند؛ نام فایل های
لیست شده توسط ls،
بــه less
فرستاده
میشود، بــه تــرتیبی
کــه انـگار مستقیماً به ترمینال فرستاده
شدهاند.
ظرفیت
یک pipe برای
دادهها محدود است.
اگر
پروسهای کـــه دیـتــا
را درون pipe
مینویسد
با نرخی سریع تر از پروسه ای کــه دیتا را
از pipe
میخواند
عمل کند و در صورتی که pipe
ظــرفیت
نگهداری دیتای بیشتر نداشته باشد، پـروسه
ی دیتا نویس تا زمانی که ظرفیت بیشتر آزاد
شود، مسدود یا به اصطلاح بلوکه میشود.
در
صورتی که پروسه ی دیتا خوان درون pipe
دیتایی
برای خواندن نیابد ، مسدود میشود تا این
که بالاخره دیتایی فراهم شود ؛ در نتیجه
pipe باعث
هم روند شدن دو پروسه میشود
.
(Synchronization) .
ساختن
pipe
ها برای
ساختن pipe
از
دستــور pipe
استفاده
کنید. یــک
آرایه با اندازه ۲ از نوع عدد صحیح هــم
فـراهم کنید.
دستور
pipe
شاخص
فایل خواننده را در محل صفر و شاخص فایل
نویسنده را در محل شماره یک آرایه ذخیره
میکند.
بـرای
مثال، کــد زیر را در نظر بگیرید:
- int
pipe_fds[2];
-
int read_fd;
-
int write_fd;
-
pipe (pipe_fds);
-
read_fd = pipe_fds[0];
-
write_fd = pipe_fds[1];
دیتای نوشته
شده به شاخص فایل read_fd
، از
شاخص فایل write_fd
قابل
باز خوانی است.
بر
قراری ارتباط بین پروسه پدر و
فرزند فـــراخــوانی
pipe در
یک پروسه ، شاخصهای
فایلی ایجاد میکند کــه
فقط مابین آن پروسه و فرزندانش معتبر
هستند.
شاخصهای
فایل یک پروسه قابل فرستادن بـــه پروسههای
نامــربوط نیست.
امـا
وقتی که یک پروسه fork
را
فراخوانی میکند، ایـن
شاخص ها برای فرزندان جدید پروسه کپی
میشود.
در
نتــیجه pipe
فقط
میتواند پروسه های
مربوط را به هم وصل کند.
در
نمونه برنامه ۱، fork
پروسه
فرزند جـــدیدی ایجاد میکند؛
فرزند جدید، شاخص های فایل را از پدر به
ارث میبرد .
پدر
رشته ای از کاراکتر ها را توی pipe
مینویسد
و فرزند، آنها را بازخوانی میکند.
نمونه
برنامه ما شاخصهای فایل
را بـــا بــکـــارگیــری fdopen،
به جـریانهای *
FILE تبدیل
میکند (واژه
جریان ، معادل stream
است).
از
آنجا کـــه به جای شاخصهای
فایل از جریان ها استفاده میکنیم،
امکان استفاده از توابع ورودی-خروجی
سطح بالاتر کتابخانه استاندارد زبان C،
مانند printf
و fgets
وجود
دارد.
نمونه
برنامه ۱:
کاربرد
pipe
برای
ارتباط با پروسه فرزند
- #include
<stdlib.h>
#include <stdio.h> #include <unistd.h> -
/* Write COUNT copies of MESSAGE to
STREAM, pausing for a second
between each. */ -
void writer (const char* message,
int count, FILE* stream)
{ for (; count > 0; --count) { /*
Write the message to the stream, and send it off immediately.
*/ fprintf (stream, “%s\n”, message); fflush (stream); -
/* Snooze a while. */
sleep
(1); } } /* Read random strings from the stream as long as
possible. */ void reader (FILE* stream) { char
buffer[1024]; -
/* Read until we hit the end of the
stream. fgets reads until
either a newline or the end-of-file. */ -
while (!feof (stream) &&
!ferror (stream) && fgets (buffer, sizeof (buffer), stream)
!= NULL){
fputs (buffer, stdout); } -
int main ()
{ -
int fds[2];
pid_t pid; -
/* Create a pipe. File descriptors
for the two ends of the pipe are
placed in fds. */ -
pipe (fds);
-
/* Fork a child process. */
-
pid = fork ();
if (pid ==
(pid_t) 0) { FILE* stream; -
/* This is the child process. Close
our copy of the write end of
the file descriptor. */ close
(fds[1]); -
/* Convert the read file descriptor
to a FILE object, and read
from it. */ -
stream = fdopen (fds[0],
“r”);
reader (stream); close (fds[0]); } -
else {
-
/* This is the parent process.
*/
FILE* stream; -
/* Close our copy of the read end
of the file descriptor. */
-
close (fds[0]);
-
/* Convert the write file
descriptor to a FILE object, and write
to it. */ -
stream = fdopen (fds[1],
“w”);
writer (“Hello, world.”, 5, stream); close
(fds[1]); } return 0; }
در
ابتدای تابع main،
آرایهای از اعداد صحیح
به نام fds
و با
اندازه ۲، اعلان شده است.
فــراخوانی
تابع pipe
پس از
ایجاد pipe
، شاخص
های فایل خواندنی و نوشتنی را در آن آرایه
ذخیره میکند.
برنامهما
در ادامه یک پروسه فرزند ایجاد میکند.
پس از
بستن سر خواندنی pipe،
پروسه ی پدر شروع به نوشتن رشته ها به درون
pipe کرده
و پس از بستن سر نوشتنی pipe،
پروسه فرزند رشته ها را از pipe
میخواند.
منظور
از بستن سر pipe،
تعیین مرز برای فضایی از حافظه است که به
عنوان pipe
باید
مورد استفاده قرار گیرد.
توجه
کنید که پروسهی پــدر
در تابع writer
پس از
نوشتن در pipe،
تابع fflush
را
فـــراخــوانی و بدین وسیله pipe
را
وادار میکند تا رشته
را بدون درنگ ارسال نماید.
هنگامی
کــه فرمان ls
| less را
اجرا میکنید، دو fork
انجـــام
میشود؛ یــکــی بــرای
پـــروسه ی فرزند ls
و دیگری
برای پروسهی فرزند
less. هر
دوی این فرزندان شاخص های فایل pipe
را به
ارث میبرند طوری که
بتوانند از طریق pipe
ارتباط
برقرار کنند.
برای
برقراری ارتباط بین پروسه های نا مرتبط
از FIFO باید
استفاده کرد که در ادامه بدان پرداخته
خواهد شد.
حمید
نصیبی h.nassiby@gmail.com
PDF Version
|