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

قوائم انتظار الرسائل

 

قوائم انتظار الرسائل


قوائم انتظار الرسائل


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

لماذا نحتاج إلى قوائم انتظار الرسائل عندما يكون لدينا بالفعل ذاكرة مشتركة؟ سيكون لأسباب متعددة ، دعونا نحاول تقسيم هذا إلى عدة نقاط للتبسيط -

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

  • إذا أردنا التواصل مع تنسيقات الرسائل الصغيرة.

  • يجب حماية بيانات الذاكرة المشتركة من خلال المزامنة عند اتصال عمليات متعددة في نفس الوقت.

  • معدل تكرار الكتابة والقراءة باستخدام الذاكرة المشتركة مرتفع ، فسيكون من الصعب جدًا تنفيذ الوظيفة. لا يستحق فيما يتعلق بالاستخدام في هذا النوع من الحالات.

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

  • إذا أردنا التواصل مع حزم بيانات مختلفة ، فلنقل أن العملية A ترسل نوع الرسالة 1 للمعالجة B ، ونوع الرسالة 10 للمعالجة C ، ونوع الرسالة 20 لمعالجة D. في هذه الحالة ، يكون التنفيذ أسهل مع قوائم انتظار الرسائل. لتبسيط نوع الرسالة المحددة مثل 1 ، 10 ، 20 ، يمكن أن تكون إما 0 أو + ve أو –ve كما هو موضح أدناه.

  • بالطبع ، ترتيب قائمة انتظار الرسائل هو FIFO (First In First Out). الرسالة الأولى المدرجة في قائمة الانتظار هي أول رسالة يتم استردادها.

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

يمكن أن يحدث الاتصال باستخدام قوائم انتظار الرسائل بالطرق التالية -

  • الكتابة في الذاكرة المشتركة بعملية واحدة والقراءة من الذاكرة المشتركة بعملية أخرى. كما نعلم ، يمكن أن تتم القراءة من خلال عمليات متعددة أيضًا.

قائمة انتظار الرسائل
  • الكتابة في الذاكرة المشتركة من خلال عملية واحدة مع حزم بيانات مختلفة والقراءة منها بعمليات متعددة ، أي حسب نوع الرسالة.

متعددة قائمة انتظار الرسائل

بعد الاطلاع على معلومات معينة حول قوائم انتظار الرسائل ، حان الوقت الآن للتحقق من استدعاء النظام (النظام الخامس) الذي يدعم قوائم انتظار الرسائل.

لإجراء الاتصال باستخدام قوائم انتظار الرسائل ، فيما يلي الخطوات -

الخطوة 1 - إنشاء قائمة انتظار الرسائل أو الاتصال بقائمة انتظار الرسائل الموجودة بالفعل (msgget ())

الخطوة 2 - الكتابة في قائمة انتظار الرسائل (msgsnd ())

الخطوة 3 - القراءة من قائمة انتظار الرسائل (msgrcv ())

الخطوة 4 - إجراء عمليات التحكم في قائمة انتظار الرسائل (msgctl ())

الآن ، دعونا نتحقق من بناء الجملة وبعض المعلومات حول المكالمات المذكورة أعلاه.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg)

يقوم استدعاء النظام هذا بإنشاء أو تخصيص قائمة انتظار رسائل System V. يجب تمرير الحجج التالية -

  • الوسيطة الأولى ، المفتاح ، تتعرف على قائمة انتظار الرسائل. يمكن أن يكون المفتاح إما قيمة عشوائية أو قيمة يمكن اشتقاقها من وظيفة المكتبة ftok ().

  • الوسيطة الثانية ، shmflg ، تحدد إشارة / إشارات قائمة انتظار الرسائل المطلوبة مثل IPC_CREAT (إنشاء قائمة انتظار الرسائل إذا لم تكن موجودة) أو IPC_EXCL (تُستخدم مع IPC_CREAT لإنشاء قائمة انتظار الرسائل وفشل الاستدعاء ، إذا كانت قائمة انتظار الرسائل موجودة بالفعل). تحتاج إلى تمرير الأذونات كذلك.

ملاحظة - راجع الأقسام السابقة للحصول على تفاصيل حول الأذونات.

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

العديد من الأخطاء المتعلقة بهذه المكالمة هي EACCESS (تم رفض الإذن) ، EEXIST (قائمة الانتظار موجودة بالفعل لا يمكن إنشاؤها) ، ENOENT (قائمة الانتظار غير موجودة) ، ENOMEM (لا توجد ذاكرة كافية لإنشاء قائمة الانتظار) ، إلخ.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg)

ترسل مكالمة النظام هذه / تُلحق رسالة في قائمة انتظار الرسائل (النظام الخامس). يجب تمرير الحجج التالية -

  • يتعرف الوسيط الأول ، msgid ، على قائمة انتظار الرسائل ، أي معرف قائمة انتظار الرسائل. يتم استلام قيمة المعرف عند نجاح msgget ()

  • الوسيطة الثانية ، msgp ، هي المؤشر إلى الرسالة ، المرسلة إلى المتصل ، والمحددة في بنية النموذج التالي -

