الخميس، 1 أكتوبر 2020

الاتصال بين العمليات - الأنابيب

 

الاتصال بين العمليات - الأنابيب


الاتصال بين العمليات - الأنابيب


الإعلانات
Ad by Valueimpression

الأنبوب هو وسيلة اتصال بين عمليتين أو أكثر مترابطة أو مترابطة. يمكن أن يكون إما ضمن عملية واحدة أو اتصال بين الطفل والعمليات الأم. يمكن أن يكون الاتصال أيضًا متعدد المستويات مثل الاتصال بين الوالد والطفل والحفيد ، وما إلى ذلك. يتم تحقيق الاتصال من خلال عملية واحدة الكتابة في الأنبوب والقراءة الأخرى من الأنبوب. لتحقيق استدعاء نظام الأنابيب ، قم بإنشاء ملفين ، أحدهما للكتابة في الملف والآخر للقراءة من الملف.

يمكن رؤية آلية الأنابيب من خلال سيناريو في الوقت الفعلي مثل ملء الماء بالأنبوب في بعض الحاويات ، على سبيل المثال دلو ، وشخص ما يسترجعها ، على سبيل المثال مع كوب. عملية التعبئة ليست سوى الكتابة في الأنبوب وعملية القراءة ليست سوى استرداد من الأنبوب. هذا يعني أن أحد المخرجات (الماء) هو مدخل للآخر (دلو).

الأنابيب مع واحد
#include<unistd.h>

int pipe(int pipedes[2]);

سيؤدي استدعاء النظام هذا إلى إنشاء أنبوب للاتصال أحادي الاتجاه ، أي أنه ينشئ واصفين ، الأول متصل للقراءة من الأنبوب والآخر متصل للكتابة في الأنبوب.

أنابيب الواصف [0] للقراءة و pipedes [1] للكتابة. كل ما هو مكتوب في pipedes [1] يمكن قراءته من pipedes [0].

هذه المكالمة سترجع صفرًا عند النجاح و -1 في حالة الفشل. لمعرفة سبب الفشل ، تحقق من متغير errno أو وظيفة perror ().

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

على الرغم من أن العمليات الأساسية للملف تتم قراءتها وكتابتها ، فمن الضروري فتح الملف قبل إجراء العمليات وإغلاق الملف بعد الانتهاء من العمليات المطلوبة. عادة ، افتراضيًا ، يتم فتح 3 واصفات لكل عملية ، والتي تُستخدم للإدخال (الإدخال القياسي - stdin) ، والإخراج (الإخراج القياسي - stdout) والخطأ (الخطأ القياسي - stderr) الذي يحتوي على واصفات الملفات 0 و 1 و 2 على التوالي.

سيعيد استدعاء النظام هذا واصف ملف يستخدم لمزيد من عمليات الملف للقراءة / الكتابة / البحث (lseek). عادةً ما تبدأ واصفات الملفات من 3 وتزداد برقم واحد مع زيادة عدد الملفات المفتوحة.

الوسيطات التي تم تمريرها لفتح استدعاء النظام هي اسم المسار (مسار نسبي أو مطلق) ، علامات تشير إلى الغرض من فتح الملف (على سبيل المثال ، الفتح للقراءة ، O_RDONLY ، للكتابة ، O_WRONLY ، للقراءة والكتابة ، O_RDWR ، للإلحاق بالملف الموجود O_APPEND ، لإنشاء ملف ، إذا لم يكن موجودًا مع O_CREAT وما إلى ذلك) والوضع المطلوب الذي يوفر أذونات القراءة / الكتابة / التنفيذ للمستخدم أو المالك / المجموعة / الآخرين. يمكن ذكر الوضع مع الرموز.

اقرأ - 4 ، اكتب - 2 ونفذ - 1.

