คิวข้อความ
ทำไมเราต้องมีคิวข้อความเมื่อเรามีหน่วยความจำที่ใช้ร่วมกันอยู่แล้ว อาจมีหลายสาเหตุให้เราลองแบ่งประเด็นนี้ออกเป็นหลาย ๆ จุดเพื่อให้ง่ายขึ้น -
ตามที่เข้าใจแล้วเมื่อได้รับข้อความจากกระบวนการแล้วกระบวนการอื่นจะไม่สามารถใช้ได้อีกต่อไป ในขณะที่หน่วยความจำแบบแบ่งใช้ข้อมูลจะพร้อมใช้งานสำหรับกระบวนการต่างๆในการเข้าถึง
หากเราต้องการสื่อสารด้วยรูปแบบข้อความขนาดเล็ก
ข้อมูลหน่วยความจำที่ใช้ร่วมกันจำเป็นต้องได้รับการป้องกันด้วยการซิงโครไนซ์เมื่อหลายกระบวนการสื่อสารในเวลาเดียวกัน
ความถี่ในการเขียนและอ่านโดยใช้หน่วยความจำที่ใช้ร่วมกันนั้นสูงดังนั้นการใช้ฟังก์ชันนี้จะซับซ้อนมาก ไม่คุ้มกับการใช้ประโยชน์ในกรณีแบบนี้
จะเกิดอะไรขึ้นถ้ากระบวนการทั้งหมดไม่จำเป็นต้องเข้าถึงหน่วยความจำที่ใช้ร่วมกัน แต่มีกระบวนการน้อยมากที่ต้องการเพียงแค่ใช้งานกับคิวข้อความจะดีกว่า
หากเราต้องการสื่อสารกับแพ็กเก็ตข้อมูลที่แตกต่างกันให้พูดว่ากระบวนการ 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.