struct msgbuf {
   long mtype;
   char mtext[1];
};

يتم استخدام المتغير mtype للتواصل مع أنواع الرسائل المختلفة ، موضحة بالتفصيل في مكالمة msgrcv (). mtext المتغير عبارة عن مصفوفة أو بنية أخرى يتم تحديد حجمها بواسطة msgsz (قيمة موجبة). إذا لم يتم ذكر حقل mtext ، فسيتم اعتباره رسالة بحجم صفري ، وهو مسموح به.

  • الوسيطة الثالثة msgsz هي حجم الرسالة (يجب أن تنتهي الرسالة بحرف فارغ)

  • الوسيطة الرابعة ، msgflg ، تشير إلى علامات معينة مثل IPC_NOWAIT (تُرجع فورًا عندما لا توجد رسالة في قائمة الانتظار أو MSG_NOERROR (اقتطاع نص الرسالة ، إذا كان أكثر من msgsz بايت)

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

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgrcv(int msgid, const void *msgp, size_t msgsz, long msgtype, int msgflg)

يقوم استدعاء النظام هذا باسترداد الرسالة من قائمة انتظار الرسائل (النظام الخامس). يجب تمرير الحجج التالية -

  • يتعرف الوسيط الأول ، msgid ، على قائمة انتظار الرسائل ، أي معرف قائمة انتظار الرسائل. يتم استلام قيمة المعرف عند نجاح msgget ()

  • الوسيطة الثانية ، msgp ، هي مؤشر الرسالة المستلمة من المتصل. يتم تعريفه في هيكل النموذج التالي -

struct msgbuf {
   long mtype;
   char mtext[1];
};

يستخدم المتغير mtype للتواصل مع أنواع الرسائل المختلفة. mtext المتغير عبارة عن مصفوفة أو بنية أخرى يتم تحديد حجمها بواسطة msgsz (قيمة موجبة). إذا لم يتم ذكر حقل mtext ، فسيتم اعتباره رسالة بحجم صفري ، وهو مسموح به.

  • الوسيطة الثالثة msgsz هي حجم الرسالة المستلمة (يجب أن تنتهي الرسالة بحرف فارغ)

  • الوسيطة fouth ، msgtype ، تشير إلى نوع الرسالة -

    • إذا كانت msgtype تساوي 0 - يقرأ أول رسالة مستلمة في قائمة الانتظار

    • إذا كانت msgtype هي + ve - يقرأ الرسالة الأولى في قائمة الانتظار من النوع msgtype (إذا كان نوع msgtype هو 10 ، فسيقرأ فقط الرسالة الأولى من النوع 10 على الرغم من وجود أنواع أخرى في قائمة الانتظار في البداية)

    • إذا كان msgtype هو –ve - يقرأ الرسالة الأولى من النوع الأدنى أقل من أو يساوي القيمة المطلقة لنوع الرسالة (على سبيل المثال ، إذا كان نوع msgtype هو -5 ، فإنه يقرأ الرسالة الأولى من النوع أقل من 5 ، أي نوع الرسالة من 1 إلى 5)

  • تشير الوسيطة الخامسة ، msgflg ، إلى علامات معينة مثل IPC_NOWAIT (تُرجع فورًا عندما لا توجد رسالة في قائمة الانتظار أو MSG_NOERROR (اقتطاع نص الرسالة إذا كان أكثر من مللي ثانية بايت)

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

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msgid, int cmd, struct msqid_ds *buf)

يقوم استدعاء النظام هذا بإجراء عمليات التحكم في قائمة انتظار الرسائل (النظام الخامس). يجب تمرير الحجج التالية -

  • يتعرف الوسيط الأول ، msgid ، على قائمة انتظار الرسائل ، أي معرف قائمة انتظار الرسائل. يتم استلام قيمة المعرف عند نجاح msgget ()

  • الوسيطة الثانية ، cmd ، هي الأمر لتنفيذ عملية التحكم المطلوبة في قائمة انتظار الرسائل. القيم الصالحة لـ cmd هي -

IPC_STAT - ينسخ معلومات القيم الحالية لكل عضو من عناصر البنية msqid_ds إلى البنية التي تم تمريرها والتي يشير إليها buf. يتطلب هذا الأمر إذن قراءة في قائمة انتظار الرسائل.

IPC_SET - يضبط معرف المستخدم ومعرف المجموعة للمالك والأذونات وما إلى ذلك التي يشير إليها هيكل buf.

IPC_RMID - يزيل قائمة انتظار الرسائل على الفور.

IPC_INFO - يعرض معلومات حول حدود قائمة انتظار الرسائل والمعلمات في الهيكل المشار إليه بواسطة buf ، وهو من النوع Struct msginfo

MSG_INFO - إرجاع بنية msginfo تحتوي على معلومات حول موارد النظام المستهلكة من خلال قائمة انتظار الرسائل.

  • الوسيطة الثالثة ، buf ، هي مؤشر إلى بنية قائمة انتظار الرسائل المسمى Struct msqid_ds. سيتم استخدام قيم هذا الهيكل لأي مجموعة أو الحصول عليها حسب cmd.

سيعيد هذا الاستدعاء القيمة بناءً على الأمر الذي تم تمريره. يؤدي نجاح IPC_INFO و MSG_INFO أو MSG_STAT إلى إرجاع فهرس أو معرف قائمة انتظار الرسائل أو 0 لعمليات أخرى و -1 في حالة الفشل. لمعرفة سبب الفشل ، تحقق من متغير errno أو وظيفة perror ().

بعد الاطلاع على المعلومات الأساسية ومكالمات النظام فيما يتعلق بقوائم انتظار الرسائل ، حان الوقت الآن للتحقق من البرنامج.

دعونا نرى الوصف قبل النظر في البرنامج -

الخطوة 1 - إنشاء عمليتين ، إحداهما للإرسال إلى قائمة انتظار الرسائل (msgq_send.c) والأخرى للاسترداد من قائمة انتظار الرسائل (msgq_recv.c)

الخطوة 2 - إنشاء المفتاح باستخدام وظيفة ftok (). لهذا ، يتم إنشاء ملف msgq.txt مبدئيًا للحصول على مفتاح فريد.

الخطوة 3 - تقوم عملية الإرسال بما يلي.

  • يقرأ إدخال السلسلة من المستخدم

  • يزيل الخط الجديد ، إن وجد

  • يرسل إلى قائمة انتظار الرسائل

  • يكرر العملية حتى نهاية الإدخال (CTRL + D)

  • بمجرد استلام نهاية الإدخال ، يتم إرسال الرسالة "end" للإشارة إلى نهاية العملية

الخطوة 4 - في عملية الاستلام ، يقوم بما يلي.

  • يقرأ الرسالة من قائمة الانتظار
  • يعرض الإخراج
  • إذا كانت الرسالة المستلمة "إنهاء" ، يتم إنهاء العملية والخروج

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

الآن ، دعنا نتحقق من العملية (إرسال الرسالة إلى قائمة الانتظار) - الملف: msgq_send.c

/* Filename: msgq_send.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PERMS 0644
struct my_msgbuf {
   long mtype;
   char mtext[200];
};

int main(void) {
   struct my_msgbuf buf;
   int msqid;
   int len;
   key_t key;
   system("touch msgq.txt");
   
   if ((key = ftok("msgq.txt", 'B')) == -1) {
      perror("ftok");
      exit(1);
   }
   
   if ((msqid = msgget(key, PERMS | IPC_CREAT)) == -1) {
      perror("msgget");
      exit(1);
   }
   printf("message queue: ready to send messages.\n");
   printf("Enter lines of text, ^D to quit:\n");
   buf.mtype = 1; /* we don't really care in this case */
   
   while(fgets(buf.mtext, sizeof buf.mtext, stdin) != NULL) {
      len = strlen(buf.mtext);
      /* remove newline at end, if it exists */
      if (buf.mtext[len-1] == '\n') buf.mtext[len-1] = '\0';
      if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
      perror("msgsnd");
   }
   strcpy(buf.mtext, "end");
   len = strlen(buf.mtext);
   if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
   perror("msgsnd");
   
   if (msgctl(msqid, IPC_RMID, NULL) == -1) {
      perror("msgctl");
      exit(1);
   }
   printf("message queue: done sending messages.\n");
   return 0;
}