على سبيل المثال: القيمة Octal (تبدأ بـ 0) ، تشير 0764 إلى أن المالك قد قرأ أذونات القراءة والكتابة والتنفيذ ، وقامت المجموعة بالقراءة والكتابة ، وأذونات القراءة الأخرى. يمكن أيضًا تمثيل هذا كـ S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH ، مما يعني أو تشغيل 0700 | 0040 | 0020 | 0004 → 0764.

يقوم استدعاء النظام هذا ، عند النجاح ، بإرجاع معرف واصف الملف الجديد و -1 في حالة حدوث خطأ. يمكن تحديد سبب الخطأ باستخدام متغير errno أو دالة perror ().

#include<unistd.h>

int close(int fd)

استدعاء النظام أعلاه إغلاق واصف الملف المفتوح بالفعل. هذا يعني أن الملف لم يعد قيد الاستخدام ويمكن إعادة استخدام الموارد المرتبطة بأي عملية أخرى. يُرجع استدعاء النظام هذا صفرًا عند النجاح و -1 في حالة الخطأ. يمكن تحديد سبب الخطأ باستخدام متغير errno أو دالة perror ().

#include<unistd.h>

ssize_t read(int fd, void *buf, size_t count)

استدعاء النظام أعلاه هو القراءة من الملف المحدد مع وسيطات واصف الملف fd ، ومخزن مؤقت مناسب مع ذاكرة مخصصة (إما ثابتة أو ديناميكية) وحجم المخزن المؤقت.

معرف واصف الملف هو تحديد الملف المعني ، والذي يتم إرجاعه بعد استدعاء استدعاء النظام open () أو pipe (). يجب فتح الملف قبل القراءة من الملف. يفتح تلقائيًا في حالة استدعاء نظام الأنبوب ().

سيعيد هذا الاستدعاء عدد البايت المقروء (أو صفر في حالة مواجهة نهاية الملف) عند النجاح و -1 في حالة الفشل. يمكن أن تكون وحدات البايت التي تم إرجاعها أصغر من عدد البايت المطلوب ، فقط في حالة عدم توفر بيانات أو إغلاق الملف. يتم تعيين رقم الخطأ الصحيح في حالة الفشل.

لمعرفة سبب الفشل ، تحقق من متغير errno أو وظيفة perror ().

#include<unistd.h>

ssize_t write(int fd, void *buf, size_t count)

استدعاء النظام أعلاه هو الكتابة إلى الملف المحدد مع وسيطات واصف الملف fd ، ومخزن مؤقت مناسب بذاكرة مخصصة (إما ثابتة أو ديناميكية) وحجم المخزن المؤقت.

معرف واصف الملف هو تحديد الملف المعني ، والذي يتم إرجاعه بعد استدعاء استدعاء النظام open () أو pipe ().

يجب فتح الملف قبل الكتابة إلى الملف. يفتح تلقائيًا في حالة استدعاء نظام الأنبوب ().

سيعيد هذا الاستدعاء عدد البايت المكتوب (أو صفر في حالة عدم كتابة أي شيء) عند النجاح و -1 في حالة الفشل. يتم تعيين رقم الخطأ الصحيح في حالة الفشل.

لمعرفة سبب الفشل ، تحقق من متغير errno أو وظيفة perror ().

أمثلة على البرامج

فيما يلي بعض الأمثلة على البرامج.

برنامج المثال 1 - برنامج لكتابة وقراءة رسالتين باستخدام الأنابيب.

الخوارزمية

الخطوة 1 - قم بإنشاء أنبوب.

الخطوة 2 - إرسال رسالة إلى الأنبوب.

الخطوة 3 - استرجع الرسالة من الأنبوب واكتبها إلى الإخراج القياسي.

الخطوة 4 - أرسل رسالة أخرى إلى الأنبوب.

الخطوة 5 - استرجع الرسالة من الأنبوب واكتبها إلى الإخراج القياسي.

ملاحظة - يمكن أيضًا استرداد الرسائل بعد إرسال جميع الرسائل.

كود المصدر: simplepipe.c

#include<stdio.h>
#include<unistd.h>

int main() {
   int pipefds[2];
   int returnstatus;
   char writemessages[2][20]={"Hi", "Hello"};
   char readmessage[20];
   returnstatus = pipe(pipefds);
   
   if (returnstatus == -1) {
      printf("Unable to create pipe\n");
      return 1;
   }
   
   printf("Writing to pipe - Message 1 is %s\n", writemessages[0]);
   write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
   read(pipefds[0], readmessage, sizeof(readmessage));
   printf("Reading from pipe – Message 1 is %s\n", readmessage);
   printf("Writing to pipe - Message 2 is %s\n", writemessages[0]);
   write(pipefds[1], writemessages[1], sizeof(writemessages[0]));
   read(pipefds[0], readmessage, sizeof(readmessage));
   printf("Reading from pipe – Message 2 is %s\n", readmessage);
   return 0;
}

ملاحظة - من الناحية المثالية ، يجب التحقق من حالة الإرجاع لكل مكالمة نظام. لتبسيط العملية ، لا يتم إجراء الفحوصات لجميع المكالمات.

خطوات التنفيذ

التحويل البرمجي

gcc -o simplepipe simplepipe.c

التنفيذ / الإخراج

Writing to pipe - Message 1 is Hi
Reading from pipe – Message 1 is Hi
Writing to pipe - Message 2 is Hi
Reading from pipe – Message 2 is Hell

برنامج المثال 2 - برنامج لكتابة وقراءة رسالتين من خلال الأنبوب باستخدام عمليات الوالدين والفرع.

الخوارزمية

الخطوة 1 - قم بإنشاء أنبوب.

الخطوة 2 - إنشاء عملية تابعة.

الخطوة 3 - عملية الأصل يكتب على الأنبوب.

الخطوة 4 - تسترجع عملية الطفل الرسالة من الأنبوب وتكتبها في الإخراج القياسي.

الخطوة 5 - كرر الخطوة 3 والخطوة 4 مرة أخرى.

كود المصدر: pipewithprocesses.c

#include<stdio.h>
#include<unistd.h>

int main() {
   int pipefds[2];
   int returnstatus;
   int pid;
   char writemessages[2][20]={"Hi", "Hello"};
   char readmessage[20];
   returnstatus = pipe(pipefds);
   if (returnstatus == -1) {
      printf("Unable to create pipe\n");
      return 1;
   }
   pid = fork();
   
   // Child process
   if (pid == 0) {
      read(pipefds[0], readmessage, sizeof(readmessage));
      printf("Child Process - Reading from pipe – Message 1 is %s\n", readmessage);
      read(pipefds[0], readmessage, sizeof(readmessage));
      printf("Child Process - Reading from pipe – Message 2 is %s\n", readmessage);
   } else { //Parent process
      printf("Parent Process - Writing to pipe - Message 1 is %s\n", writemessages[0]);
      write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
      printf("Parent Process - Writing to pipe - Message 2 is %s\n", writemessages[1]);
      write(pipefds[1], writemessages[1], sizeof(writemessages[1]));
   }
   return 0;
}

خطوات التنفيذ

التحويل البرمجي

gcc pipewithprocesses.c –o pipewithprocesses

إعدام

Parent Process - Writing to pipe - Message 1 is Hi
Parent Process - Writing to pipe - Message 2 is Hello
Child Process - Reading from pipe – Message 1 is Hi
Child Process - Reading from pipe – Message 2 is Hello

اتصال ثنائي الاتجاه باستخدام الأنابيب

يُنظر إلى اتصال الأنابيب على أنه اتصال أحادي الاتجاه فقط ، أي إما أن تكتب العملية الأم وتقرأ العملية الفرعية أو العكس ولكن ليس كلاهما. ومع ذلك ، ماذا لو احتاج كل من الوالد والطفل إلى الكتابة والقراءة من الأنابيب في وقت واحد ، فإن الحل هو اتصال ثنائي الاتجاه باستخدام الأنابيب. يلزم وجود أنبوبين لإنشاء اتصال ثنائي الاتجاه.

