> For the complete documentation index, see [llms.txt](https://docs.aic-eec.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.aic-eec.com/computer-operation-systems/linux-os-dev.-engineer/ipc/msg.-queue-programming/system-v.md).

# System V

Message Queue based on System V

จะมีลักษณะคล้ายกับไฟล์ pipe คือสามารถส่งบล็อกข้อมูลจากโปรเซสหนึ่งไปยังอีกโปรเซสหนึ่งผ่านลีนุกซ์คอร์เนลเช่นเดียวกัน โดยแต่ละบล็อกข้อมูลจะมีหมายเลขอ้างอิงกำหนดไว้ให้เพื่อให้โปรเซสอื่นๆสามารถอ้างอิงหมายเลขข้อมูลที่ต้องการนั้นได้ ซึ่งจะไม่เหมือนกับการส่งข้อมูลผ่าน pipe ซึ่งทั้งสองโปรเซสจะต้องรอซึ่งกันและกัน ดังนั้นขั้นตอนการส่งข้อมูลแบบ message queue จะมีขั้นตอนคือโปรเซสที่ทำการเขียนข้อมูลไปยัง queue (ด้วยคำสั่ง <mark style="color:purple;">**`msgsnd`**</mark>) แล้ว ก็สามารถจบการทำงานได้ทันที ถ้าโปรเซสใดต้องการอ่านก็สามารถเข้าไปอ่านข้อมูลใน queue ได้ภายหลัง (ด้วยคำสั่ง <mark style="color:purple;">**`msgrcv`**</mark>) ซึ่งตัว message queue เปรียบเสมือนกล่องส่วนกลางที่จะมีโปรเซสใดก็ได้ที่สามารถเข้ามาทิ้งข้อมูลไว้ หรือจะมีโปรเซสใดก็ได้ที่จะเข้ามาดึงข้อความจากกล่องที่แชร์ไว้นี้ได้&#x20;

ภายใต้มาตราฐาน System V IPC เมื่อโปรเซสมีการสร้าง message queue แล้ว แม้ว่าโปรเซสนั้นถูกทำลายหรือสิ้นสุดการทำงานไปแล้วก็ตาม แต่ตัว message queue นั้นก็ยังค้างอยู่ในระบบซึ่งสามารถใช้คำสั่ง ipcs ภายใน shell เพื่อแสดงรายการ message queue ที่มีอยู่ในระบบดังตัวอย่างการใช้งานข้างล่างและสามารถใช้คำสั่ง <mark style="color:purple;">**`ipcrm`**</mark> เมื่อต้องการลบ message queue ออกไปจากระบบ

```shell-session
$ ipcs -q
------ Message Queues --------
key        msqid      owner       perms      used-bytes   messages    
0x42016dfa 32768      sriborrirux  644        0            0     
```

{% hint style="info" %}
**ฟังก์ชันและตัวแปรที่เกี่ยวข้อง**

* ไลบรารีที่เกี่ยวข้องคือ <mark style="color:purple;">**`sys/types.h`**</mark>, <mark style="color:purple;">**`sys/ipc.h`**</mark> และ <mark style="color:purple;">**`sys/msg.h`**</mark>
* ฟังก์ชัน <mark style="color:orange;">**`int msgget(key_t key, int msgflg);`**</mark>
  * เมื่อต้องการติดต่อไปยัง message queue หรือสร้าง message queue ขึ้นมาใหม่ โดยการระบุหมายเลขกุญแจ (*key*) ซึ่งในกรณีที่มีการสร้าง message queue จะต้องระบุค่าแฟลก (flag) ให่กับตัวแปร `msgflg` เช่น `0666|IPC_CREAT` เพื่อระบุสิทธิ์การเข้าถึงของ message queue ของตัวที่จะถูกสร้างขึ้น
    {% endhint %}

ตัวอย่างเช่น โปรแกรม A ต้องการสร้างกุญแจ เพื่อทำการสร้าง message queue ตัวใหม่ จะมีขั้นตอนดังนี้

```c
...
key = ftok("/home/wiroon/somefile", 'P');
msqid = msgget(key, 0666 | IPC_CREAT);
...
```

ซึ่งจากคำสั่งข้างต้นก็จะได้ message queue ที่มีสิทธิ์การเข้าถึงเป็น `666` หรือ `rw-rw-rw-` และหมายเลขเฉพาะของ `msqid` เพื่อให้โปรเซสอื่นๆอ้างอิงหมายเลขของ message queue ที่ต้องการจะเข้าถึงได้ แต่อย่างไรก็ตามโปรเซสใดก็ตามถ้าต้องการเข้าถึง message queue ที่ถูกสร้างโดยโปรแกรม A ก็จะต้องมีขั้นตอนในการนำกุญแจที่จะสามารถเข้ามาเปิดเอาหมายเลข message queue นั้นได้ ดังนั้น โปรแกรมอื่นๆ จึงจำเป็นต้องใช้พารามิเตอร์ภายในฟังก์ชัน `ftok()` เดียวกันโปรแกรม A ใช้ แล้วจึงจะได้หมายเงขของ message queue (msqid) โดยใช้คำสั่งภายในดังตัวอย่างโปรแกรมข้างล่างนี้

```c
...
key = ftok("/home/wiroon/somefile", 'P');
msqid = msgget(key, 0666);
...
```

หลังจากสร้างกุญแจและ message queue ได้เรียบร้อยแล้ว แต่อย่างไรก็ตาม message queue ก็ยังมีข้อจำกัดเรื่องขนาดบล็อกข้อมูล ที่จะถูกจำกัดขนาดไว้รวมทั้งจำนวนบล็อกทั้งหมดที่มีบนระบบก็จะถูกจำกัดไว้เช่นกัน ซึ่งภายในระบบปฏิบัติการลีนุกซ์ตัวแปรบล็อกข้อมูลจะประกอบไปด้วย 2 ส่วนคือ `MSGMAX(4096)` และ `MSGMNB(16384)` ซึ่งตัวแรกจะหมายถึงความจุของแต่ละบล็อกข้อความ และตัวที่สองจะหมายถึงความจุทั้งหมดของ queue แต่ในบางระบบ ค่าเหล่านี้อาจจะแตกต่างกันไป หรืออาจจะไม่ใช้ค่านี้เลยก็เป็นได้ &#x20;

{% hint style="info" %}
**ฟังก์ชันและตัวแปรที่เกี่ยวข้อง**

* ฟังก์ชัน <mark style="color:orange;">**`int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);`**</mark>
  * เป็นฟังก์ชันสำหรับใช้ส่งข้อความเข้าไปเก็บใน message queue ตามที่ระบุมหายเลขไว้ (msqid) สำหรับตัวพอยเตอร์ `*msgp` เป็นตัวชี้ไปยังข้อมูลที่ต้องการนำไปวางบน message queue ตามขนาดไบต์ข้อมูลที่ระบุไว้ (msgsz) นอกจากนั้นก็สามารถกำหนดค่าแฟลกในตัวแปร `msgflg` ได้เช่นกัน แต่โดยทั่วไปจะกำหนดไว้ในเป็นศูนย์
    {% endhint %}

ในทางปฏิบัติการสร้างข้อมูลเพื่อนำไปเก็บไว้ใน message queue จะมีการสร้างให้อยู่ในลักษณะตัวแปรแบบ struct เช่น

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

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

```c
/* calculate the size of the data to send: */
int size = sizeof(struct my_msgbuf) - sizeof(long); 
```

{% hint style="info" %}
**ฟังก์ชันและตัวแปรที่เกี่ยวข้อง**

* ฟังก์ชัน <mark style="color:orange;">**`int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);`**</mark>
  * เป็นฟังก์ชันสำหรับอ่านข้อมูลจาก message ลงในตัวแปรพอยเตอร์ `*msgp` ที่ชี้ไปที่ข้อมูลที่ได้รับมา ดังตัวอย่างข้างล่างนี้

```c
...
key_t key;
int msqid;
struct my_msgbuf rev_msgbuf;
struct pirate_msgbuf pmb; /* where data is to be kept */


key = ftok("/home/wiroon/somefile", 'P');
msqid = msgget(key, 0666 | IPC_CREAT);


/* get data off the queue! */
msgrcv(msqid, &rev_msgbuf, sizeof(struct my_msgbuf) - sizeof(long), 2, 0);
...
```

* ฟังก์ชัน <mark style="color:orange;">**`int msgctl(int msqid, int cmd, struct msqid_ds *buf);`**</mark>
  * เป็นฟังก์ชันสำหรับใช้ในควบคุม message queue หมายเลขที่ต้องการ (msqid) โดยการระบุคำสั่ง (cmd) ที่ต้องการเช่น `IPC_RMID` เพื่อต้องการลบ queue เป็นต้น ทิ้งออกจากระบบ นอกจากจะใช้คำสั่ง ipcrm ที่เคยอธิบายไว้ข้างบน ซึ่งในกรณีที่ใช้ `IPC_RMID` ก็สามารถระบุค่า `NULL` ให้กับตัวแปร `*buf` ได้
    {% endhint %}

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2FPTfeLJyVUyAl0Jy6Vdhf%2FMQ_SysV.png?alt=media&amp;token=cedb776d-e29c-420d-98e0-adb0856da8ad" alt=""><figcaption><p><em>แสดงการส่งข้อมูลแบบ message queue</em></p></figcaption></figure>

{% hint style="success" %}

### ตัวอย่างที่ 1

{% endhint %}

แสดงตัวอย่างโปรแกรมสำหรับส่งบล๊อกข้อมูลเข้าไปใน Message Queues โดยสร้างโปรแกรมส่งข้อมูลไปเก็บไว้ใน message queue ชื่อว่า `msg_sender.c` และโปรแกรมสำหรับอ่านค่าจาก message queue ชื่อว่า `msg_receiver.c` ดังข้างล่างนี้

```c
/*
 * System V IPC
 ** msg_sender.c -- writes to a message queue
 */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

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

int main(void) {
	struct my_msgbuf buf;
	int msqid;
	key_t key;

	if ((key = ftok("msg_sender.c", 'B')) == -1) {
		perror("ftok");
		exit(1);
	}

	if ((msqid = msgget(key, 0644 | IPC_CREAT)) == -1) {
		perror("msgget");
		exit(1);
	}
	printf("Enter lines of text, ^D to quit:\n");

	buf.mtype = 1; /* in this case we don’t need to classify any data types yet */
	while (fgets((buf.mtext), sizeof(buf) + 1, stdin)) {
		if (msgsnd(msqid, (struct msgbuf*) &buf, sizeof(buf), 0) == -1)
			perror("msgsnd");
	}

	if (msgctl(msqid, IPC_RMID, NULL) == -1) {
		perror("msgctl");
		exit(1);
	}

	return 0;
}
```

โปรแกรม `msg_receiver.c` สำหรับอ่านบล๊อกข้อมูลภายใน Message Queues ที่โปรแกรม `msg_sender.c` เขียนข้อมูลเข้าไป

```c
/*
 ** msg_receiver.c -- reads from a message queue
 */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

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

int main(void) {
	struct my_msgbuf buf;
	int msqid;
	key_t key;

	if ((key = ftok("msg_sender.c", 'B')) == -1) {
		/* same key as msg_sender.c */
		perror("ftok");
		exit(1);
	}
	if ((msqid = msgget(key, 0644)) == -1) { /* connect to the queue */
		perror("msgget");
		exit(1);
	}
	printf("spock: ready to receive messages, captain.\n");

	for (;;) { /* Spock never quits! */
		if (msgrcv(msqid, (struct msgbuf*) &buf, sizeof(buf), 0, 0) == -1) {
			perror("msgrcv");
			exit(1);
		}
		printf("spock: \"%s\"\n", buf.mtext);
	}

	return 0;
}
```

{% tabs %}
{% tab title="หน้าต่าง Terminal ที่ 1" %}
ทำการคอมไพล์โปรแกรม `msg_sender` แล้วรันโปรแกรมเพื่อพร้อมเขียนข้อมูลลง message queue ต่อไป

```shell-session
$ gcc -o msg_sender msg_sender.c -Wall
$ ./msg_sender
Enter lines of text, ^D to quit:

```

{% endtab %}

{% tab title="หน้าต่าง Terminal ที่ 2" %}
ทำการเปิดอีกหน้าต่าง terminal ขึ้นมาเพื่อคอมไพล์โปรแกรม msg\_receiver.c เมื่อมีข้อความเข้ามายัง message queue แล้ว โปรแกรม msg\_receiver จะอ่านออกมาดังผลลัพธ์ข้างล่างนี้

```shell-session
$ gcc -o msg_receiver msg_receiver.c -Wall
$ ./msg_receiver
spock: ready to receive messages, captain.

```

{% endtab %}
{% endtabs %}

กลับมาที่ หน้าต่าง Terminal ที่ 1 เพื่อทำการพิมพ์ข้อความเพื่อส่งไปยัง msg\_receiver ดังตังอย่างข่างล่าง

> <mark style="color:orange;">**หน้าต่าง Terminal ที่ 1**</mark>

```shell-session
...
Enter lines of text, ^D to quit:
Hello from Sender....   <-- ข้อความที่พิพม์จาก msg_sender ในหน้าต่าง terminal ที่ 1
```

จะสังเกตุเห็นข้อความปรากฏขึ้นในหน้าต่าง Terminal ที่ 2 เนื่องจากโปรเซส msg\_receiver ได้รับแล้วแสดงดังนี้

> <mark style="color:green;">**หน้าต่าง Terminal ที่ 2**</mark>

```shell-session
...
spock: ready to receive messages, captain.
spock: "Hello from Sender...." <-- ข้อความที่ msg_receiver ได้รับ ในหน้าต่าง terminal ที่ 2
```
