# 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="/files/FmFq0XuKpwLgvNBLAqy2" 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
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aic-eec.com/computer-operation-systems/linux-os-dev.-engineer/ipc/msg.-queue-programming/system-v.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
