คิวข้อความ

ทำไมเราต้องมีคิวข้อความเมื่อเรามีหน่วยความจำที่ใช้ร่วมกันอยู่แล้ว อาจมีหลายสาเหตุให้เราลองแบ่งประเด็นนี้ออกเป็นหลาย ๆ จุดเพื่อให้ง่ายขึ้น -

  • ตามที่เข้าใจแล้วเมื่อได้รับข้อความจากกระบวนการแล้วกระบวนการอื่นจะไม่สามารถใช้ได้อีกต่อไป ในขณะที่หน่วยความจำแบบแบ่งใช้ข้อมูลจะพร้อมใช้งานสำหรับกระบวนการต่างๆในการเข้าถึง

  • หากเราต้องการสื่อสารด้วยรูปแบบข้อความขนาดเล็ก

  • ข้อมูลหน่วยความจำที่ใช้ร่วมกันจำเป็นต้องได้รับการป้องกันด้วยการซิงโครไนซ์เมื่อหลายกระบวนการสื่อสารในเวลาเดียวกัน

  • ความถี่ในการเขียนและอ่านโดยใช้หน่วยความจำที่ใช้ร่วมกันนั้นสูงดังนั้นการใช้ฟังก์ชันนี้จะซับซ้อนมาก ไม่คุ้มกับการใช้ประโยชน์ในกรณีแบบนี้

  • จะเกิดอะไรขึ้นถ้ากระบวนการทั้งหมดไม่จำเป็นต้องเข้าถึงหน่วยความจำที่ใช้ร่วมกัน แต่มีกระบวนการน้อยมากที่ต้องการเพียงแค่ใช้งานกับคิวข้อความจะดีกว่า

  • หากเราต้องการสื่อสารกับแพ็กเก็ตข้อมูลที่แตกต่างกันให้พูดว่ากระบวนการ A กำลังส่งข้อความประเภท 1 เพื่อประมวลผล B ข้อความประเภท 10 เพื่อประมวลผล C และข้อความประเภท 20 เพื่อประมวลผล D ในกรณีนี้การใช้คิวข้อความจะง่ายกว่า เพื่อลดความซับซ้อนของประเภทข้อความที่ระบุเป็น 1, 10, 20 อาจเป็น 0 หรือ + ve หรือ –ve ตามที่กล่าวไว้ด้านล่าง

  • แน่นอนลำดับของคิวข้อความคือ FIFO (เข้าก่อนออกก่อน) ข้อความแรกที่แทรกในคิวคือข้อความแรกที่จะถูกเรียก

การใช้หน่วยความจำที่ใช้ร่วมกันหรือคิวข้อความขึ้นอยู่กับความต้องการของแอปพลิเคชันและวิธีการใช้งานได้อย่างมีประสิทธิภาพ

การสื่อสารโดยใช้คิวข้อความสามารถเกิดขึ้นได้ด้วยวิธีต่อไปนี้ -

  • การเขียนลงในหน่วยความจำแบบแบ่งใช้โดยกระบวนการหนึ่งและอ่านจากหน่วยความจำแบบแบ่งใช้โดยกระบวนการอื่น ดังที่ทราบกันดีว่าการอ่านสามารถทำได้โดยใช้กระบวนการหลายขั้นตอนเช่นกัน

  • การเขียนลงในหน่วยความจำแบบแบ่งใช้โดยกระบวนการเดียวที่มีแพ็กเก็ตข้อมูลที่แตกต่างกันและการอ่านจากมันโดยหลายกระบวนการเช่นตามประเภทข้อความ

เมื่อเห็นข้อมูลบางอย่างเกี่ยวกับคิวข้อความตอนนี้ก็ถึงเวลาตรวจสอบการเรียกระบบ (System V) ซึ่งรองรับคิวข้อความ

ในการดำเนินการสื่อสารโดยใช้คิวข้อความให้ทำตามขั้นตอนต่อไปนี้ -

Step 1 - สร้างคิวข้อความหรือเชื่อมต่อกับคิวข้อความที่มีอยู่แล้ว (msgget ())

Step 2 - เขียนลงในคิวข้อความ (msgsnd ())

Step 3 - อ่านจากคิวข้อความ (msgrcv ())

Step 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 เพื่อสร้างคิวข้อความและการเรียกล้มเหลวหากมีคิวข้อความอยู่แล้ว) จำเป็นต้องผ่านการอนุญาตเช่นกัน

Note - อ้างอิงส่วนก่อนหน้าสำหรับรายละเอียดเกี่ยวกับการอนุญาต

การเรียกนี้จะส่งคืนตัวระบุคิวข้อความที่ถูกต้อง (ใช้สำหรับการเรียกคิวข้อความเพิ่มเติม) เมื่อสำเร็จและ -1 ในกรณีที่ล้มเหลว หากต้องการทราบสาเหตุของความล้มเหลวให้ตรวจสอบด้วยฟังก์ชัน errno variable หรือ 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)

การเรียกระบบนี้จะส่ง / ต่อท้ายข้อความในคิวข้อความ (System V) ต้องส่งผ่านข้อโต้แย้งต่อไปนี้ -

  • อาร์กิวเมนต์แรก msgid รู้จักคิวข้อความเช่นตัวระบุคิวข้อความ ค่าตัวระบุจะได้รับเมื่อสำเร็จของ msgget ()

  • อาร์กิวเมนต์ที่สอง msgp เป็นตัวชี้ไปยังข้อความที่ส่งไปยังผู้เรียกซึ่งกำหนดไว้ในโครงสร้างของรูปแบบต่อไปนี้ -

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

ตัวแปร mtype ใช้สำหรับการสื่อสารกับข้อความประเภทต่างๆอธิบายโดยละเอียดในการเรียก msgrcv () ตัวแปร mtext คืออาร์เรย์หรือโครงสร้างอื่น ๆ ที่ระบุขนาดโดย msgsz (ค่าบวก) หากไม่ได้กล่าวถึงช่อง mtext จะถือว่าเป็นข้อความขนาดศูนย์ซึ่งได้รับอนุญาต

  • อาร์กิวเมนต์ที่สาม msgsz คือขนาดของข้อความ (ข้อความควรลงท้ายด้วยอักขระ null)

  • อาร์กิวเมนต์ที่สี่ msgflg ระบุแฟล็กบางอย่างเช่น IPC_NOWAIT (ส่งกลับทันทีเมื่อไม่พบข้อความในคิวหรือ MSG_NOERROR (ตัดทอนข้อความหากมีมากกว่า msgsz ไบต์)

การโทรนี้จะส่งคืน 0 เมื่อสำเร็จและ -1 ในกรณีที่ล้มเหลว หากต้องการทราบสาเหตุของความล้มเหลวให้ตรวจสอบด้วยฟังก์ชัน errno variable หรือ 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)

การเรียกระบบนี้ดึงข้อความจากคิวข้อความ (System V) ต้องส่งผ่านข้อโต้แย้งต่อไปนี้ -

  • อาร์กิวเมนต์แรก msgid รู้จักคิวข้อความเช่นตัวระบุคิวข้อความ ค่าตัวระบุจะได้รับเมื่อสำเร็จของ msgget ()

  • อาร์กิวเมนต์ที่สอง msgp เป็นตัวชี้ของข้อความที่ได้รับจากผู้โทร ถูกกำหนดไว้ในโครงสร้างของรูปแบบต่อไปนี้ -

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

ตัวแปร mtype ใช้สำหรับสื่อสารกับข้อความประเภทต่างๆ ตัวแปร mtext คืออาร์เรย์หรือโครงสร้างอื่น ๆ ที่ระบุขนาดโดย msgsz (ค่าบวก) หากไม่ได้กล่าวถึงช่อง mtext จะถือว่าเป็นข้อความขนาดศูนย์ซึ่งได้รับอนุญาต

  • อาร์กิวเมนต์ที่สาม msgsz คือขนาดของข้อความที่ได้รับ (ข้อความควรลงท้ายด้วยอักขระ null)

  • อาร์กิวเมนต์ fouth ประเภท msg ระบุประเภทของข้อความ -

    • If msgtype is 0 - อ่านข้อความแรกที่ได้รับในคิว

    • If msgtype is +ve - อ่านข้อความแรกในคิวของประเภท msgtype (ถ้า msgtype เป็น 10 จากนั้นจะอ่านเฉพาะข้อความแรกของประเภท 10 แม้ว่าประเภทอื่นอาจอยู่ในคิวตอนต้น)

    • If msgtype is –ve - อ่านข้อความแรกของประเภทต่ำสุดที่น้อยกว่าหรือเท่ากับค่าสัมบูรณ์ของประเภทข้อความ (เช่นถ้า msgtype เป็น -5 จะอ่านข้อความแรกของประเภทที่น้อยกว่า 5 เช่นประเภทข้อความตั้งแต่ 1 ถึง 5)

  • อาร์กิวเมนต์ที่ห้า msgflg ระบุแฟล็กบางอย่างเช่น IPC_NOWAIT (ส่งกลับทันทีเมื่อไม่พบข้อความในคิวหรือ MSG_NOERROR (ตัดทอนข้อความหากมีมากกว่า msgsz ไบต์)

การเรียกนี้จะส่งคืนจำนวนไบต์ที่ได้รับจริงในอาร์เรย์ mtext เมื่อสำเร็จและ -1 ในกรณีที่ล้มเหลว หากต้องการทราบสาเหตุของความล้มเหลวให้ตรวจสอบด้วยฟังก์ชัน errno variable หรือ perror ()

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

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

การเรียกระบบนี้ดำเนินการควบคุมคิวข้อความ (System V) ต้องส่งผ่านข้อโต้แย้งต่อไปนี้ -

  • อาร์กิวเมนต์แรก msgid รู้จักคิวข้อความเช่นตัวระบุคิวข้อความ ค่าตัวระบุจะได้รับเมื่อสำเร็จของ msgget ()

  • อาร์กิวเมนต์ที่สอง cmd เป็นคำสั่งในการดำเนินการควบคุมที่จำเป็นบนคิวข้อความ ค่าที่ถูกต้องสำหรับ cmd คือ -

IPC_STAT- คัดลอกข้อมูลของค่าปัจจุบันของสมาชิกของ struct msqid_ds ไปยังโครงสร้างที่ส่งผ่านที่ชี้โดย buf คำสั่งนี้ต้องการสิทธิ์ในการอ่านบนคิวข้อความ

IPC_SET - ตั้งค่า ID ผู้ใช้ ID กลุ่มของเจ้าของสิทธิ์ ฯลฯ ที่ชี้ไปตามโครงสร้าง buf

IPC_RMID - ลบคิวข้อความทันที

IPC_INFO - ส่งคืนข้อมูลเกี่ยวกับขีด จำกัด คิวข้อความและพารามิเตอร์ในโครงสร้างที่ชี้โดย buf ซึ่งเป็นประเภท struct msginfo

MSG_INFO - ส่งคืนโครงสร้าง msginfo ที่มีข้อมูลเกี่ยวกับทรัพยากรระบบที่ใช้โดยคิวข้อความ

  • อาร์กิวเมนต์ที่สาม buf เป็นตัวชี้ไปยังโครงสร้างคิวข้อความที่ชื่อ struct msqid_ds ค่าของโครงสร้างนี้จะใช้สำหรับ set หรือ get ตาม cmd

การเรียกนี้จะคืนค่าโดยขึ้นอยู่กับคำสั่งที่ผ่าน ความสำเร็จของ IPC_INFO และ MSG_INFO หรือ MSG_STAT ส่งคืนดัชนีหรือตัวระบุของคิวข้อความหรือ 0 สำหรับการดำเนินการอื่น ๆ และ -1 ในกรณีที่ล้มเหลว หากต้องการทราบสาเหตุของความล้มเหลวให้ตรวจสอบด้วยฟังก์ชัน errno variable หรือ perror ()

เมื่อได้เห็นข้อมูลพื้นฐานและการโทรของระบบเกี่ยวกับคิวข้อความแล้วตอนนี้ก็ถึงเวลาตรวจสอบกับโปรแกรม

ให้เราดูคำอธิบายก่อนดูโปรแกรม -

Step 1 - สร้างสองกระบวนการหนึ่งสำหรับการส่งไปยังคิวข้อความ (msgq_send.c) และอีกกระบวนการหนึ่งสำหรับการดึงข้อมูลจากคิวข้อความ (msgq_recv.c)

Step 2- การสร้างคีย์โดยใช้ฟังก์ชัน ftok () สำหรับสิ่งนี้ในตอนแรกไฟล์ msgq.txt ถูกสร้างขึ้นเพื่อรับคีย์เฉพาะ

Step 3 - ขั้นตอนการส่งดำเนินการดังต่อไปนี้

  • อ่านอินพุตสตริงจากผู้ใช้

  • ลบบรรทัดใหม่หากมีอยู่

  • ส่งเข้าสู่คิวข้อความ

  • ทำซ้ำกระบวนการจนกว่าจะสิ้นสุดการป้อนข้อมูล (CTRL + D)

  • เมื่อได้รับข้อมูลสิ้นสุดแล้วให้ส่งข้อความ“ end” เพื่อแสดงว่าสิ้นสุดกระบวนการ

Step 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 messages.