فيما يلي خطوات تحقيق اتصال ثنائي الاتجاه -

الخطوة 1 - إنشاء أنبوبين. الأول هو أن يكتب الوالد ويقرأه الطفل ، على سبيل المثال أنبوب 1. الثاني هو أن يكتب الطفل ويقرأه الوالد ، على سبيل المثال أنبوب 2.

الخطوة 2 - إنشاء عملية تابعة.

الخطوة 3 - أغلق النهايات غير المرغوب فيها حيث لا يلزم سوى طرف واحد لكل اتصال.

الخطوة 4 - أغلق الأطراف غير المرغوب فيها في العملية الأصلية ، واقرأ نهاية الأنبوب 1 واكتب نهاية الأنبوب 2.

الخطوة 5 - أغلق الأطراف غير المرغوب فيها في العملية الفرعية ، واكتب نهاية الأنبوب 1 واقرأ نهاية الأنبوب 2.

الخطوة 6 - قم بإجراء الاتصال كما هو مطلوب.

الأنابيب مع اثنين

برامج العينة

نموذج برنامج 1 - تحقيق اتصال ثنائي الاتجاه باستخدام الأنابيب.

الخوارزمية

الخطوة 1 - قم بإنشاء أنبوب 1 للعملية الأصل للكتابة والعملية الفرعية للقراءة.

الخطوة 2 - قم بإنشاء أنبوب 2 لعملية الطفل للكتابة والعملية الأصلية للقراءة.

الخطوة 3 - أغلق طرفي الأنبوب غير المرغوب فيه من جانب الوالدين والطفل.

الخطوة 4 - عملية الوالدين لكتابة رسالة والعملية الفرعية للقراءة والعرض على الشاشة.

الخطوة 5 - عملية الطفل لكتابة رسالة والعملية الأبوية للقراءة والعرض على الشاشة.

كود المصدر: twowayspipe.c

#include<stdio.h>
#include<unistd.h>

int main() {
   int pipefds1[2], pipefds2[2];
   int returnstatus1, returnstatus2;
   int pid;
   char pipe1writemessage[20] = "Hi";
   char pipe2writemessage[20] = "Hello";
   char readmessage[20];
   returnstatus1 = pipe(pipefds1);
   
   if (returnstatus1 == -1) {
      printf("Unable to create pipe 1 \n");
      return 1;
   }
   returnstatus2 = pipe(pipefds2);
   
   if (returnstatus2 == -1) {
      printf("Unable to create pipe 2 \n");
      return 1;
   }
   pid = fork();
   
   if (pid != 0) // Parent process {
      close(pipefds1[0]); // Close the unwanted pipe1 read side
      close(pipefds2[1]); // Close the unwanted pipe2 write side
      printf("In Parent: Writing to pipe 1 – Message is %s\n", pipe1writemessage);
      write(pipefds1[1], pipe1writemessage, sizeof(pipe1writemessage));
      read(pipefds2[0], readmessage, sizeof(readmessage));
      printf("In Parent: Reading from pipe 2 – Message is %s\n", readmessage);
   } else { //child process
      close(pipefds1[1]); // Close the unwanted pipe1 write side
      close(pipefds2[0]); // Close the unwanted pipe2 read side
      read(pipefds1[0], readmessage, sizeof(readmessage));
      printf("In Child: Reading from pipe 1 – Message is %s\n", readmessage);
      printf("In Child: Writing to pipe 2 – Message is %s\n", pipe2writemessage);
      write(pipefds2[1], pipe2writemessage, sizeof(pipe2writemessage));
   }
   return 0;
}

خطوات التنفيذ

التحويل البرمجي

gcc twowayspipe.c –o twowayspipe

إعدام

In Parent: Writing to pipe 1 – Message is Hi
In Child: Reading from pipe 1 – Message is Hi
In Child: Writing to pipe 2 – Message is Hello
In Parent: Reading from pipe 2 –

التسميات: