יום שבת

IPC - Msg queues

נציג דוגמא ל-msg queues. כמו בהרבה פונקציות מערכת, גם כאן קיימם שני סוגי מימוש לmsg queues. מימוש של posix ומימוש של system v .  המימושים הנ"ל די דומים. הדוגמא שנראה כאן משתמשת ב- system v api. 
הדוגמא כוללת שני קבצים: kirk.c, האחראי על שליחת הודעות ל-msg queue - התוכנית קולטת את ההודעת אינטראקטיבית מהמשתמש  שמקליד אותן, ו-spoke.c האחראי על שליפת ההודעות מה-msg queue ושליחתן לתצוגה.

הדוגמא לקוחה כולה מתוך Beej's Guide to Unix IPC.

נתחיל עם הקובץ kirk.c

בקובץ שלוש פונקציות עיקריות - מודגשות בצהוב.
1. יצירת ה-msg queueu
2. שליחת הודעות ל-msg queueu
3. מחיקת ה-queue





  1. *
  2.  * kirk.c
  3.  *
  4.  *  Created on: May 10, 2012
  5.  */


  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <errno.h>
  9. #include <string.h>
  10. #include <sys/types.h>
  11. #include <sys/ipc.h>
  12. #include <sys/msg.h>

  13. struct my_msgbuf {
  14.     long mtype;
  15.     char mtext[200];
  16. };

  17. int main(void)
  18. {
  19.     struct my_msgbuf buf;
  20.     int msqid;
  21.     key_t key;

  22.     if ((key = ftok("/tmp", 'B')) == -1) {
  23.         perror("ftok");
  24.         exit(1);
  25.     }

  26.     if ((msqid = msgget(key, 0644 | IPC_CREAT)) == -1) {
  27.         perror("msgget");
  28.         exit(1);
  29.     }

  30.     printf("Enter lines of text, ^D to quit:\n");

  31.     buf.mtype = 1; /* we don't really care in this case */

  32.     while(fgets(buf.mtext, sizeof buf.mtext, stdin) != NULL) {
  33.         int len = strlen(buf.mtext);
  34. struct my_msgbuf {
  35.     long mtype;
  36.     char mtext[200];
  37. };
  38.         /* ditch newline at end, if it exists */
  39.         if (buf.mtext[len-1] == '\n') buf.mtext[len-1] = '\0';

  40.         if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
  41.             perror("msgsnd");
  42.     }

  43.     if (msgctl(msqid, IPC_RMID, NULL) == -1) {
  44.         perror("msgctl");
  45.         exit(1);
  46.     }

  47.     return 0;
  48. }



הפונקציה main מבצעת שליחה של הודעות ל-msg queue.
ההודעות מוקלדות ע"י המשתמש ונקלטות  ע"י fgets  - ראו שורות 43-47.

main מפעילה 3 פונקציות הקשורות ל=msg queue (מודגשות בצהוב):
שורה 34 - msgget, יוצרת את ה-queue.
שורה 49 - msgsnd שולחת הודעה ל-queue.
שורה 53 - מחיקת ה-queue בסיום.

נתחיל עם יצירת ה-queue - שורה 34:
msgget
הנה ה-prototype של msgget:

int msgget(key_t key, int msgflg);

        if (msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0) == -1) {


הפונקציה מקבלת שני פרמטרים:
key - זהו למעשה משתנה מסוג long, שמהווה מציין עבור ה-queue. כדי לגשת ל-queue הזה מכל process שהוא, יש להשתמש באותו מציין. אין שני msg queues עם אותו key.
msgflag - ה-flag הוא למעשה שילוב של flag ושל ה-permission עבור ה-queue.
בדוגמא שלנו למשל, בשורה 34:
    if ((msqid = msgget(key, 0644 | IPC_CREAT)) == -1) {
msgflag הוא OR של 644 (rw-r-r) ושל IPC_CREAT שמסמן לצור msg queue חדש. נראה מאוחר יותר שבפעולת ה-msgget בצד הקולט, הדגל IPC_CREAT לא קיים.


לגבי בחירת ה-key - את ה-key אפשר לקבוע באופן חופשי. ומה אם ה-key שבחרנו כבר תפוס? זו אכן בעיה. נשתמש בפונקציה ftok שמייצרת key עפ"י שם קובץ כלשהו (שצריך להיות קיים על הדיסק). ftok אמורה לעזור בבחירת unique key.
ראו שורה 29:
(key = ftok("/tmp", 'B'))
הנה ה-prototype של ftok:


key_t ftok(const char *path, int id);

(key = ftok("/tmp", 'B'))
הנה ה-prototype של ftok:


key_t ftok(const char *path, int id);


msgsnd
שורה 49 - כאן מתבצעת השליחה ל-queue.


msgsnd
שורה 49 - כאן מתבצעת השליחה ל-queue. 


הנה ה-prototype של msgsnd:

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


פרוט 4 הפרמטרים של הפונקציה:
msgqid - הערך המוחזר ע"י msgget.
msgp - מצביע על ה-buffer של ה--data שתשלח ל-queue. הסבר על מבנה ה-buffer מיד.
msgsz - כמות ה-bytes שישלחו ל-queue. גודל ה-buffer לא כולל את ה-mtype - הסבר על מבנה ה-buffer מיד בהמשך.
msgflg - דגלים לאפיונים שונים. כרגע נשים 0.


מבנה ה-buffer הנמסר ל-msg queue
הפרמטר השני בפונקציה msgsnd מצביע על ה-buffer שישלח ל-queue.
ה-buffer בדוגמא הנ"ל נראה כך - שורות 18-21:

struct my_msgbuf {
    long mtype;
    char mtext[200];
};

בראש ה-buffer נמצא משתנה ה-mtype מסוג long. הוא תמיד חייב להיות כאן.
אחריו - המקום לשמירת הנתונים עצמם. הם כמובן לא חייבים להיות char, כך שגם הדוגמא הבאה תקינה:


struct my_msgbuf {
    long mtype;
    int mtext[10];
};

ערכו של ה-mtype יכול לשמש לקביעת סדר שליפת הנתונים מה-buffer ע"י הצד הקולט. נראה זאת כשנדון ב-msgrcv.
גודל ה-buffer - הפרמטר השלישי ב-msgsnd, אינו כולל את שדה ה-msgtype, כך שבדוגמא שלנו, הגודל יהיה שווה לגודל המערך mtext, כלומר 200.

msgcntl
מחיקת ה-queue.
שורה 53:
    if (msgctl(msqid, IPC_RMID, NULL) == -1) {
ה-prototype של msgctl:


int msgctl(int msqid, int cmd,struct msqid_ds *buf);
שלושת הפרמטרים:
msgqid - הערך שמחזירה msgget.
cmd - הפעולה לביצוע/
struct msgqid_ds - מקבל כאן NULL.



נעבור לקובץ שממש את פונקצית הקריאה מה-msg queue:


  1. /*
  2.  * spock.c
  3.  *
  4.  *  Created on: May 10, 2012
  5.  */


  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <errno.h>
  9. #include <sys/types.h>
  10. #include <sys/ipc.h>
  11. #include <sys/msg.h>

  12. struct my_msgbuf {
  13.     long mtype;
  14.     char mtext[200];
  15. };

  16. int main(void)
  17. {
  18.     struct my_msgbuf buf;
  19.     int msqid;
  20.     key_t key;

  21.     if ((key = ftok("/tmp", 'B')) == -1) {  /* same key as kirk.c */
  22.         perror("ftok");
  23.         exit(1);
  24.     }

  25.     if ((msqid = msgget(key, 0644)) == -1) { /* connect to the queue */
  26.         perror("msgget");
  27.         exit(1);
  28.     }

  29.     printf("spock: ready to receive messages, captain.\n");

  30.     for(;;) { /* Spock never quits! */
  31.         if (msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0) == -1) {
  32.             perror("msgrcv");
  33.             exit(1);
  34.         }
  35.         printf("spock: \"%s\"\n", buf.mtext);
  36.     }

  37.     return 0;
  38. }





כאן מדובר בשתי פונקציות המטפלות ב-nsg queue - מודגשות בצהוב:
שורה 31: msgget. מחזירה את ה-msgqid שישמש לכל הגישות ל-queue. הקריאה ל-msgget דומה לזו שראינו בקובץ kirk.c, רק שכאן הדגל IPC_CREAT לא קיים, היות שה-msg queue כבר נוצר.
ה-key חייב כמובן להיות זהה, ולכן הוא מיוצר כמו ב-kirk.c עם ftok - שורה 26/
שורה 39: קריאה מה-msg queue.
ה-prototype:


int msgrcv(int msqid, void *msgp, size_t msgsz,long msgtyp, int msgflg);





פרוט 5 הפרמטרים של הפונקציה:
msgqid - הערך המוחזר ע"י msgget.
msgp - מצביע על ה-data שתשלח ל-queue.
msgsz - כמות ה-bytes שישלחו ל-queue.
msgtyp - שלושת תחומי הערכים של msgtyp:
msgtyp<0: תשלף ההודעה הראשונה שה-mtype שלה קטן או שווה מערכו המוחלט של msgtyp.
msgtyp>0: תשלף ההודעה הראשונה שה-mtype שלה שווה ל msgtyp.
msgtyp=0: תשלף ההודעה הראשונה, בלי   קשר לערכו של mtype.



--סוף--



אין תגובות:

הוסף רשומת תגובה