خطوات التجميع والتنفيذ

message queue: ready to send messages.
Enter lines of text, ^D to quit:
this is line 1
this is line 2
message queue: done sending messages.

فيما يلي الكود من عملية استلام الرسالة (استرداد الرسالة من قائمة الانتظار) - الملف: msgq_recv.c

/* Filename: msgq_recv.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PERMS 0644
struct my_msgbuf {
   long mtype;
   char mtext[200];
};

int main(void) {
   struct my_msgbuf buf;
   int msqid;
   int toend;
   key_t key;
   
   if ((key = ftok("msgq.txt", 'B')) == -1) {
      perror("ftok");
      exit(1);
   }
   
   if ((msqid = msgget(key, PERMS)) == -1) { /* connect to the queue */
      perror("msgget");
      exit(1);
   }
   printf("message queue: ready to receive messages.\n");
   
   for(;;) { /* normally receiving never ends but just to make conclusion 
             /* this program ends wuth string of end */
      if (msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0) == -1) {
         perror("msgrcv");
         exit(1);
      }
      printf("recvd: \"%s\"\n", buf.mtext);
      toend = strcmp(buf.mtext,"end");
      if (toend == 0)
      break;
   }
   printf("message queue: done receiving messages.\n");
   system("rm msgq.txt");
   return 0;
}

خطوات التجميع والتنفيذ

message queue: ready to receive messages.
recvd: "this is line 1"
recvd: "this is line 2"
recvd: "end"
message queue: done receiving mes

التسميات: