# Process Synchronization

กระบวนการทำงานของโปรเซสหรือเทรดที่เกิดขึ้นอยู่ภายในระบบปฏิบัติการหลายครั้งที่อาจจะต้องมีการใช้ทรัพยากรร่วมกัน ไม่ว่าจะเป็นหน่วยความจำที่ถูกใช้ร่วมกัน (shared memory) ตัวแปรกลางที่ใช้ร่วมกัน (global variable) หรือไฟล์ที่จะต้องใช้ร่วมกัน (shared file) ซึ่งถ้าเกิดเหตุการณ์ที่โปรเซสมากกว่าหนึ่งตัวต้องการเข้ามาใช้ทรัพยากรตัวเดียวกัน ก็จะขึ้นอยู่กับว่าโปรเซสตัวใดทำงานขึ้นมาก่อนหรือเข้าถึงได้เร็วกว่ากัน ลักษณะการแย่งเข้าใช้หรือแข่งกันเข้าถึงทรัพยากรในลักษณะนี้เรียกว่า “<mark style="color:red;">**เงื่อนไขแข่งขัน**</mark>” (<mark style="color:red;">**Race Conditions**</mark>) ทำให้ตัวโปรเซสที่เร็วกว่าได้เข้าไปเปลี่ยนแปลงค่าภายในพื้นที่นั้น และถ้าไม่ได้ป้องกันในขณะที่อีกโปรเซสกำลังเข้าไปเปลี่ยนแปลงค่าในพื้นที่ส่วนนั้น แต่มีอีกโปรเซสก็เข้าไปเปลี่ยนแปลงค่าด้วยเช่นกันก็จะยิ่งทำให้เกิดความผิดพลาดของข้อมูลที่โปรเซสอื่นๆ จะได้รับผลกระทบไปด้วย

ตัวอย่างที่เห็นได้ชัดเช่น สร้างตัวแปรกลางสำหรับใช้งานร่วมกัน (`int count = 5`) เมื่อโปรเซสหนึ่งโดยสมมติให้เป็นผู้ผลิต (producer) ที่จะต้องทำการเพิ่มค่าตัวแปรกลางที่ใช้งานร่วมกัน (`count = count + 1`) แต่ในขณะที่อีกโปรเซสโดยสมมติให้เป็นผู้ใช้ (consumer) ที่จะทำการลดค่าตัวแปรกลางลง (count = count - 1) ซึ่งเมื่อโปรเซสทั้งสองเริ่มทำงานพร้อมๆกัน ผลการทำงานที่ควรจะเป็นคือ ผู้ผลิตต้องทำงานหรือเพิ่มค่าตัวแปร `count` ให้เสร็จก่อนที่ผู้ใช้จะทำงาน (ลดค่าตัวแปร `count`) หากโปรแกรมทำงานอย่างถูกต้อง ผลลัพธ์ของค่าตัวแปร `count` จะมีค่าเท่ากับ 5 แต่ผลที่ได้ของการรันโปรแกรมแต่ละครั้งอาจจะเป็น 4, 5 หรือ 6 เนื่องจากการทำงานของภาษาเครื่องจะแยกตัวแปรรีจิสเตอร์ (CPU register) ออกจากกันระหว่างคำสั่ง `count = count + 1` และคำสั่ง `count = count - 1` ดังแสดงข้างล่างนี้

<table><thead><tr><th width="348">Producer</th><th>Consumer</th></tr></thead><tbody><tr><td><code>count = count + 1</code></td><td><code>count = count - 1</code></td></tr><tr><td><code>register1 = count</code></td><td><code>register2 = count</code></td></tr><tr><td><code>register1 = register1+ 1;</code></td><td><code>register2 = register2 - 1;</code></td></tr><tr><td><code>count = register1</code></td><td><code>count = register2</code></td></tr></tbody></table>

ซึ่งจะเห็นว่าลำดับการทำงานของโปรเซสแบ่งออกเป็น 6 ขั้นตอน (ในบางครั้งการรันอาจจะสลับการทำงานกันได้) ดังนี้

{% hint style="warning" %} <mark style="color:purple;">**ผลการรัน Producer**</mark>

1. โปรเซสผู้ผลิตทำงาน `register1 = count`&#x20;
2. โปรเซสผู้ผลิตทำงาน `register1 = register1 + 1`&#x20;

โปรเซสผู้ผลิตถูกหยุดและนำออกจากหน่วยประมวลผลกลาง

1. โปรเซสผู้ใช้ทำงาน `register2 = count`&#x20;
2. โปรเซสผู้ใช้ทำงาน `register2 = register2 - 1`&#x20;

โปรเซสผู้ใชถูกหยุดและนำออกจากหน่วยประมวลผลกลาง

1. โปรเซสผู้ผลิตทำงาน `count = register1`&#x20;
2. โปรเซสผู้ใช้ทำงาน `count = register2`   <mark style="color:orange;">==> ค่า count สุดท้ายมีค่าเท่ากับ</mark> <mark style="color:orange;"></mark><mark style="color:orange;">`4`</mark>
   {% endhint %}

*หรือ*

{% hint style="warning" %} <mark style="color:purple;">**ผลการรัน Consumer**</mark>

1. โปรเซสผู้ใช้ทำงาน `register2 = count`&#x20;
2. โปรเซสผู้ใช้ตทำงาน `register2 = register2 - 1`&#x20;

โปรเซสผู้ผลิตถูกหยุดและนำออกจากหน่วยประมวลผลกลาง

1. โปรเซสผู้ผลิตทำงาน `register1 = count`&#x20;
2. โปรเซสผู้ผลิตทำงาน `register1 = register1 + 1`&#x20;

โปรเซสผู้ใชถูกหยุดและนำออกจากหน่วยประมวลผลกลาง

1. โปรเซสผู้ใช้ทำงาน `count = register2`&#x20;
2. โปรเซสผู้ผลิตทำงาน `count = register1`   <mark style="color:orange;">==> ค่า count สุดท้ายมีค่าเท่ากับ</mark> <mark style="color:orange;"></mark><mark style="color:orange;">`6`</mark>
   {% endhint %}

จากลำดับการทำงานข้างต้นจะเห็นได้ว่าเงื่อนไขแข่งขันของทั้งสองโปรเซสทำให้ผลลัพธ์สุดท้ายมีค่าเท่ากับ 4 และ 6 ซึ่งไม่ถูกต้อง เนื่องจากทั้งสองโปรเซสทำการเขียนข้อมูลตัวเดียวกัน (`count`) ในเวลาที่ขนานกัน แต่ที่ถูกต้องแล้วจะต้องให้โปรเซสผู้ผลิตทำงานให้เสร็จสิ้นโดยที่โปรเซสผู้ใช้จะต้องรอและไม่เข้าไปเปลี่ยนแปลงค่า count แต่อย่างใดก่อน เรียกส่วนพื้นที่ที่มีการถูกใช้งานร่วมกันและอาจมีแนวโน้มที่จะทำให้เกิดปัญหาดังที่กล่าวมาข้างต้นเรียกว่า  เขตวิกฤต (<mark style="color:blue;">**critical section - CS**</mark>) หรือ (<mark style="color:purple;">**critical region - CR**</mark>)&#x20;

ดังนั้นแนวทางในการป้องกันเพื่อไม่ให้เกิดปัญหาดังที่กล่าวมาข้างต้นคือการต้องหาแนวทางที่จะต้องทำให้มีเพียงโปรเซสเพียงตัวเดียว ณ เวลานั้น มีสิทธิ์ในการใช้งานทรัพยากรโดยเฉพาะในกรณีที่ต้องการเข้าไปเปลี่ยนแปลงข้อมูลภายในพื้นที่นั้นและจะต้องไม่อนุญาตให้โปรเซสอื่นเข้ามาทำการเปลี่ยนแปลงข้อมูลภายในพื้นที่ในเวลาเดียวกันเป็นอันขาด เรียกแนวทางนี้ว่า การห้ามอยู่พร้อมกัน (<mark style="color:orange;">**mutual exclusion**</mark>)

<figure><img src="/files/cWKvGyuAwwC2BpCYyjF0" alt=""><figcaption><p><em>รูปการห้ามอยู่พร้อมกันของโปรเซสภายในพื้นที่วิกฤติ</em></p></figcaption></figure>

แต่อย่างไรก็ตามการพยายามหลีกเลี่ยงสถานการณ์ race conditions นั้นอาจจะไม่เพียงพอสำหรับกลุ่มโปร  เซสที่มีความจำเป็นจริงๆที่จะต้องทำงานแบบขนานและต้องเข้าถึงข้อมูลที่ใช้ร่วมกันให้สามารถทำงานกันได้ถูกต้องและได้ประสิทธิภาพ ดังนั้นต้องอย่างน้อยอีก 3 เงื่อนไขที่จะช่วยให้การทำงานดีขึ้น&#x20;

1. ต้องไม่มีโปรเซสมากกว่าหนึ่งโปรเซสที่จะสามารถเข้าไปในเขตวิกฤตพร้อมกันได้ (mutual exclusion)
2. ต้องไม่ให้โปรเซสที่อยู่นอกเขตวิกฤติไปขัดขวางไม่ให้โปเซสอื่นเข้าถึงเขตวิกฤตไม่ได้ (progress)
3. ต้องไม่ให้โปรเซสต้องทำการรอแบบไม่มีวันสิ้นสุดเพื่อที่จะเข้าถึงเขตวิกฤต (bound waiting)

ในทางปฏิบัติการแก้ปัญาการเข้าใช้ทรัพยากรในเขตวิกฤตนั้นมีด้วยกัน 3 แนวทางคือ

1. นักพัฒนาโปรแกรมเขียนส่วนของควบคุมเองเพื่อไม่ให้แต่ละโปรเซสแย่งกัน (Application layer)
2. รองรับในระดับฮาร์ดแวร์ (Hardware support)
3. นักพัฒนาโปรแกรมเขียนส่วนของการควบคุมโดยใช้ API ที่ระบบปฏิบัติการเตรียมไว้ให้ (OS support)

## SW Solution

ตัวอย่างของการใช้ SW Solution โดยการสร้างตัวแปรที่ระบุว่าเป็นคราวของโปรเซสหมายเลขใดที่จะได้เข้าใช้งานในเขตวิกฤติ

{% tabs %}
{% tab title="Process 0" %}

```c
while(TRUE) {
	while(turn!=0); // wait
	critical_section(); 
	turn = 1; 
	noncritical_section();
}
```

{% endtab %}

{% tab title="Process 1" %}

```c
while(TRUE) {
	while(turn!=1); // wait
	critical_section();
	turn = 0;
	noncritical_section();
}
```

{% endtab %}
{% endtabs %}

จากตัวอย่างโปรแกรมข้างต้นทั้งสองโปรเซสจะตรวจสอบค่าในตัวแปร <mark style="color:orange;">`turn`</mark> ว่าเป็นรอบของใคร ถ้าค่าตรงกับหมายเลขโปรเซสของตนก็จะเข้าไปใช้ในพื้นที่เขตวิกฤตทันที (`critical_section()`) เมื่อทำงานเสร็จสิ้นแล้วก็จะเปลี่ยนค่าในตัวแปร <mark style="color:orange;">`turn`</mark> ให้เป็นของอีกโปรเซสหนึ่ง โดยทั้งสองโปรเซสจะต่างรออยู่ในพื้นที่ที่ไม่ใช่เขตวิกฤต

## Peterson’s Solution

อัลกอริทึมของปีเตอร์สัน (Peterson’s Algorithm) ซึ่งนิยมเรียกกันว่า Peterson’s solution ถูกสร้างโดยนาย Gary L. Peterson ในปี ค.ศ. 1981 เป็นแนวทางการเขียนโปรแกรมในลักษณะที่ทำงานคู่ขนานเพื่อแก้ปัญหาการแย่งชิงทรัพยากรโดยจะยอมให้ทั้งสองโปรเซสสามารถเข้าไปใช้งานทรัพยากรร่วมกันได้ โดยการกำหนดตัวแปรขึ้นมาสองตัวได้แก่ตัวแปรอาเรย์ชนิดตรรกะชื่อว่า <mark style="color:orange;">`flag[ ]`</mark> และตัวแปรชนิดจำนวนเต็ม `turn`

โดยตัวแปร <mark style="color:orange;">`flag[n]`</mark> จะเก็บค่าไม่จริง (True) ก็เท็จ (False) สมมติว่าถ้า `flag[n]` มีค่าเป็นจริง (`true`) หมายความว่าหมายเลขของโปรเซสที่ `n` ต้องการที่เข้าใช้ในเขตวิกฤติ (CR)&#x20;

```c
bool flag[0] = false;
bool flag[1] = false;
int turn;  
```

จากตัวอย่างโปรแกรมข้างต้น เมื่อโปรเซสหมายเลขศูนย์ (`0`) กำลังเข้าใช้งานภายในเขตวิกฤติอยู่ แล้วมีโปรเซส หมายเลขหนึ่ง (`1`) ต้องการจะเข้าถึงเขตวิกฤติด้วยเช่นกัน ก็จะต้องตรวจสอบว่า `flag[0]` เป็นจริงหรือไม่ และตรวจสอบว่าเป็นรอบของใคร (`turn=?`) ถ้าออกมาแล้วยังเป็นเท็จอยู่ ก็จะต้องวนรออยู่ภายในลูป while (busy wait) แต่ถ้าโปรเซสศูนย์ออกจากเขตวิกฤติแล้วก็จะกำหนดค่า `flag[0]` ให้เป็นเท็จทันทีเพื่อบอกกับอีกโปรเซสว่าไม่ได้เข้าใช้แล้ว


---

# 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/process-synchronization.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.
