# System V

หน่วยความจำที่ถูกใช้ร่วมกัน หรือเรียกว่า shared memory นั้นเป็นพื้นที่ที่จะให้โปรเซสทั้งหลายสามารถใช้เป็นที่แลกเปลี่ยนสื่อสารข้อมูลระหว่างกันได้ ซึ่ง shared memory นี้เป็นพื้นที่ของหน่วยความจำเสมือน (virtual address space) โดยแต่ละหน้า (page) ของหน่วยความจำเสมือนนี้จะถูกอ้างอิงโดยชุดเก็บตารางของแต่ละหน้า (page table) ในแต่ละตารางหน้าของโปรเซสที่กำลังใช้งานร่วมกัน ในระบบ System V การเข้าถึงพื้นที่ของหน่วยความจำที่ใช้งานร่วมกันนั้นจะถูกควบคุมไปกับชุดกุญแจและการตรวจสอบสิทธิ์การเข้าถึง (access rights) &#x20;

เมื่อเกิดพื้นที่หน่วยความจำส่วนหนึ่งกลายเป็น shared memory segments ขึ้น ก็ยังไม่มีกลไกการตรวจสอบว่าจะให้แต่ละโปรเซสเข้าใช้งานอย่างไร ซึ่งจะต้องใช้กลไกอย่างอื่นช่วยเหลือแทน ตัวอย่างเช่น System V semaphores ที่ทำให้มีการทำงานที่สอดคล้องกัน เพื่อให้การเข้าถึงหน่วยความจำนี้ไม่เกิดปัญหา เป็นต้น&#x20;

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

* ไลบรารีที่เกี่ยวข้องคือ <mark style="color:purple;">**`sys/ipc.h`**</mark>, <mark style="color:purple;">**`sys/types.h`**</mark> และ <mark style="color:purple;">**`sys/shm.h`**</mark>
* ฟังก์ชัน <mark style="color:orange;">**`int shmget(key_t key, size_t size, int shmflg);`**</mark>
  * ในการสร้างกุญแจ (key) นั้นจะมีขบวนการคล้ายกับการสร้างกุญแจของ message queues ด้วยฟังก์ชัน `ftok()` ถัดมาจะเป็นขนาด (`size`) ของส่วนของหน่วยความจำที่เข้าใช้งานร่วมกัน (shared memory segment) ในส่วนสุดท้าย (`shmflg`) จะเป็นการกำหนดสิทธิการเข้าถึงและแฟลกที่ระบุการสร้าง (`IPC_CREAT`) ซึ่งเมื่อสามารถสร้างส่วนพื้นที่หน่วยความจำนี้ได้ฟังก์ชัน `shmget()` จะส่งค่าหมายเลขของพื้นที่ (`shmid`) ออกมา

ดังตัวอย่างการกำหนดสิทธิให้เป็น `644 (rw-r--r--)` ในส่วนพื้นที่ขนาด `1KB`

```c
// ...
key_t key;
int shmid;

key = ftok("/home/wiroon/somefile", 'R');
shmid = shmget(key, 1024, 0644 | IPC_CREAT);
// ...
```

* ฟังก์ชัน <mark style="color:orange;">**`void *shmat(int shmid, void *shmaddr, int shmflg);`**</mark>
  * ใช้เพื่ออ่านค่าพอยเตอร์ที่ชี้ไปยังส่วนพื้นที่สำหรับเก็บข้อมูล ซึ่งอ้างอิงจากหมายเลขพื้นที่หน่วยความจำ (`shmid`) ที่ได้รับจากฟังก์ชัน `shmget()` สำหรับส่วนของพื้นที่หน่วยจำที่จะให้ใช้นั้นก็ระบุในตัวแปร `shmaddr` แต่อย่างไรก็ตามควรจะกำหนดให้เป็นศูนย์ (`0`) เพื่อปล่อยให้ระบบปฏิบัติการเป็นคนเลือกให้ตามความเหมาะสมเอง สำหรับแฟลก (`shmflg`) นั้นสามารถที่จะถูกกำหนดให้เป็น `SHM_RDONLY` ได้ถ้าต้องการเพียงแค่อ่าน หรืออีกอย่างหนึ่งก็ตั้งให้เป็นศูนย์ (`0`) เพื่อใช้ทั้งอ่านและเขียน

ดังตัวอย่างการดึงตำแหน่งของพื้นที่สำหรับเก็บข้อมูลขนาด `1KB` ข้างล่างนี้

```c
// ...
key_t key;
int shmid;
char *data;

key = ftok("/home/wiroon/somefile", 'R');
shmid = shmget(key, 1024, 0644 | IPC_CREAT);
data = shmat(shmid, (void *)0, 0);
if (data == (char *)(-1))
    perror("shmat");
// ...
```

ดังนั้นเมื่อได้ตำแหน่งชี้พื้นที่เก็บข้อมูล ((char)\*data) เรียบร้อยแล้ว ต่อไปก็เป็นเพียงการเรียกฟังก์ชันพื้นฐานสำหรับอ่านและเขียนค่าที่เป็นชนิดข้อความลงไปในพื้นที่ดังกล่าว ตัวอย่างเช่น เมื่อต้องการแสดงค่าที่อยู่ในส่วนของพื้นที่หน่วยความจำนั้น

```c
printf("shared contents: %s\n", data);
```

หรือต้องการบันทึกข้อมูลลงไปในพื้นที่ส่วนนั้น

```c
printf("Enter a string: ");
gets(data);
```

* ฟังก์ชัน <mark style="color:orange;">**`int shmdt(void *shmaddr);`**</mark>
  * ใช้เมื่อต้องการถอนออกจากการเข้าใช้พื้นที่หน่วยความจำโดยการระบุตำแหน่งของพื้นที่ของหน่วยความจำนั้น (`shmaddr`) ที่ได้มาจากฟังก์ชัน `shmat()`
    {% endhint %}

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

```shell-session
$ ipcs -m
------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 294912     sriborriru 600        524288     2          dest         
0x00000000 1245185    sriborriru 600        524288     2          dest         
0x00000000 425986     sriborriru 600        524288     2          dest         
0x00000000 622595     sriborriru 600        524288     2          dest         
0x00000000 786436     sriborriru 600        524288     2          dest         
0x00000000 819205     sriborriru 600        33554432   2          dest         
```

{% hint style="success" %}

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

{% endhint %}

ตัวอย่างโปรแกรมสำหรับสร้างส่วนของพื้นที่หน่วยความจำสำหรับใช้งานร่วมกัน (shared memory segments) และการเข้าใช้งานพื้นฐาน&#x20;

```c
// shmdemo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE 1024  /* make it a 1K shared memory segment */

int main(int argc, char *argv[]) {
	key_t key;
	int shmid;
	char *data;

	if (argc < 2) {
		fprintf(stderr, "usage: ./shmdemo [data_to_write]\n");
		exit(1);
	}

	/* make the key: */
	if ((key = ftok("shmdemo.c", 'R')) == -1) {
		perror("ftok");
		exit(1);
	}

	/* connect to (and possibly create) the segment: */
	if ((shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT)) == -1) {
		perror("shmget");
		exit(1);
	}

	/* attach to the segment to get a pointer to it: */
	data = shmat(shmid, (void*) 0, 0);
	if (data == (char*) (-1)) {
		perror("shmat");
		exit(1);
	}

	/* read or modify the segment, based on the command line: */
	if (argc == 2) {
		printf("writing to segment: \"%s\"\n", argv[1]);
		strncpy(data, argv[1], SHM_SIZE);
	} else
		printf("segment contains: \"%s\"\n", data);

	/* detach from the segment: */
	if (shmdt(data) == -1) {
		perror("shmdt");
		exit(1);
	}
	return 0;
}
```

ทำการคอมไพล์และรันโปรแกรม โดยใส่อาร์กิวเม้นต์เป็นชุด

```shell-session
$ gcc -o shmdemo shmdemo.c 
$ ./shmdemo                                                                                                                                     ─╯
usage: shmdemo [data_to_write]

$ ./shmdemo "I'm going to access this shared memory segment..."
writing to segment: "I'm going to access this shared memory segment..."
```

{% hint style="warning" %}
&#x20;**สิ่งที่ควรรู้**  &#x20;

เนื่องจากพื้นที่หน่วยความจำส่วนนี้เป็นส่วนที่แชร์ให้แต่ละโปรเซสสามารถเข้าถึงได้ ดังนั้นอาจจะเกิดเหตุการณ์ที่มีมากกว่าหนึ่งโปรเซสต้องการเข้ามาเพื่อทำการเขียนข้อมูลลงในพื้นที่ส่วนนี้พร้อมในเวลาเดียวกัน (concurrency) ผลกระทบจากการเข้าใช้พื้นที่ในเวลาเดียวกันนี้ อาจจะทำให้เกิดการเสียหายของข้อมูลที่แต่ละโปรเซสได้เขียนลงไป แล้วส่งผลให้โปรเซสที่ต้องการจะอ่านข้อมูลจากพื้นที่นี้เพื่อนำไปประมวลผลต่อเกิดความเสียหายได้ ดังนั้นเพื่อแก้ปัญหาการเข้าใช้งานทรัพยากรที่แชร์ร่วมกันภายในระบบ จึงจำเป็นจะต้องมีกลไกการควบคุมและจัดการเหตุการณ์ที่โปรเซสจะเข้ามาพร้อมกันในเวลาเดียวกัน เช่นเทคนิค Semaphore เป็นต้น ซึ่งจะกล่าวถึงในบทถัดไป
{% endhint %}


---

# 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/share-memory-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.
