الدرس الرابع: POSIX Messages

سلسلة من الدروس في الـ POSIX

الدرس الرابع: POSIX Messages

POSIX Messages:

عند حصول ضرورة للتخاطب في المسألة المدروسة بين الـ Threads يمكن استخدام نموذج Producer/Consumer مع رتل لتحقيق هذا التخاطب ولا يكون هناك حاجة لبنية جديدة قد تزيد من تعقيد النظام. عند وجود ضررة لاتصال مجموعة من الـ Processes فيما بينها و تتبادل البيانات من خلال الرسائل فلابد من وجود آلية أكثر فعالية و بنية تضمان التواصل بين الـ Processes. يتم تبادل الرسائل بين الـ Processes في POSIX من خلال رتل رسائل Message-Queue، صممت كآلية تمرير رسائل الزمن الحقيقي لتتعامل مع الذاكرة المشتركة و ذلك لتتكيف مع متطلبات تطبيقات الزمن الحقيقي بتبادل كميات اعتباطية من البيانات بآلية فعّالة و محددة.

رتل الرسائل:

  • توجد في POSIX على شكل بنية Structure، و لاستخدامها يجب تضمين المكتبة mqueue.h.

  • جميع الرسائل لها أولوية Priority و يكون رتل الرسائل رتل أفضليات بحيث تكون الرسائل ذات الأولوية الأعلى في مقدمة الرتل.

  • يمكن التحكم بخصائص رتل الرسائلعدد الرسائل الأعظمي و حجم الرسالة الواحدة.

  • توجد مجموعة من التوابع لإنشاء – فتح – رتل رسائل و التحكم بخائصها و تبادل البيانات بين الـ Processes و إدارة الوصول إلى رتل الرسائل.

  • يمكن معرفة حالة الرتل من خلال مجموعة من التوابع عدد الرسائل الموجودة في الرتل، عدد الـ Processes التي في حالة انتظار، حجم الرتل الأعظمي، و حجم الرسالة الأعظمي.

التعامل مع رتل الرسائل:

  • نعرف متحول من نوع mqd_t.

mqd_t queue_test;

  • ننشأ بنية structure لضبط خصائص الرتل من نوع mq_attr.

struct mq_attr q;

  • نحدد حجم الرتل و حجم الرسالة الأعظمي.

q.mq_msgsize = MessageMaxSize;

q.mq_maxmsg = QueueMaxSize;

  • ننشأ الرتل الجديد:

mqd_t mq_open(const char *name, int flags, ... [ mode_t mode, struct mq_attr *mq_attr ]);

عند إنشاء رتل جديد يجب تمرير القيم الأربعة.

name: اسم الرتل الذي سنقوم بإنشاءه – فتحه. يجب أن يكون اسم الرتل مسبوقاً بـ/ .

oflag:تمثل علم دلالة لتحديد طبيعة التعامل مع الرتل و تأخذ القيم:

  • O_CREATعندما نريد إنشاء رتل جديد.

  • O_CREAT|O_EXCLيعيد التابع خطأ عند محاولة إنشاء رتل باسم مستخدَم مسبقاً.

  • O_RDONLYالقراءة من الرتل فقط، و تأتي مع أحد القيمتين السابقتين في حال إنشاء رتل جديد.

  • O_WRONLYالكتابة على الرتل فقط، و تأتي معO_CREAT, O_CRAET|O_EXCLفي حال إنشاء رتل جديد.

  • O_RDWRالقراءةو الكتابة، و تأتي معO_CREAT, O_CRAET|O_EXCLفي حال إنشاء رتل جديد.

  • O_NONBLOCKالآلية الافتراضية هي أن تنتظر الـ Process وجود مكان فارغ في الرتل لتضع الرسالة في حال الإرسال، و أن تنتظر وصول رسالة إلى الرتل عندما يكون الرتل فارغاً في حال الاستقبال. إن أردنا ألا تتم عملية الانتظار (تحول الـProcess إلى حالة blocked) فأننا نستخدم هذا العلم.

mode: تحديد سماحيات الوصول للرتل.

mq_attr: مميزات الرتل الذي تم ضبطها بشكل مسبق.

  • إرسال رسالة إلى الرتل(الكتابة على الرتل):

int mq_send(mqd_t mqdes, const char *msgbuf, size_t len, unsigned int prio);

msgbuf:الرسالة التي نريد إرسالها و تكون على شكل نص حصراً!

len:حجم الرسالة عدد الأحرفيمكن أن تكون من نوع int.

prio: أولوية الرسالة.

تعيد القيمة صفر في حال نجاح الإرسال و إلا تعيد -1 في حال الفشل.

  • استقبال رسالة من الرتل(قراءة رسالة من الرتل):

size_t mq_receive(mqd_t mqdes, char *buf, size_t len, unsigned *prio);

buf:المكان الذي ستوضع فيه الرسالة المستقبلة من الرتل، و يتم حذف هذه الرسالة من الرتل و التي تمثل الرسالة ذات الألولوية الأعلى.

len:حجم المكان المخصص لوضع الرسالة المستقبلة و يجب أن يكون أكبر بـ1 من حجم الرسالة الأعظمية الي تم تحديده عند ضبط خصائص الرتل.

prio:أولوية الرسالة المستقبلة، يمكن تمرير NULLإذا لم نرد قراءة الأولوية.

تعيد حجم الرسالة المستقبلة، أو تعيد -1 في حال الفشل في الاستقبال.

  • تنبيه Processعند وصول رسالة جديدة إلى الرتل الفارغ:

    int mq_notify(mqd_t mqdes, const struct sigevent *sevp;)

    sigevent sevp

يقوم التابع بتسجيل طلب تنبيه للـ Process من أجل تنبيهه في حال وصول رسالة إلى الرتل القارغ.

  • لا يتم إرسال التنبيه في حال كان الرتل فيه رسائل حتى يفرغ و تأتي رسالة جديدة.

  • لا يمكن تسجيل أكثر من طلب واحد لجميع الـ Processes.

  • إن تم تسجيل الطلب و الرتل فارغ ولكن يوجد هناك Process2 تنتظر استلام رسالة(mq_receive) فيتم تجاهل طلب التنبيه و تسليم الرسالة إلى الـ Process2، لكن لايتم حذف الطلب.

  • التنبيه يتم لمرة واحدة و بعدها يحذف تسجيل طلب التنبيه فوراً.

  • يمكن حذف طلب التنبيه قبل حدوثه بشكل يدوي من خلال تمرير NULL لنوع الإشارة.

  • إغلاق الرتل:

int mq_close(mdq_t mqdes)

تغلق الرتل الموصوف بـ mdq_t، و تعيد 0 في حال النجاح و -1 في حال الفشل.

  • الانتهاء من استخدام الرتل:

int mq_unlink(const char *name)

الانتهاء كلياً من التعامل مع الرتل، أي تابع mq_closeيتم استدعائه من الـ Processes -التي تتعامل مع نفس الرتل- بعد التابع mq_unlinkسيؤدي إلى حذف الرتل نهائياً. يعيد القيمة 0 في حال النجاح و إلا -1.

مثال1:المرسل يرسل 3 رسائل مع أن الحجم الأعظمي للرتل هو 2 فيضطر للانتظار حتى قيام المستقبل بقراءة أول رسالة حتى يكمل إرسال الرسالة الثالثة.

Sender code:

#include<stdio.h>

#include<stdlib.h>

#include<mqueue.h>

#include<fcntl.h>

#define QueueMaxSize 2

#define MessageMaxSize 4

int main(int argc, char *argv[])

{

mqd_t queue_test;

struct mq_attr q;

q.mq_msgsize = MessageMaxSize;

q.mq_maxmsg = QueueMaxSize;

char *msgptr;

size_t msglen = 4;

int priority = 1;

int i;

queue_test = mq_open(“/ZAIN”,O_WRONLY|O_CREAT,0666,&q);

msgptr = “1234”;

if(-1 != queue_test)

for(i=1;i<4;i++)

{

printf(“I’ll send a message to the queue\n”);

if(mq_send(queue_test,msgptr,msglen,priority) == 0)

printf(“I sent a message #%d\n”,i);

priority += 8;

}

else printf(“Error\n”);

mq_unlink(“/ZAIN”);

mq_close( queue_test);

return 0;

}

Receiver code:

#include<stdio.h>

#include<stdlib.h>

#include<mqueue.h>

#include<fcntl.h>

int main(int argc, char *argv[])

{

mqd_t queue_test;

char msgptr[5];

size_t msglen = 5;

int priority;

int i,msgsize;

int number;

queue_test = mq_open(“/ZAIN”,O_RDONLY);

if(-1 != queue_test)

for(i=1;i<4;i++)

{

printf(“I’ll get a message from the queue\n”);

msgsize = mq_receive(queue_test,msgptr,msglen,&priority);

if( msgsize != -1)

{

number = atoi(msgptr);

printf(“I got a message (%d)\tmessage size is #%d\tthe priority is #%d\n”,number,msgsize,priority);

}

else {printf(“Error\n”); continue;}

sleep(2);

}

else printf(“Error\n”);

mq_unlink(“/ZAIN”);

mq_close( queue_test);

return 0;

}

ملاحظة: لترجمة الملف يجب تمرير مكتبة الزمن الحقيقي lrt و كذلك جميع البرامج التي تتضمن رتل رسائل،أي يجب أن نكتب gcc -lrt -o sender sender.c

الخرج:نقوم بتشغيل الـ Sender أولاً فنلاحظ بعد إرسال أول رسالتين يصبح في حالة انتظار حتى تشغيل الـ Receiver و استقبال أول رسالة.

Sender:

I’ll send a message to the queue

I sent a message #1

I’ll send a message to the queue

I sent a message #2

I’ll send a message to the queue

I sent a message #3

Receiver:

I’ll get a message from the queue

I got a message (1234) message size is #4 the priority is #9

I’ll get a message from the queue

I got a message (1234) message size is #4 the priority is #17

I’ll get a message from the queue

I got a message (1234) message size is #4 the priority is #1

ملاحظة:في حال تمريرO_NONBLOCK فإن الـ Sender لن يدخل في حالة انتظار.

مثال2:يقوم المستقبل بإنشاء الرتل و تسجيل طلب تنبيه ثم يدخل في حالة انتظار حتى يقوم المرسل بإرسال رسالة إلى الرتل فيقوم المستقبل عندها بقراءة الرسالة و طباعتها.

Receiver code:

#include<stdio.h>

#include<stdlib.h>

#include<mqueue.h>

#include<fcntl.h>

#define QueueMaxSize 1

#define MessageMaxSize 4

int main(int argc, char *argv[])

{

mqd_t queue_test;

struct mq_attr q;

q.mq_msgsize = MessageMaxSize;

q.mq_maxmsg = QueueMaxSize;

char msgptr[5];

size_t msglen = 5;

int msgsize, ret, number;

struct sigevent sev;

sev.sigev_notify = SIGEV_SIGNAL;

sev.sigev_signo = 32;

int priority ;

queue_test = mq_open(“/qnotify”,O_RDONLY|O_CREAT|O_NONBLOCK,0666,&q);

if(-1 != queue_test)

{

ret = mq_notify(queue_test,&sev);

if(ret == 0)

{

printf(“I’m waiting for a message notification\n”);

sleep();

if((msgsize = mq_receive(queue_test,msgptr,msglen,&priority)) > 0)

{

number = atoi(msgptr);

printf(“I got a message #%d\n”,number);

}

}

}

else printf(“Error\n”);

mq_unlink(“/qnotify”);

mq_close( queue_test);

return 0;

}

Sender code:

#include<stdio.h>

#include<stdlib.h>

#include<mqueue.h>

#include<fcntl.h>

int main(int argc, char *argv[])

{

mqd_t queue_test;

char *msgptr;

size_t msglen = 4;

int priority = 1;

queue_test = mq_open(“/qnotify”,O_WRONLY);

msgptr = “1234”;

if(-1 != queue_test)

{

printf(“I’ll send a message to the queue\n”);

if(mq_send(queue_test,msgptr,msglen,priority) == 0)

printf(“I sent the message\nBye!\n”);

}

else printf(“Error\n”);

mq_unlink(“/qnotify”);

mq_close( queue_test);

return 0;

}

الخرج:نقوم بتنفيذ الـ Receiver أولاً فيطبع الجملة و يدخل في حالة انتظار نتيجة استدعاء التابع sleep حتى يتم تشغيل الـSender و يرسل رسالة إلى الرتل فيتم تنبيه الـ Receiver من خلال الإشارة ذات الرقم 32 و التي تعني إيقاظ الـProcess.

Receiver:

I’m waiting for a message notification

I got a message #1234

Sender:

I’ll send a message to the queue

I sent the message

Bye!

Home Work

-Plane control-

يوجد في نظام بسيط للتحكم ببعض عناصر الطائرة ثلاثة معالجات محيطية متصلة بمعالج مركزي و مجموعة من الحساسات.

  • المعالج الأول مسؤول عن حالة الأجنحة (Wings status): يقرأ حساس يدل على حالة الأجنحة و كذلك يقوم بقراءة حساس الإرتفاع، طالما أن النتجة سليمة تتكرر عملية الاختبار كل 1ثانية و إلا يقوم بإرسال رسالة إلى المعالج المركزي ليطلب منه إما خفض السرعة أو خفض اﻹرتفاع.

  • المعالج الثاني مسؤول عن كمية الأوكسجين في الطائرة(Oxygen amount): يقرأ حساس يدل على كمية الأوكسجين بالإضافة إلى حساس اﻹرتفاع، طالما أن كمية الأوكسجين كافية تتكرر عملية الاختبار كل 5ثانية و إلا يقوم بإرسال رسالة إلى المعالج المركزي ليطلب منه إما زيادة كمية الأوكسجين أو خفضها.

  • المعالج الثالث مسؤول عن الحرارة الداخلية(Temperature): يقرأ حساس يدل على درجة الحرارة داخل الطائرة و يقوم بقراءة إرتفاع الطائرة من حساس اﻹرتفاع، طالما أن درجة الحرارة طبيعية تتكرر عملية الاختبار كل 10ثانية و إلا يقوم بإرسال رسالة إلى المعالج المركزي ليطلب منه إما رفع درجة الحرارة أو خفضها.

إن معالجة حالة الأجنحة تعتبر العملية الأخطر لذلك يجب الاستجابة لها بأولوية عن أي طلب آخر تليها كمية الأوكسجين ثم أخيراً الحرارة الداخلية.

المعالج

المصادر التي يحتاجها

زمن تكرار الفحص

الأولوية

الرسائل

Wings status

حساس حالة الأجنحة(WS_S)

1ثانية

10

خفض السرعة SS

حساس الإرتفاع(H_S)

خفض اﻹرتفارع DH

Oxygen amount

حساس كمية الأوكسجين(OA_S)

5ثانية

5

زيادة الكمية UP

حساس الإرتفاع(H_S)

إنقاص الكمية DOWN

Temperature

حساس الحرارة(T_S)

10ثانية

1

رفع الحرارة UP

حساس الإرتفاع(H_S)

خفض الحرارة DOWN

توجيه:

  • تحتاج إلى أربعة Processes.

  • Named semaphore لحماية المصدر المشترك.

  • رتل رسائل.

  • يمكن استخدام Threads في المعالج المركزي.

حل وظيفة الجلسة السابقة:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

#define Number_of_Patient 10

int Doctor = 2, Nurse = 6 , Clerk = 1 , Bed = 6 ;
pthread_mutex_t mutex_Doctor , mutex_Nurse , mutex_Clerk , mutex_Bed;

/* Simulates the Check In step of the simulation */
void check_in(int id) {
check: pthread_mutex_lock(&mutex_Clerk);
if(Clerk <= 0)
{
pthread_mutex_unlock(&mutex_Clerk);
sleep(1);
goto check;
}
else
{
Clerk–;
pthread_mutex_unlock(&mutex_Clerk);
}
sleep(4);
printf(“check_in %i\n”,id);
pthread_mutex_lock(&mutex_Clerk);
Clerk++;
pthread_mutex_unlock(&mutex_Clerk);
}

/* Simulates the Assess step of the simulation */
void assess(int id) {
check: pthread_mutex_lock(&mutex_Bed);
if(Bed <= 0 || Nurse <= 0)
{
pthread_mutex_unlock(&mutex_Bed);
sleep(1);
goto check;
}
else
{
Bed–;
Nurse–;
pthread_mutex_unlock(&mutex_Bed);
}
sleep(6);
printf(“assess %i\n”,id);
pthread_mutex_lock(&mutex_Nurse);
Nurse++;
pthread_mutex_unlock(&mutex_Nurse);
}

/* Simulates the Treat step of the simulation */
void treat(int id) {
check: pthread_mutex_lock(&mutex_Doctor);
if(Doctor <= 0)
{
pthread_mutex_unlock(&mutex_Doctor);
sleep(1);
goto check;
}
else
{
Doctor–;
pthread_mutex_unlock(&mutex_Doctor);
}
sleep(10);
printf(“treat %i\n”,id);
pthread_mutex_lock(&mutex_Doctor);
Doctor++;
pthread_mutex_unlock(&mutex_Doctor);
}

/* Simulates the Reassess step of the simulation */
void reassess(int id) {
check: pthread_mutex_lock(&mutex_Nurse);
if(Nurse <= 0)
{
pthread_mutex_unlock(&mutex_Nurse);
sleep(1);
goto check;
}
else
{
Nurse–;
pthread_mutex_unlock(&mutex_Nurse);
}
sleep(5);
printf(“reassess %i\n”,id);
pthread_mutex_lock(&mutex_Bed);
Nurse++;
Bed++;
pthread_mutex_unlock(&mutex_Bed);
}

/* Simulates the Check Out step of the simulation */
void check_out(int id) {
check: pthread_mutex_lock(&mutex_Clerk);
if(Clerk <= 0)
{
pthread_mutex_unlock(&mutex_Clerk);
sleep(1);
goto check;
}
else
{
Clerk–;
pthread_mutex_unlock(&mutex_Clerk);
}
sleep(2);
printf(“check_out %i\n”,id);
pthread_mutex_lock(&mutex_Clerk);
Clerk++;
pthread_mutex_unlock(&mutex_Clerk);
}

/* Simulates the arrival of a patient in the ER, and the different steps that the patient goes through.rc = pthread_create(&threads[i], NULL, arrive, (void *) &threads_id[i]);
*/
void *arrive(void *n)
{
int id = *(int *)n;
printf(“hello %i\n”,id);

check_in(id);
assess(id);
treat(id);
reassess(id);
check_out(id);

printf(“good bye %i!\n”,id);
return NULL;
}

main()
{
//Start the simulation with one patient (id = 0)
int rc,i ;
pthread_t threads[Number_of_Patient];
int threads_id[Number_of_Patient];

for(i=0 ; i< Number_of_Patient ; i++)
{
threads_id[i]=i;
rc = pthread_create(&threads[i] , NULL , arrive , (void *)&threads_id[i]);
}
for(i=0 ; i< Number_of_Patient ; i++)
rc = pthread_join(threads[i], NULL);

}

About زين العابدين

مهندس حواسيب - معهد IDA - جامعة Braunshweig التقنية.
هذا المنشور نشر في دروس تعليمية وكلماته الدلالية , , , , , , . حفظ الرابط الثابت.

أضف تعليقاً

إملأ الحقول أدناه بالمعلومات المناسبة أو إضغط على إحدى الأيقونات لتسجيل الدخول:

WordPress.com Logo

أنت تعلق بإستخدام حساب WordPress.com. تسجيل خروج   / تغيير )

صورة تويتر

أنت تعلق بإستخدام حساب Twitter. تسجيل خروج   / تغيير )

Facebook photo

أنت تعلق بإستخدام حساب Facebook. تسجيل خروج   / تغيير )

Google+ photo

أنت تعلق بإستخدام حساب Google+. تسجيل خروج   / تغيير )

Connecting to %s