วันพฤหัสบดีที่ 6 มีนาคม พ.ศ. 2551

บทที่2 โปรเซส และ Thread

บทที่ 2โปรเซส และ Thread
การศึกษาเรื่องโปรเซส เป็นหัวใจของการทำความเข้าใจของระบบคอมพิวเตอร์ที่มีผู้ใช้หลายคน ( multiuser computer system ) คำว่าโปรเซสถูกใช้ครั้งแรกโดยผู้ออกแบบระบบมัลติกส์ ( multics ) ในทศวรรษ 1690 มีการให้ความหมายของคำว่า "โปรเซส" ไว้หลายความหมายเช่น
- โปรแกรมที่กำลังถูกเอ็กซีคิ้ว
- กิจกรรมที่มีการทำงานสัมพันธ์กัน
- สิ่งที่ถูกมอบหมายไปให้โปรเซสเซอร์ได้
- หน่วยซึ่งถูกส่งต่อได้ ( dispatchable )
ยังไม่มีความหมายใดที่เป็นที่ยอมรับกันทุกคน แต่ความหมายที่ว่า โปรเซส คือ "โปรแกรมที่กำลังถูกเอ็กซีคิ้ว" นั้นถูกใช้บ่อยมากที่สุด ดังนั้นจึงเอาความหมายนี้เป็นความหมายของคำว่า โปรเซส เราอาจเปรียบเทียบโปรแกรมเหมือนกับรถยนต์ที่จอดนิ่งอยู่ที่พร้อมที่จะวิ่งไป ในระบบหลายโปรแกรม ( multiprogramming ) โปรเซสอาจเปรียบกับรถยนต์ที่วิ่งออกจากจุดเริ่มต้น ถ้ามีหลายโปรเซสอยู่ในระบบก็เหมือนกับการที่เรามีรถหลายคันที่จะต้องออกวิ่ง ไปพร้อม ๆ กัน ตัวซีพียูเปรียบได้กับคนขับรถ ถ้าซีพียูมีตัวเดียวก็เหมือนกับคนขับรถมีเพียงคนเดียว ดังนั้นเมื่อรถหลายคันออกวิ่งการที่คนขับคนเดียวจะพารถหลายๆ คันวิ่งไปต้องขับรถทีละคันให้วิ่งเดินหน้าไปทีละนิด เวียนเปลี่ยนไปจนครบทุกคัน จนถึงจุดหมายปลายทาง ( โปรแกรมสิ้นสุดลง ) นั้นคือเราสามารถมีโปรเซสหลายๆ โปรเซสทำงานไปพร้อมๆ กันได้โดยมีซีพียูเพียงตัวเดียว
2.1องค์ประกอบของโปรเซส
1. ชื่อและหมายเลขประจำตัว (process ID) ของโปรเซส ซึ่งต้องไม่ซ้ำกับโปรเซสอื่น
2. โค้ดของโปรแกรม (program code) เป็นคำสั่งที่สามารถเอ็กซีคิ้วได้ทันที (ภาษาเครื่อง)
3. ข้อมูล (data) ที่โปรแกรมต้องการหรือจัดการ ข้อมูลนี้อาจเป็นของโปรเซสใดโปรเซส
หนึ่ง หรืออาจใช้ได้ร่วมกันกับโปรเซสอื่นๆ ก็ได้
4. บล็อกควบคุมโปรเซส (process control block) หรือ PCB OS กำหนดเนื้อที่บาง
ส่วนในหน่วยความจำเพื่อทำเป็น PCB PCB เป็นโครงสร้างข้อมูลชนิดหนึ่งซึ่งเก็บข้อมูลที่สำคัญๆ ของโปรเซสนั้นๆ เอาไว้ ข้อมูลเหล่านี้ได้แก่
4.1 พอยเตอร์ (Pointer)
4.2 สถานะของโปรเซส (Process state)
4.3 หมายเลขโปรเซส (Program id)
4.4 ตัวนับจำนวน (Program counter)
4.5 รีจิสเตอร์ (Register)
4.6 ข้อมูลการจัดเวลาของซีพียู (CPU scheduling information)
4.7 ข้อมูลการจัดการหน่วยความจำ (Memory management information)
4.8 ข้อมูลแอ็กเคาต์ (Account information)
4.9 ข้อมูลสถานะอินพุต/เอาต์พุต (I/O status information)
5. PSW (program status words) เป็นตัวควบคุมลำดับการเอ็กซีคิ้วคำสั่งของโปรเซส และยังเก็บข้อมูลเกี่ยวกับสถานะของโปรเซส แอดเดรสของคำสั่งต่อไปที่จะถูกเอ็กซีคิ้วจะถูกเก็บไว้ใน PSW PSW นี้จึงมีหน้าที่คล้ายๆ กับโปรแกรมเคาน์เตอร์บนเครื่องไมโครคอมพิวเตอร์
6. คุณสมบัติของโปรเซส ได้แก่
6.1 ลำดับความสำคัญ (Priority) ของโปรเซส โปรเซสแต่ละตัวจะถูกกำหนดความสำคัญขึ้นขณะที่โปรเซสถูกสร้างขึ้น ความสำคัญนี้อาจเปลี่ยนแปลงได้หรือไม่ได้สุดแล้วแต่ OS โปรเซสที่มีความสำคัญมาก OS ก็จะให้สิทธิพิเศษมากกว่าโปรเซสที่มีความสำคัญน้อย เช่น ให้เวลาของซีพียูนานกว่า (ให้ครอบครองซีพียูได้นานกว่า)
6.2 อำนาจหน้าที่ (authority) เป็นสิ่งที่บ่งบอกว่าโปรเซสนั้นๆ สามารถทำอะไรได้บ้าง ใช้อุปกรณ์ชิ้นไหนได้บ้างเป็นต้น ตัวอย่างเช่นโปรเซส A ห้ามใช้ดิสก์ใดๆ ทั้งสิ้นแต่สามารถรับข้อมูลจากทุกๆ โปรเซสในระบบได้
6.3 คุณสมบัติอื่นๆ ที่ OS จะกำหนดให้มี
2.2 สถานะของโปรเซส
สถานะของโปรเซสแบ่งได้ 6 สถานะ
ในขณะที่โปรเซสกำลังถูกประมวลผลอยู่นั้น โปรเซสอาจมีการเปลี่ยนสถานะก็ได้ การกำหนดสถานะของโปรเซสจะเป็นไปตามกิจกรรมที่กำลังดำเนินการอยู่ในขณะนั้น แต่ละโปรเซสอาจมีสถานะต่อไปนี้ 1. สถานะเริ่มต้น(New) เป็นสถานะที่เริ่มต้นเมื่อสร้างโปรเซสขึ้นมา 2. สถานะพร้อม(Ready) เป็นสถานะที่พร้อมจะครอบครอง CPU ในทันทีที่ระบบปฏิบัติการมอบหมายงานให้ สถานะนี้จะยังไม่มีการรันโปรเซส เพียงแต่หยุดนิ่งรอเท่านั้น 3. สถานะรัน หรือสถานะทำงาน(Running) เป็นสถานะที่โปรเซสที่ครอบครอง CPU อยู่แล้วมีการรันนั้นโดยมีการประมวลผลคำสั่งในโปรแกรมของโปรเซส4. สถานะรอ(Wait) เป็นสถานที่ที่โปรเซสกำลังรอเหตุการณ์บางอย่างให้เกิดขึ้นก่อน เช่นการจัดการ I/O ก่อน หรืออาจจะเป็นการรอสัญญาณ 5. สถานะบล็อก(Block) เป็นสถานะที่อาจจะต้องการใช้อุปกรณ์ I/O หรือเกิดอินเทอร์รัพต์ ระหว่างที่รันโปรเซส จะต้องมีการรอเพื่อให้มีการจัดการอุปกรณ์ I/O หรือจัดการเทอร์รัพต์ให้เรียบร้อยก่อนที่จะกลับไปสถานะรันได้ต่อไป 6. สถานะสิ้นสุด(Terminate)เป็นสถานะที่หยุดนิ่งเนื่องมาจากโปรเซสถูกประมวลผลเสร็จหมดแล้ว
สถานะของโปรเซสแบ่งได้อีกแบบมี 4 สถานะ
1. สถานะพร้อม (ready state) คือสถานะที่โปรเซสพร้อมที่จะใช้ซีพียูทันทีที่ระบบปฏิบัติการมอบหมายให้ ในสถานะนี้ไม่มีการรันของโปรเซส
2. สถานะรัน (running state) คือสถานะที่โปรเซสกำลังครอบครองซีพียูอยู่ มีการรันของโปรเซสจริงๆ เพราะโปรเซสใช้ซีพียูเอ็กซีคิ้วคำสั่ง หรือโค้ดโปรแกรมของโปรเซสนั้น
3. สถานะติดขัด (blocked state) คือสถานะที่โปรเซสหยุดรอเหตุการณ์ใดเหตุการณ์หนึ่งให้เกิดขึ้น โปรเซสไม่จำเป็นต้องใช้ซีพียูและยังไม่พร้อมที่จะครอบครองซีพียู ซึ่งจะทำให้
โปรเซสอื่นเข้ามาครอบครองซีพียูในช่วงนี้ได้
4. สถานะพัก (suspend state) คือสถานะที่โปรเซสไม่มีการทำงานใดๆ หยุดนิ่งอย่างสมบูรณ์ ไม่มีการรอการใช้ซีพียูหรือเหตุการณ์ใดๆ ให้เกิดขึ้น
2.3การจัดเวลาโปรเซส
ใช้ในระบบมัลติโปรแกรมมิ่ง คือการจัดโปรเซสให้รันตลอดเวลา เพื่อให้ใช้ประโยชน์ซีพียูได้สูงสุด
โปรเซสที่ระบบปฏิบัติการสร้างขึ้นมาใหม่จะอยู่นสถานะพร้อมและเก็บ อยู่ในคิว เรียกว่า Ready Queue เพื่อรอการรัน
ต้องพิจารณาหน่วยความจำของระบบมีเพียงพอหรือไม่
Ready Queue จะเก็บโปรเซสในลักษณะลิงค์ลิสต์ (Link list) ที่ส่วนหัวของคิว จะประกอบด้วยพอยเตอร์ของ PCB เริ่มต้นและท้ายสุดของลิสต์ และแต่ละ PCB จะมีพอยเตอร์ชี้ไปยัง PCB ตัวต่อไปของคิว

รูป 2.3 คิวที่พร้อมทำงานและอินพุต/เอาต์พุตอื่น ๆ


รูป2.4 เปลี่ยนสถานะของโปรเซสสั้นๆ
2.3.1 ดีไวซ์คิว(Device Queue) สำหรับคิวของโปรเซสที่รอการตอบสนองจากการใช้อุปกรณ์อินพุต/เอาต์พุตเรียกว่า “ดีไวซ์คิว” ซึ่งอุปกรณ์แต่ละอันจะมีคิวเป็นของตนเอง ที่มีพอยเตอร์เก็บส่วนหัวและส่วนท้ายของ PCB ที่เรียกใช้อุปกรณ์นี้ และในส่วนของ PCB ก็จะมีพอยเตอร์ที่บอกว่า PCB ใดเป็นโปรเซสต่อไปที่จะรัน
รูป2.7 การจัดคิวของโปรเซส
2.3.2 คอนเท็กซ์สวิตช์(Context Switch) เมื่อ CPU รันโปรเซสจนเสร็จเรียบร้อยแล้วจะต้องมีการสวิตช์ หรือสลับไปยังโปรเซสในคิวต่อไป การสวิตช์นี้เรียกว่าคอนแท็กซ์สวิตช์ ในช่วงที่ทำการสวิตช์จะไม่มีการทำงานใดๆ ส่วนเวลาที่ใช้ในการสวิตช์จะแตกต่างไปตามเครื่อง เช่นความเร็วของหน่วยเก็บความจำ, จำนวนรีจิสเตอร์ที่ถูกกอปปี้และคำสั่งพิเศษที่มีอยู่ในระบบ โดยทั่วไปความเร็วจะอยู่ระหว่าง 1-1000 ไมโครวินาที จะเห็นว่าการสวิตช์จะขึ้นอยู่กับประสิทธิภาพของเครื่อง และเมื่อเครื่องคอมพิวเตอร์มีอุปกรณ์พิเศษอย่างเช่นมีหลายรีจิสเตอร์ หรือมีหลาย CPU การจัดการอุปกรณ์เหล่านี้ได้ต้องมีระบบปฏิบัติการที่ดีด้วย และเนื่องจากข้อจำกัดของอุปกรณ์ฮาร์ดแวร์ทำให้เกิดปัญหาคอขวดขึ้น เพื่อแก้ไขปัญหานี้จึงมีการเพิ่มประสิทธิภาพโครงสร้างใหม่ที่เรียกว่า Thread ขึ้นมา
2.4 โอเปอเรชันของโปรเซส
โปรเซสต่างๆ ในระบบที่เอ็กซิคิวได้พร้อมกัน จะต้องมีการสร้างและลบตลอดเวลา ระบบปฎิบัติการเป็นตัวสร้างเหล่านี้ โดยทั้งสั่งให้โปรเซสสิ้นสุดโดยใช้กลไกที่น่าสนใจดังนี้
2.4.1การสร้างโปรเซส (Process Creation)
โปรเซสหนึ่ง ๆ อาจสร้างโปรเซสใหม่เกิดขึ้น โดยการใช้คำสั่งเรียกระบบสร้างโปรเซส และโปรเซสใหม่ ๆ ที่เกิดขึ้นมานั้นอาจจะสร้างโปรเซสใหม่ ๆ ต่อไปได้อีก ดังรูป

รูปที่ 2.8 รูปแบบโครงสร้างโปรเซส
โปรเซสที่เป็นผู้สร้าง เรียกว่า โปรเซสแม่ (Parent Process) โปรเซสใหม่ที่ถูกสร้างขึ้นมาเรียกว่า โปรเซสลูก (Children Process) โดยทั่วไป โปรเซสหนึ่ง ๆ จะต้องการทรัพยากร เช่น เวลาที่ใช้ในหน่วยประมวลผลกลาง , หน่วยความจำ , แฟ้มข้อมูล ,อุปกรณ์รับ-ส่งข้อมูล เป็นจำนวนที่แน่นอน เพื่อที่จะทำงานได้จนเสร็จสมบูรณ์ และเมื่อมีโปรเซสย่อยเกิดขึ้น โปรเซสย่อยนั้นอาจร้องขอทรัพยากร ที่ต้องการจากระบบปฏิบัติการได้โดยตรง หรือ ถูกจำกัดให้ใช้ได้เฉพาะทรัพยากรส่วนของทรัพยากรแม่เท่านั้น ดังนั้น โปรเซสแม่ต้องแบ่งทรัพยากรให้โปรเซสย่อยแต่ละตัว หรือ ให้โปรเซสย่อยเหล่านั้น ใช้ทรัพยากรร่วมกัน (เช่น แฟ้มข้อมูล หรือ หน่วยความจำ) หรือ ให้โปรเซสย่อยใช้ทรัพยากรส่วนเฉพาะของแม่ (ซึ่งเป็นการป้องกันไม่ให้ระบบถูกแย่งทรัพยากรไปทั้งหมด เนื่องจากมีบางโปรเซสสร้างโปรเซสย่อยจำนวนมากเกินไป) หลังจากสร้างโปรเซสย่อยแล้ว โปรเซสแม่อาจทำงาน (execute) ได้ 2 วิธี คือ– โปรเซสแม่ ทำงานพร้อม ๆ กัน (concurrent) ไปกับโปรเซสลูก โปรเซสแม่คอยจนกระทั่ง โปรเซสลูกทั้งหมด สิ้นสุดการทำงาน ในการระบุพื้นที่ว่างของโปรเซสใหม่ทำได้ 2 วิธี คือ– โปรเซสลูกทำสำเนา (duplicate) มาจากโปรเซสแม่ (จำลองมา)– โปรเซสลูกมีโปรแกรมที่ถูก load เข้ามาเอง (สร้างเอง) ในระบบปฏิบัติการ UNIX แต่ละโปรเซสจะมีเลขประจำตัวเฉพาะ (Process Identifier) เป็นเลขจำนวนเต็ม (Integer) โปรเซสจะสร้างโปรเซสย่อย โดยคำสั่งเรียกระบบ fork โปรเซสใหม่ที่เกิดขึ้น จะมีตำแหน่งโปรแกรมของโปรเซสเดิม ซึ่งช่วยให้โปรเซสแม่(เดิม) สามารถติดต่อสื่อสารกับโปรเซสลูก (ใหม่) ได้สะดวก โปรเซสทั้งสอง (แม่และลูก) จะทำงานจากคำสั่ง (ในโปรแกรมเดียวกัน) จากคำสั่ง fork โดยที่โปรเซสลูกจะได้รหัสส่งคืนจากคำสั่ง forkเป็นศูนย์แต่โปรเซสแม่จะได้รหัสส่งคืนเป็นตัวเลขแสดงหมายเลขของโปรเซสลูก โดยปกติ คำสั่งหลักจากคำสั่ง fork จะเป็นคำสั่งเรียกระบบ execve ทำหน้าที่นำโปรแกรมจากจานบันทึกมาลงในหน่วยความจำทับโปรแกรมเดิมของโปรเซสที่ใช้คำสั่งนี้ แล้วทำงานต่อในโปรแกรมใหม่โปรเซสแม่สามารถสร้างลูกได้เรื่อยๆหรือถ้าไม่มีอะไรทำในขณะลูกกำลังทำงาน มันจะใช้คำสั่งเรียกระบบ wait เพื่อย้ายตัวเองไปที่แถวพร้อม จนกระทั้งลูกเสร็จสิ้นการทำงาน
2.4.2 การเสร็จสิ้นโปรเซส (Process Termination)
โปรเซสจะสิ้นสุดหรือถูกยกเลิก เมื่อทำงานเสร็จในขั้นสุดท้าย แล้วร้องขอให้ระบบปฏิบัติการลบโปรเซสทิ้งไป โดยใช้คำสั่งเรียกระบบ exit ซึ่งโปรเซสสามารถส่งข้อมูล (output) กลับไปให้โปรเซสแม่ได้ โดยคำสั่งเรียกระบบ wait โปรเซสหนึ่งอาจยกเลิกโปรเซสอื่นได้ โดยใช้คำสั่งเรียกระบบ เช่น คำสั่งยกเลิก (abort) การใช้คำสั่งนี้โดยปกติ ต้องเป็นโปรเซสแม่ใช้กับโปรเซสลูกเท่านั้น (เพราะโปรเซสแม่ทราบหมายเลขของโปรเซสลูกจากเมื่อตอนที่สร้างโปรเซสลูก)โปรเซสแม่อาจ
ต้องการยกเลิกโปรเซสลูกตัวหนึ่งเพราะ- โปรเซสลูกใช้ทรัพยากรที่โปรเซสแม่แบ่งให้จนหมดแล้ว ทรัพยากรไม่พอใช้
- โปรเซสแม่ไม่ต้องการใช้โปรเซสลูกตัวนี้ อีกต่อไป
- โปรเซสแม่เสร็จสิ้นและระบบปฏิบัติการไม่ต้องการให้โปรเซสลูกทำงานต่อ
ระบบส่วนใหญ่ เมื่อโปรเซสแม่สิ้นสุด โปรเซสลูกต้องสิ้นสุดด้วย เราเรียกว่า การยกเลิกแบบลูกโซ่ (Cascading Termination) ซึ่งระบบมักจะเป็นผู้จัดการ ตัวอย่าง ในระบบ UNIX โปรเซสลูกจะสิ้นสุดหรือถูกยกเลิก โดยใช้คำสั่งเรียกระบบ exit ซึ่งโปรเซสแม่จะรอเหตุการณ์นี้ โดยใช้คำสั่งเรียกระบบ wait ซึ่งจะให้ค่ากลับคืนมาเป็นหมายเลขของโปรเซสลูกที่ถูกยกเลิกไป ในระบบ UNIX โปรเซสแม่ไม่จำเป็นต้องแบ่งทรัพยากรให้โปรเซสลูกเลย ระบบจะจัดการให้โดยให้ศักดิ์เท่า ๆ กัน ทุกโปรเซสในการใช้ทรัพยากรในระบบ (เช่น หน่วยความจำหลัก , จานบันทึก)
2.5 โปรเซสสื่อประสาน (Cooperating Process)
โปรเซสจะเป็นโปรเซสสื่อประสานถ้าโปรเซสนั้นมีผลกระทบหรือได้รับผลกระทบจากโปรเซสอื่นที่เอ็กซิคิวต์อยู่ในระบบนั้น อาจกล่าวได้ว่าโปรเซสสื่อประสานจะมีการแชร์ข้อมูลกับโปรเซสอื่นในระบบ โดยระบบปฏิบัติการจะต้องแชร์หน่วยความจำไว้ใช้งาน เนื้อที่หน่วยความจำส่วนนั้นคือบัฟเฟอร์
เหตุที่ทำให้เกิดการประสานระหว่างโปรเซส
การแชร์ข้อมูลข่าวสาร : ผู้ใช้หลายคนต้องการข้อมูลเดียวกัน ในเวลานั้น
การเพิ่มความเร็วในการคำนวณ : โดยการแบ่งโปรแกรมออกเป็นส่วนย่อย ๆ แล้วเอ็กซิคิวต์ ขนานกันไป
ความสะดวก : ผู้ใช้ต้องการทำงานหลายอย่างในเวลาเดียวกัน
กลไกที่สนับสนุนให้โปรเซสสามารถประสานกันได้ คือ การติดต่อระหว่างโปรเซส (InterProcess Communication) และการซินโครไนซ์โปรเซส (Synchronize Process)
การเอ็กซิคิวต์โปรเซสที่มีการประสานกับโปรเซสอื่นจำเป็นต้องใช้บัฟเฟอร์ โดยระบบปฏิบัติการจะต้องแชร์หน่วยความจำไว้ใช้งาน และจะต้องมีกลไกที่สนับสนุนให้สามารถประสานได้ กลไกที่ว่าคือการติดต่อระหว่างโปรเซส (InterProcess Communication : IPC) และการซินโครไนซ์โปรเซส (Synchronize) ซึ่งผู้เขียนจะกล่าวรายละเอียดในหัวข้อต่อไป
2.6 การติดต่อระหว่างโปรเซส (InterProcess Communication)
ในหัวข้อที่ผ่านมา คุณคงได้ทราบเกี่ยวกับการประสานโปรเซส ที่สามารถติดต่อในภาวะที่ใช้บัฟเฟอร์หรือหน่วยความจำ และต้องใช้กลไกคือการติดต่อระหว่างโปรเซส และการซินโครไนซ์โปรเซส สำหรับในหัวข้อนี้จะกล่าวถึงการติดต่อระหว่างโปรเซส หรือ IPC (TnterProcess Communication)
2.6.1 โครงสร้างพื้นฐาน
IPC สนับสนุนกลไกเพื่อให้โปรเซสสามารถติดต่อซึ่งกันและกันได้อย่างมีประสิทธิภาพ ที่เห็นได้ชัดที่สุดคือระบบแมสเสจ (Message system) ฟังก์ชันของระบบแมสเสจที่ IPC สนับสนุนมีอย่างน้อย 2 ประการคือ การส่งแมสเสจ (Send message) และรับแมสเสจ (Receive message) เมื่อโปรเซสใดต้องการแมสเสจจากโปรเซสอื่น จะส่งคำร้องขอไปยังโปรเซสนั้น โปรเซสที่ถูกร้องขอจะส่งแมสเสจนั้นไปให้ ขนาดของข้อมูลอาจจะมีขนาดคงที่ หรือขนาดไม่คงที่ก็ได้ ก่อนกรรับ-ส่งแมสเสจจะต้องมีการสร้างลิงค์ให้พร้อมก่อน ทั้งลิงค์ทางกายภาพ (เช่น การแชร์หน่วยความจำ, บัส หรือเน็ตเวิร์คก็ได้) และลิงค์ทางลอจิก ซึ่งลิงค์ทางลอจิกจะมีคุณสมบัติที่น่าสนใจดังนี้
- จะสร้างลิงค์อย่างไร
- ลิงค์ที่สร้างนั้นสัมพันธ์กับโปรเซสมากกว่า 2 โปรเซสหรือไม่
- ระหว่างโปรเซสทั้งสองจะมีกี่ลิงค์
- ความจุของลิงค์เป็นเท่าไร
- ลิงค์นั้นมีบัฟเฟอร์หรือไม่ ถ้ามีขนาดของบัฟเฟอร์เป็นเท่าไร
- ลิงค์เป็นแบบทางเดียว (unidirectional) หรือสองทิศทาง (bidirectional) โดยที่การลิงคแบบทางเดียวจะให้มีการรับส่งแมสเสจได้ทางเดียว คือโปรเซสในส่งก็ส่งอย่างเดียว สามารถรับได้ หรือโปรเซสใดรับก็รับอย่างเดียวจะส่งไม่ได้ ส่วนกรลิงค์แบบสองทิศทางจะช่วยให้โปรเซสนั้นสามารถรับหรือส่งแมสเสจได้ สำหรับการลิงค์หลายโปรเซสจ้ะองมีอย่างน้อย 1 โปรเซสที่ทำหน้าที่รับแมสเสจ
นอกจากนี้ยังมีอีกหลายวิธีที่แสดงถึงการลิงค์แบบลอจิกและโอเปอเรชันในการรับ-ส่งแมสเสจ ดังนี้
- การติดต่อเป็นแบบทางตรง หรือทางอ้อม
- การติดต่อเป็นแบบสมมาตรหรือไม่สมมาตร
- เป็นแบบอัตโนมัติหรือใช้บัฟเฟอร์เฉพาะแบบ
- ส่งแบบสำเนา หรืออ้างอิง
- แมสเสจมีขนาดคงที่ หรือไม่คงที่
2.6.2 วิธีการติดต่อ
การติดต่อระหว่างโปรเซสอาจจะใช้วิธีส่งทางตรง หรือทางอ้อม (direct/indirect) ก็ได้ โดยทั้ง 2 วิธี มีคุณสมบัติและวิธีการที่น่าสนใจดังนี้
2.6.2.1 การติดต่อทางตรง (direct communication)
การติดต่อแบบนี้แต่ละโปรเซสที่ต้องการติดต่อกันจะต้องกำหนดชื่อเฉพาะที่ใช้ในการติดต่อทั้งผู้รับและผู้ส่ง ยกตัวอย่างเช่น ถ้าต้องการส่งแมสเสจจาก A ไป B จะมารกำหนดรูปแบบคือ

send( B, message) จะเป็นการส่งแมสเสจไปยังโปรเซส B
receive( A, message) จะเป็นการรับแมสเสจจากโปรเซส A

ลิงค์แบบนี้จะมีคุณสมบัติดังนี้
- การสร้างลิงค์จะเป็นแบบอัตโนมัติระหว่างคู่ของโปรเซสที่ต้องการติดต่อ (ในที่นี้คือ A กับ B) โปรเซสจะทราบหายเลขโปรเซสที่จะติดต่อด้วย
- ลิงค์หนึ่งๆ จะมีความสัมพันธ์เฉพาะโปรเซสสองโปรเซสเท่านั้น
- ระหว่างโปรเซสแต่ละคู่นั้นจะมีเพียงลิงค์เดียวเท่านั้น
- ลิงค์นี้เป็นได้ทั้งทิศทางเดียว และสองทิศทาง แต่ปกติจะเป็นแบบสองทิศทาง
วิธีการรับ-ส่งแมสเสจแบบนี้ ระบบปฏิบัติการจะไม่ดำเนินการติดต่อให้กับโปรเซสทั้งสอง โปรเซสทั้งสองจะต้องจัดการเอง ตัวอย่างการส่งแมสเสจจากโปรเซส A ไปโปรเซส B หลังจากสร้างลิงค์เรียบร้อยแล้ว ทั้งโปรเซส A และโปรเซส B จะจองหน่วยความจำที่แชร์ไว้แล้ว (โปรเซสทั้งสองจะทราบว่าหน่วยความจำนั้นอยู่ตำแหน่งใด) โปรเซส A จะนำข้อมูลไปวางบนตำแหน่งของหน่วยความจำที่จองไว้ โปรเซส B จะต้องคอยตรวจสอบว่าโปรเซส A วางข้อมูลหรือยัง ถ้ายังไม่วางก็จะยังไม่ดึงข้อมูล รอจนกระทั่งโปรเซส A วางเรียบร้อยแล้วจึงจะดึงข้อมูลนั้นไปใช้งาน ส่วนโปรเซส A ก็จะต้องตรวจสอบเช่นกันว่าข้อมูลที่ตนเองวางนั้น โปรเซส B ดึงไปใช้งานหรือยัง ถ้ายังไม่ดึง โปรเซส A จะต้องรอก่อน ยังไม่มีการวางข้อมูลใหม่ เพื่อป้องกันการวางข้อมูลใหม่ทัยข้อมูลเดิมซึ่งจะมีผลให้การทำงานไม่สมบูรณ์ (ข้อมูลหายไป) รอจนกระทั่งการดึงข้อมูลเรียบร้อยจึงวางข้อมูลใหม่ได้ ทำเช่นนี้เรื่อยไปจนกว่าข้อมูลจะหมด กลไกที่ใช้ในการตรวจสอบเวลาที่เหมาะสมในการรับ-ส่งข้อมูลนี้เรียกว่า “การซินโครไนซ์โปรเซส” (Process Synchronization) ซึ่งผู้เขียนจะกล่าวรายละเอียดในหัวข้อต่อไป
2.6.2.2 การติดต่อทางอ้อม (indirect communication)
การติดต่อแบบนี้โปรเซสทั้งสองที่ต้องการติดต่อกันจะติดต่อกันผ่านทางเมลล์บ็อกซ์ (Mailbox) หรืออาจเรียกว่าเป็นการติดต่อทางพอร์ต (port) เมลล์บ็อกซ์เป็นออปเจ็กต์ที่เก็บแมสเสจจากโปรเซสหนึ่งเพื่อส่งไปอีกโปรเซสหนึ่ง แต่ละเมลล์บ็อกซ์จะมีหมายเลขไม่ซ้ำกับเมลล์บ็อกซ์อื่น วิธีนี้จะทำให้โปรเซสหนึ่งสามารถติดต่อโปรเซสอื่นผ่านทางเมลล์บ็อกซ์หลายเมลล์บ็อกซ์ที่แตกต่างกันได้ และเมลล์บ็อกซ์ที่จะใช้ในการติดต่อกันนี้จะต้องมีการแชร์เมลล์บ็อกซ์ไว้ก่อนด้วย รูปแบบคำสั่งการรับ-ส่งข้อมูลผ่านเมลล์บ็อกซ์เป็นดังนี้
send( B, message) เป็นการส่งแมสเสจไปยังเมลล์บ็อกซ์ B
receive( A, message) เป็นการรับแมสเสจจากเมลล์บ้อกซ์ A

ลิงค์แบบนี้จะมีคุณสมบัติดังนี้
- จะมีการสร้างลิงค์ระหว่างโปรเซสที่มีการแชร์เมลล์บ็อกซ์เท่านั้น
- ลิงค์หนึ่งๆ อาจจะมีความสัมพันธ์มากกว่าสองโปรเซสได้
- ระหว่างโปรเซสแต่ละคู่นั้นอาจจะมีหลายลิงค์ที่แตกต่างกันได้ แต่ละลิงค์จะมีเพียงเมลล์บ็อกซ์เดียว
- การลิงค์อาจจะเป็นทิศทางเดียว หรือสองทิศทางก็ได้
ตอนนี้เพื่อความเข้าใจมากยิ่งขึ้น สมมุติว่าทั้งโปรเซส P1, P2 และ P3 มีการแชร์เมลล์บ็อกซ์ A ไว้ โปรเซส P1 จะส่งเมสเสจไปยัง A ในขณะที่โปรเซส P2 และ P3 ต้องการรับแมสเสจจาก A คำถามก็คือว่า โปรเซสใดจะรับแมสเสจที่ส่งจาก A คำถามนี้อาจจะแก้ได้หลายวิธี ดังนี้
- ยอมให้มีการลิงค์ทั้งสองโปรเซส
- ยอมให้มีการรับแมสเสจได้เพียงครั้งละ 1 โปรเซส
- ยอมให้ระบบเลือกว่าโปรเซสใดที่จะเข้ารับแมสเสจ (ในที่นี้อาจจะเป็น P2 หรือ P3 ก็ได้ แต่ไม่ใช้ครั้งละสองโปรเซสพร้อมกัน) โดยระบบจะกำหนดผู้รับไปยังผู้ส่งก่อนการส่ง
เมลล์บ็อกซ์แบบคิว โครงสร้างของเมลล์บ็อกซ์แบบนี้เป็นโครงสร้างที่ดึงข้อมูลออกจากเมลล์บ็อกซ์ตามลำดับก่อน-หลังของข้อมูลที่ส่งเข้ามา นั่นคือข้อมูลใดส่งเข้ามาในเมลล์บ็อกซ์ก่อนก็จะถูกดึงออกไปก่อน ส่วนข้อมูลใดส่งเข้ามาภายหลังก็จะถูกดึงออกไปภายหลัง อาจเรียกการทำงานแบบนี้ว่า FIFO (First In First Out) ลักษณะโครงสร้างเมลล์บ็อกซ์แบบคิวเป็นดังรูป 2.9

เมลล์บ็อกซ์แบบไปป์ โครงสร้างของเมลล์บ็อกซ์แบบนี้เป็นโครงสร้างที่คล้ายกับโครงสร้างแบบคิว คือ การดึงข้อมูลจะเป็นในลักษณะที่ข้อมูลใดส่งเข้ามาก่อนก็จะถูกดึงออกไปก่อน ข้อมูลใดส่งเข้ามาภายหลังจะถูกดึงออกไปใช้งานภายหลัง แต่ข้อแตกต่างระหว่างเมลล์บ็อกซ์แบบคิวกับเมลล์บ็อกซ์แบบไปป์คือเมลล์บ็อกซ์แบบคิวจะมีขนาดบ็อกซ์คงที่ ถ้าใส่ข้อมูลมากเกินไปเมลล์บ็อกซ์จะเต็ม แต่ถ้าเป็นเมลล์บ็อกซ์แบบไปป์ขนาดของบ็อกซ์จะยืดหยุ่นได้ ทำให้สามารถใส่ข้อมูลในเมลล์บ็อกซ์ได้มากเท่าที่ต้องการ เมลล์บ็อกซ์จะขยายตัวโดยอัตโนมัติ ลักษณะโครงสร้างเมลล์บ็อกซ์แบบไปป์เป็นดังรูป 2.10

เมลล์บ็อกซ์แบบสแต็ก โครงสร้างของเมลล์บ็อกซ์แบบนี้เป็นโครงสร้างตรงข้ามกับเมลล์บ็อกซ์แบบคิวในการดึงข้อมูล นั่นก็คือข้อมูลใดส่งเข้ามาเมลล์บ็อกซ์ก่อนจะถูกดึงออกไปใช้งานภายหลัง โดยจะนำข้อมูลที่ส่งเข้ามาภายหลังออกไปใช้ก่อน อาจเรียกการทำงานแบบนี้ว่า FILO (First In Last Out) ลักษณะโครงสร้างเมลล์บ็อกซ์แบบสแต็กเป็นดังรูป 2.11

2.6.3 การจัดบัฟเฟอร์
ในการสร้างลิงค์ นอกจากจะเป็นการกำหนดเส้นทางข้อมูลแล้ว ลิงค์ยังมีความจุที่เป็นตัวเลขแสดงจำนวนแมสเสจที่สามารถเก็บไว้ชั่วคราวได้ คุณสมบัตินี้อาจจะมองว่าเป็นคิวของแมสเสจที่ผูกติดลิงค์ก็ได้ โดยพื้นฐานจะมีความจุ 3 ลักษณะ คือ
- ความจุศูนย์ (Zero capacity) ความจุแบบนี้จำให้คิวมีขนาดสูงสุดเป็น 0 ดังนั้นลิงค์จะไม่มีแมสเสจรอยู่เลย ในกรณีนี้ผู้ส่งจะต้องรอจนกว่าผู้รับแมสเสจ โปรเซสทั้งสองจะต้องซินโครไนซ์เพื่อให้เกิดการถ่ายโอนแมสเสจ “rendezvous”
- ความจุแบบมีขอบเขต (Boundd capacity) ความจุแบบี้จะทำให้คิวมีขนาดคงที่เป็น n ดังนั้นจะมีแมสเสจได้สูงสุด n แสเสจที่เก็บในคิวได้ถ้าคิวยังไม่เต็มเมื่อแมสเสจใหม่ถูกส่งเข้ามา หลังจากนั้นก็เข้าไปอยู่ในคิว (แมสเสจนั้นอาจจะถูกก็อปปี้ไว้หรือตำแหน่งพอยเตอร์ของแมสเสจไว้ก็ได้) ในขณะที่ผู้ส่งสามารถเอ็กซิคิวต์ได้ต่อไปโดยไม่ต้องรอ เนื่องจากเป็นความจุที่คงที่ ดังนั้นเมื่อคิวเต็ม ผู้ส่งจะต้องรอจนกว่าจะมีที่ว่างในคิว
- ความจุไม่มีขอบเขต (Unbounded capacity) ความจุแบบนี้ทำให้คิวมีขนาดใหญ่ไม่คงที่ทำให้แมสเสจถูกเก็บไว้ได้เสมอ ผู้ส่งไม่ต้องรอเวลาเลย
อาจกล่าวได้ว่าความจุศูนย์เป็นระบบที่ไม่มีบัฟเฟอร์ก็ได้ ส่วนกรณีอื่นมีการจัดบัฟเฟอร์ให้อัตโนมัติ สำหรับในกรณีที่ไม่ใช่ศูนย์โปรเซสจะไม่รู้เลยว่าแมสเสจถึงปลายทางหรือไม่หลังจากการส่งเสร็จสิ้นไปแล้ว ถ้าข้อมูลนี้มีความสำคัญในการคำนวณ ผู้ส่งจะต้องติดต่อกับผู้รับเพื่อหาแมสเสจที่ส่งมาครั้งหลังสุด ตัวอย่างเช่น ถ้าโปรเซส P ส่งแมสเสจไปยังโปรเซส Q และสามารถเอ็กซิคิวต์ได้ต่อไปเฉพาะหลังจากที่แมสเสจได้รับไปแล้ว โปรเซส P จะมีขั้นตอนดังนี้


send( Q, message);
receive( Q, message);

โปรเซส Q จะเอ็กซิคิวต์คำสั่ง
receive ( P, message);
send ( P, “acknowledgment”);

การรับ-ส่งแมสเสจระหว่างโปรเซส P และโปรเซส Q ในลักษณะนี้เรียกว่า “asynchronous” อย่างไรก็ตามยังมี 2-3 กรณีที่ไม่เข้ากลุ่มใดตามที่กล่าวมาแล้วนี้
- การส่งแมสเสจของโปรเซสได้โดนไม่ต้องคอย กรณีนี้ถ้าผู้รับยังได้รับแมสเสจก่อนที่ผู้ส่งจะส่งแมสเสจอื่น แมสเสจแรกจะหายไป ข้อได้เปรียบของกรณีนี้ก็คือแมสเสจขนาดใหญ่ไม่จำเป็นต้องก็อปปี้ไว้หลายครั้ง ส่วนข้อเสียเปรียบคือการเขียนโปรแกรมของงานจะมีความยุ่งยาก โปรเซสเหล่านี้จำเป็นต้องมีการซินโครไนซ์แบบพิเศษเพื่อให้มั่นใจว่าแมสเสจไม่สูญหายไปไหน ทั้งผู้รับและผู้ส่งไม่ต้องคำนวรบัฟเฟอร์ของแมสเสจ
- การส่งแมสเสจของโปรเซสจะล่าช้าออกไปจนกว่าโปรเซสจะได้รับคำตอบ วีการี้นำมาใช้ในระบบปฏิบัติการที่ชื่อว่า Thoth โดยในระบบนี้แมสเสจจะมีขนาดแน่นอน (8 word) โปรเสซ P ที่แมสเสจจะถูกบล็อกจนกว่า โปรเซสี่ทำหน้าที่รับแมสเสจแล้วส่งคำตอบกับมา 8 word ในรูปแบบคำสั่ง reply(P,message) แมสเสจที่ตอบมานี้จะเขียนทับบัฟเฟอร์ดั้งเดิมของแมสเสจ ข้อแตกต่างระหว่าง send คือ คำสั่ง send จะทำให้โปรเซสที่ส่งแมสเสจเกิดการบล็อก ในขณะที่ reply จะยอมให้ทั้งโปรเซสที่ส่งแมสเสจและโปรเซสที่รับแมสเสจเอ็กซิติวต์ต่อไปได้เลยโดยไม่มีการบล็อก
2.6.4 เงื่อนไขยกเว้น
ระบบแมสเสจมีประโยชน์มนสภาพแวดล้อมแบบกระจายซึ่งโปรเซสอาจจะอยู่บนเครื่องอื่น ในสภาพ
แวดล้อมที่กล่าวถึงความน่าจะเป็นที่จะเกิดข้อผิดพลาดระหว่างการติดต่อสื่อสารและโปรเซสจะมากกว่าในสภาพ
แวดล้อมที่เป็นเครื่องเดียว สำหรับสภาพแวดล้อมที่เป็นเครื่องเดียว แมสเสจจะอยู่ในหน่วยความจำที่แชร์ ถ้าเกิดข้อผิดพลาดขึ้น ระบบโดยรวมจะล่ม แต่สำหรับในระบบกระจายแมสเสจจะถ่ายโอนไปตามสาย การเกิดข้อผิดพลาดที่เครื่องใดเครื่องหนึ่งไม่จำเป็นที่ระบบโดยรวมจะล่มก็ได้ ทั้งระบบที่เป็นศูนย์กลางคือระบบกระจายข้อผิดพลาดอาจจะได้รับการแก้ไข ตอนนี้เรามาพิจารณาเงื่อนไขยกเว้นที่ระบบต้องดูแลแมสเสจ
2.6.4.1 การสิ้นสุดของโปรเซส
ถ้าผู้รับหรือผู้ส่งแมสเสจสิ้นสุดก่อนแมสเสจจะเอกซิคิวต์ ในสภาวะแบบนี้ทำให้แมสเสจจะถูกกำจัด ทำให้ผู้รับไม่ได้รับแมสเสจ หรือผู้ส่งไม่ได้ส่งแมสเสจ ลองพิจารรา 2 กรณี ดังนี้
1. ผู้รับโปรเซส P อาจจะรอแมสเสจจากโปรเซส Q ที่สิ้นสุดไปแล้ว ถ้าไม่มีแอ็กชันใดๆ เกิดขึ้นโปรเซส P จะถูกบล็อกตลอดไป ในกรณีนี้ระบบอาจจะสั่งให้ P สิ้นสุด หรืออาจจะแจ้งให้ R ทราบว่า Q สิ้นสุดไปแล้วก็ได้
2. โปรเซส P อาจจะส่งแมสเสจไปยังโปรเซส Q ที่สิ้นสุดไปแล้ว รูปแบบการจัดบัฟเฟอร์แบบอัตโนมัติจะไม่เกิดอันตรายใดๆ โดย P ยังคงเอ็กซิคิวต์ต่อไป ถ้าโปรเซส P ต้องการทราบว่าแมสเสจของตนนั้นโปรเซส Q เอ็กซิคิวต์หรือไม่ จะต้องมีการโปรแกรมพิเศษสำหรับการแจ้งให้ทราบ แต่ในกรณีที่ไม่มีบัฟเฟอร์ โปรเซส P จะถูกบล็อกตลอดไปเช่นเดียวกับข้อ 1 ระบบอาจจะสั่งให้ P สิ้นสุด หรืออาจจะแจ้งให้ P ทายว่า Q สิ้นสุดไปแล้วก็ได้
2.6.4.2 การสูญหายของแมสเสจ
แมสเสจจากโปรเซส P ที่ส่งไปยังโปรเซส Q อาจจะสูญหายระหว่างทางของการสื่อสารก็ได้ ซึ่งอาจจะเป็นข้อผิดพลาดด้านฮาร์ดแวร์หรือสายสื่อสาร มี 3 วิธีพื้นฐานในการจัดการเหตุการณ์นี้
1. ระบบปฏิบัติการมีหน้าที่รับผิดชอบในการตรวจสอบการสูญหายนี้เพื่อส่งแมสเสจไปใหม่
2. โปรเซสที่ทำหน้าที่ส่งแมสเสจมีหน้าที่รับผิดชอบในการตรวจสอบการสูญหายเพื่อส่งแมสเสจใหม่ถ้าต้องการอีก
3. ระบบปฏิบัติการมีหน้าที่รับผิดชอบในการตรวจสอบการสูญหายนี้ หลังจากนั้นจะแจ้งให้โปรเซสที่ทำหน้าที่ส่งแมสเสจที่เกิดการสูญหายเพื่อให้ส่งแมสเสจไปใหม่

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

2.7 การซินโครไนซ์โปรเซส (Process Synchronization)
โดยปกติโปรเซสส่วนใหญ่ในระบจะไม่มีความเกี่ยวข้องกัน ต่างคนต่างเอ้กซิคิวต์ ลักษณะความเป็นอิสระนี้เรียกว่า “อะวิงโครนัส” (Asynchronous) แต่มีบางดปรเซสีมีความสัมพันธ์กัน นอกจากการติดต่อสื่อสารระหว่างโปรเซส (InterProcess Communication) ที่กล่าวไปแล้ว ยังมีการเข้าจังหวะของโปรเซสอีกด้วย การเข้าจังหวะของโปรเซสหรืออาจจะเรียกว่าโปรเซสนั้นเกิดการซินโครไนซ์กัน (Synchronize) การซินโครไนซ์โปรเซสจะหมายถึงการทำงานของโปรเซส 2 โปรเซสที่ต้องการมีความเกี่ยวข้องกัน อาจจะเป็นเพราะใช้รีซอร์สร่วมกัน หรืออาจจะเป็นการรอการเอ็กซิคิวต์โปรเซสหลังจากที่โปรเซสอื่นเอ็กซิคิวต์ไปแล้ว เป็นต้น ทำให้ต้องมีการรอจังหวะที่เหมาะสมเพื่อให้การทำงานนั้นถูกต้อง เพื่อความเข้าใจยิ่งขึ้นลองพิจารณารูป 2.12 จะเห็นว่าโปรเซส B จะเริ่มเอ็กซิคิวต์ได้ก็ต่อเมื่อโปรเซส A เอ็ดซิคิวต์เสร็จเรียบร้อยไปแล้ว (ซึ่งต่างกับโปรเซส C ที่ไม่ต้องรอโปรเซสใดเลยก้สามารถเอ็ก
ซิคิวต์ได้ทันที และไม่มีความเกี่ยวข้องกับโปรเซส A และ B เลย) ในทำนองเดียวกัน โปรเซส D จะเริ่มเอ็กซิคิวต์ได้ก็ต่อเมื่อมีการเอ็กซิคิวต์โปรเซส C และ C เสร็จเรียบร้อยไปแล้วนั่นเอง
ในการใช้รัซอร์สร่วมกันของดปรเซส 2 โปรเซสขึ้นไป อาจก่อให้เกิดปัญหาในการทำงานที่ถูกต้องสมมุติดปรเซสทั้งสองเป็นอิสระต่อกัน ทำงานในเวลาพร้อมๆ กัน และใช้ข้อมูลเดียวกันคือตัวแปร X ซึ่งกำหนดค่าเริ่มต้นไว้ที่ 10 โดยที่ โปรเซส A จะเป็นการเพิ่มค่าเข้าไปอีก 10 (X = X+10) ส่วนโปรเซส B จะเป็นการลดค่าจากเดิมออกไป 10(X = X-10)


จากรูป 2.13 โปรเซส A ทำงานก่อนโปรเซส B โดยอ่านค่า X เข้ามาซึ่งมีค่าเป็น 10 แล้วเพิ่มเข้าไปอีก 10 ทำให้ X มีค่าเป็น 20 แล้วเขียนลงหน่วยความจำ หลังจากนั้นโปรเซส B จึงอ่านค่า X เข้ามาซึ่งมีค่าเป็น 20 แล้วลบค่าออกไป 10 ทำให้ X มีค่าเป็น X ลงหน่วยความจำ

จากรูป 2.14 การทำงานจะคล้ายกับรูป 2.13 และได้ผลลัพธ์เหมือนกัน แต่โปรเซส B ทำงานก่อนโปรเซส A โดยอ่านค่า X เข้ามาซึ่งมีค่าเป็น 10 แล้วลบค่าออกไป 10 ทำให้ X มีค่าเป็น 0 แล้วเขียนลงหน่วยความจำ หลังจากนั้นโปรเซส A จึงอ่านค่า X เข้ามาซึ่งมีค่าเป็น 0 แล้วเพิ่มค่าเข้าไป 10 ทำให้ X มีค่าเป็น 10 แล้วเขียนค่า X ลงหน่วยความจำ สำหรับรูป 2.15 ทั้งโปรเซส A และ B จะทำงานพร้อมๆ กัน โดยค่าเริ่มต้น X เป็น 10 ทั้งสองโปรเซสจะได้ผลลัพธ์ที่ต่างกัน โดยโปรเซส A จะได้ค่า X เป็น 20 (จาก 10 เพิ่มอีก 10) ส่วนโปรเซส B จะได้ค่า X เป็น 0 (จาก 10 ลบออกไป 10) ถ้าโปรเซส A ทำงานเสร็จก่อนจะเขียนค่า X ซึ่งเท่ากับ 20 ลงหน่วยความจำ แต่ถ้าโปรเซส B ทำงานเสร็จก่อนจะเขียนค่า X ซึ่งเท่ากับ 0 ลงหน่วยความจำ ซึ่งทำให้เกิดข้อผิดพลาดเนื่องจากค่า X แน่นอน ขึ้นอยู่กับว่าโปรเซสใดทำงานเสร็จก่อน

2.8 โครงสร้างพื้นฐานการซินโครไนซ์
โปรเซสที่ทำงานร่วมกับโปรเซสอื่นในการเอ็กซิคิวต์จำเป็นต้องอาศัยการซินโครไนซ์ที่เหมาะสม เพื่อให้การทำงานถูกต้องตามที่กล่าวมาแล้วนั้น ในการซินโครซ์โปรเซสไม่ใช่เรื่องที่ทำได้ง่าย มีโครงสร้างและสิ่งที่อาจจะเกิดขึ้นในการซินโครไนซ์ที่น่าสนใจดังนี้

2.8.1 Race Condition
ในบางระบบปฏิบัติการ โปรเซสที่ทำงานร่วมกันอาจจะมีการแชร์รีซอร์ส เช่น หน่วยความจำ หรือสื่อจัดเก็บข้อมูล หรือแชร์ไฟล์ซึ่งโปรเซสทั้งสองสามารถอ่านหรือเขียนรีซอร์สเหล่านี้ได้พร้อมกัน ทำให้ผลลัพธ์อาจจะเกิดการผิดพลาดขึ้นได้ขึ้นอยู่ว่าโปรเซสใดเข้ามาใช้ก่อน สภาวะที่เกิดขึ้นนี้เรียกว่า “Race Condition” เช่นเดียวกับการทำงานของโปรเซส A และโปรเซส B ในรูป 2.15 ที่ผ่านมา

2.8.2 Mutual Exclusion และ Critical Region
แล้วเราจะแก้สภาวะ Race Condition ได้อย่างไร คำตอบก็คือเราจะต้องหยุดไม่ได้เกิดการเอ็กซิคิวต์โปรเซสที่สอง ในขณะที่เอ็กซิคิวต์โปรเซสแรกอยู่ ถ้ายังมีโปรเซสแรกครอบครองรีซอร์สที่แชรืนั้นอยู่ โดยจะต้องรอจนกว่าโปรเซสแรกทำงานเสร็จเรียบร้อยก่อนเพื่อป้องกันไม่ให้เกิดข้อผิดพลาด วิธีการป้องกันไม่ให้โปรเซสอื่นเข้าไปใช้รีซอร์สในขณะที่โปรเซสใดๆ ครอบครองรีซอร์สอยู่นั้นเรียกว่า “Mutual Exclusion” หรืออาจจะเรียกว่า “การกีดกัน” โดยในบริเวณหรือส่วนของโปรแกรมที่โปรเซสเข้าไปครอบครองรีซอร์สนี้เรียกว่า Critical region หรือ Critical Section อาจกล่าวได้ว่าในสภาวะ Mutual Exclusion ถ้ามีโปรเซสใดก็ตามอยู่ใน Critical Region แล้ว โปรเซสอื่นจะเข้าไปอยู่ใน Critical Region ไม่ได้ ต้องรอให้โปรเซสนั้นออกมาจาก Critical Region ก่อน คุณสมบัติ 4 ประการของ Mutual Exclusion มีดังนี้
1. จะต้องไม่มีโปรเซส 2 โปรเซสอยู่ใน Critical Region พร้อมกัน
2. จะต้องไม่มีสมมุติฐานและข้อจำกัดเกี่ยวกับความเร็ว และจำนวนซีพียูมาเกี่ยวข้อง
3. จะต้องไม่มีโปรเซสใดๆ ภายนอก Critical Region ที่บล็อกการทำงานของโปรเซสอื่น
4. จะต้องไม่มีโปรเซสใดที่รอการเข้า Critical Region ตลอดเวลา

พิจารณา 2.16 เมื่อโปรเซส A เข้า Critical Region ที่เวลา T1 หลังจากนั้นเวลาผ่านไปเล็กน้อย ที่เวลา T2 โปรเซส B พยายามที่จะเข้า Critical Region แต่เข้าไม่ได้เนื่องจากมีโปรเซส A อยู่ใน Critical Region อยู่แล้ว (เราอนุญาตให้มีเพียงโปรเซสเดียวที่สามารถอยู่ใน Critical Region ได้) ทำให้โปรเซส B ถูกบล็อกไม่สามารถเข้า Critical Region ได้ จนกระทั่งที่เวลา T3 เมื่อโปรเซส A ออกจาก Critical Region แล้ว โปรเซส B จะเข้า Critical Region ได้ทันที จนกระทั่งที่เวลา T4 เมื่อโปรเซส B ออกจาก Critical Region แล้วก็จะกลับสู่สภาวะปกติที่ไม่มีโปรเซสใดอยู่ใน Critical Region เลย

2.8.3 Mutual Exclusion with busy waiting
เนื่องจากในสภาวะ Mutual Exclusion จะยอมให้มีการครอบครองรีซอร์สได้ครั้งละ 1 โปรเซสเท่านั้น ทำให้โปรเซสอื่นต้องรออยู่ภายนอก Critical Region แต่การรอของโปรเซสก็ยังเป็นการครอบครองเวลาซีพียูอยู่ ทำให้เสียประโยชน์เนื่องจากไม่ได้งานอะไรเลย การรอของโปรเซสในลักษณะนี้เรียกว่า Busy waiting เราจะต้องทำอะไรสักอย่างเพื่อขัดจังหวะการทำงานของซีพียู หรือกำหนดการทำงานของซีพียูเพื่อแก้ปัญหา busy waiting สำหรับารขัดจังหวะหรือที่เรียกว่า “อินเทอร์รัพต์” (Interrupt) นี้แบ่งเป็น 2 ประเภทคือ
- Clock Interrupt เป็นอินเทอร์รัพต์ที่บอกซีพียูว่าอยู่ในสภานะบล็อกรันครบเวลาควันตัม (Quantum time) แล้ว
- I/O Interrupt เป็นอินเทอร์รัพต์ที่บอกซีพียูว่าโปรเซสที่อยู่ในสภานะบล็อกนั้นทำงานกับอุปกรณ์อินพุต/เอาต์พุตเสร็จเรียบร้อยแล้ว และเมื่อซีพียูได้รับอินเทอร์รัพต์นี้แล้ว ซีพียูจะหยุดทำงานให้กับโปรเซสที่กำลังรันนั้นก่อน แล้วมาจัดการกับโปรเซสในสภานะบล็อกที่ส่งอินเทอร์รัพท์ไปเพื่อให้โปรเซสนั้นเสร็จงานและจะได้เปลี่ยนสภานะเป็นสภานะพร้อมได้
การแก้ปัญหา Mutual Exclusion with busy waiting มี 5 วิธีดังนี้
- Disable Interrupt
- Lock Variable
- Strict Alternation
- Peterson’s Slution
- TSL Instruction

2.8.3.1 Disable Interrupt
วิธีการนี้เป็นการยกเลิกอินเทอร์รัพต์ ทำให้ซีพียูทำงานได้อย่างต่อเนื่องโดยไม่สนใจอินเทอร์รัพต์ใดๆ เลย ทำให้โปรเซสที่อยู่ใน Critical Region ได้เอ็กซิคิวต์จนเสร็จเรียบร้อยจะได้ไม่เกิด Race Condition ขึ้นมาได้แต่หลังจากออกจาก Critical Region ไปแล้ว ซีพียูจึงจะสนใจอินเทอร์รัพต์ที่ส่งเข้ามา วิธีการยกเลิกอินเทอร์รัพต์ก็คือจะมีคำสั่ง Disable Interrupt ไว้ก่อนเข้า Critical Region ในทุกๆ โปรเซสที่มี Critical Region และจะต้องใช้คำสั่ง Able Interrupt ไว้ตอนท้ายสุดก่อนออกจาก Critical Region ดังรูป 2.17



รูป 2.17 Disable Interrupt ของโปรเซส A และโปรเซส B

ข้อเสียของวิธี Disable Interrupt มี 3 ประการคือ
- ถ้าใช้คำสั่ง Disable Interrupt ไว้ตอนต้นแต่ลืมใช้คำสั่ง Able Interrupt ไว้ท้ายสุดในโปรเซสของผู้ใช้ (User Process) หรือแอปพลิเคชันเพราะจะทำให้โปรเซสนั้นรันรวดเดียวจนจบ
- ไม่นิยมใช้ใน User Mode (Application Program) แต่นิยมใช้ใน Kernel Mode (System Program)
- คำสั่ง Disable Interrupt จะมีผลกับระบบที่ใช้ซีพียูเพียงตัวเดียว ในกรณีที่ระบบเป็นแบบหลายซีพียูจะใช้ไม่ได้ผล

นอกจากMonitor จะช่วยจัดการรีซอร์สที่จะต้องจะช่วยร่วมกันแล้ว Monitor ยังจะช่วยจัดการโปรเซสที่จะเข้าCritical regionเพื่อเข้าใช้ข้อมูลโดยผู้ใช้ทราบแต่เพียงว่ามีข้อมูลเหล่านั้นอยู่และทราบเพียงว่าจะใช้ข้อมูลนั้นในลักษณะใดบ้างเท่านั้น ไม่จำเป็นต้องทราบวิธีการเข้าไปใช้ข้อมูล ประโยชน์ของ Monitor ที่เห็นใด้ชัดเจนอีกอย่างหนึ่งคืการลดโอกาศที่จะเกิดข้อผิดพลาดจากโปรแกรมเมอร์ในส่ว่นที่เกี่ยวข้องกับ Mutural Exclusion เช่นเกิด Deadlock เป็นต้นทั้งนี้เนี่องจากส่วนที่เกี่ยวข้องดังกล่าวตัวแปลภาษา และคอมไพเลอร์จะสร้างให้โดยอัตโนมัติ ด้านล่างนี้เป็นโค๊ดเพี่อจินตนาการเกี่ยวกับMonitor


2.9 ปัญหาการทำงานของโปรเซส
ปัญหาที่เกิดขึ้นกับระบบปฎิบัติการเป็นปัญหาที่น่าสนใจและถกเถียงกันอย่างกว้างขวาง ตลอดจนมีการวิเคราะห์โดยการใช้วิธีการด้านซนโครไนซ์ ในที่นี้จะนำมากล่าว 3 ปัญหาดังนี้
2.9.1 The Dining Philosophers Problem
ปัยหาแรกนี้ไดจก์สตรา (ปี 1965) ได้เสนอไว้ โดยสมมุตว่ามีนักปราชญ์ 5 ท่านนั่งรอบโต๊ะกลมเพื่อทานอาหารที่วางตรงหน้า ในการทานอาหาร นักปราชญ์แต่ละท่านต้องใช้ตะเกียบทั้ง 2 ข้างที่วางที่วางอยู่ซ้ายมือและขวามือของนักปราชญ์ท่านนั้น เมื่อทานเสร็จในรอบนั้นจะต้องวางตะเกียบลงเพื่อเปิดโอกาสให้นักปราชญ์ท่านอื่นใด้ทานอาหารบ้าง จะเกิด Deadlock เมื่อนักปราชญ์ทั้ง 5 ท่านหยิบตะเกียบคนละ 1 ข้างพร้อมกัน สมมุตว่าทุกท่านหยิบตะเกียบข้างซ้ายมือพร้อมกัน ทุกท่านจะรอตะเกียบทางขวาว่างจึงจะทานอาหารใด้ และถ้ามีการสลับกันทานอาหารโดยนักปราชญืที่โชคร้ายถูกนักปราชญ์ซ้าย – ขวาสลับกันทานอาหาร ทำให้นักปราชญืท่านกลางม่มีโอกาสทานอาหาร เรียกปรากฎการเช่นนี้ว่า “ Starvation” ตัวอย่างโค๊ดข้างล่างนี้จะไม่ทำให้เกิด Deadlock และ
Starvation เพื่อป้งกันให้นักปราชญ์ทั้ง 5 ท่านเรียก thank ซึ่งเป็น binary Semaphorre ก่อนเริ่มหยิบ
ตะเกียบนักปราชญ์จะใช้ปฎิบัติการ Down ของ Mutex หลังจากเปลี่ยนตะเกียบจะใช้ปฎิบัตการ Up ของMutex ตามทฤษฎีน่าจะเพียงพอ แต่มีข้อจำกัดตรงที่จะมีนักปราชญ์เพียงท่านเดียวที่ทานอาหารในเวลาหนึ่งๆ เราจะไม่อณุญาตให้นักปราชญ์ 2 ท่านทานอาหารพร้อมกัน



รูป 2.18 โต๊ะอาหารการแก้ปัญหา Dinner Philosophers Problem ( Mos:p125)
#Define N 5 /* จำนวนนักปราชญ*/
Void philosopher(int i) /* Iแทนจำนวนนักปราชญ์ จาก0ถึง4*/

{
while ( TURE ) {
think(); /* นักปราชญ์คิด*/
take_chop(i); /* หยิบตะเกียบซ้าย */
take_chop (i+1)%N) /*หยิบตะเกียบขวา ; % คือโอเปอเรเตอร์
Modulo*/
eat( ); /* ทานอาหาร */
put_chop(i) /* วางตะเกียบซ้ายลงบนโต๊ะ */

put_chop(i+1)%N) ; /* วางตะเกียบซ้ายลงบนโต๊ะ */
ตัวอย่างข้างล่างนี้เป็นการแก้ปัญหาทำให้นักปราชญ์ทานอาหารใด้มากกว่า 1คน(สูงสุดเท่าที่จะยอมใด้ เช่นถ้ามีนักปราชญ์5คนก็จะทานใด้ 2 คนพร้อมกันใด้ จะใช้อาร์เรย์ที่ชื่อ state ติดตามสถานะว่านักปราชญ์ท่านใดกำลังกิน กำลังคิด หรือหิว (พยายามหยิบตะเกียบ) นักปราชญ์ 1 ท่าน อาจจะย้ายไปสถานะกำลังกินถ้าคนข้างใดกำลังกินข้างไม่ใช่สถานะกำลังกิน หมายเลขนักปราชญ์ข้างเคียงถูกกำหนดด้วยมาโครLIFEและRIGHTอาจกล่าวใด้ว่า ถ้าi คือ 2 แล้ว LIFTคือ1 และRIGHTคือ 3 ในโค้ดจะใช้อาเรย์ของ Semaphore 1 อาเรย์ต่อ 1 นักปราชญ์ ดังนั้นนักปราชญ์ที่หิวจะบล๊อกถ้าตะเกียบที่ต้องการใช้งานอยู่ จำใว้ว่าแต่ละโปรเซสรันโปรซีเดอร์ Philosopher
เป็นโค๊ดหลัก แต่โปรซีเดอร์อื่นทั้ง Take_chops,put_chopscและ test เป็นโปรดีเซอร์ธรรมดาและไม่ใด้แยกจากโปรเซส

#define N 5 /* จำนวนนักปราชญ์*/
#define LIFE (i+n-1)%N /* นักปราชญ์ที่อยู่ด้านซ้าย*/
#define RIGHT (i+n-1)%N /* นักปราชญ์ที่อยู่ด้านขวา*/
#define THINKING /* นักปราชญ์ที่กำลังคิด*/
#define HUNGRY /* นักปราชญ์ที่กำลังหยิบตะเกียบ*/
#define EATING 2 /* นักปราชญ์ที่กำลังกิน*/
Typedef int semaphore; /* Semaphore เป็นชนิดพิเศษของ int*/
int state[N]; /* อาร์เรย์เก็บสถานะของนักปราชญ์แต่ละคน*/
Semaphore mutex =1; /* Mutual ExclusionสำหรับCritical region*/
Semphore s[n]; /* 1 Semaphore ต่อ1 นักปราชญ์*/

Void philosopher(int i) /* i คือหมายเลขนักปราชญ์ จาก 0 ถึง N-1*/
{

while(true){ /*วนรอบ*/
think(); /* นักปราชญ์กำลังคิด*/
take_chops(i); /* ต้องการตะเกียบ 2 ข้างหรือบล๊อกใว้ก่อน*/
eat(); /* ทานอาหาร*/
put_chops(i); /* วางตะเกียบ 2 ข้างลงนโต๊ะ*/

}
}
void take_chops(int i) /* หมายเลขนักปราชญ์จาก 0 ถึง -1*/


{

Down( &mutex ) ; /* เข้า Critical region*/
state[i] = HUNGRY; /* บันทึกว่านักปราชญ์หิว*/
test(i); /* พยายามหยิบตะเกียบ*/
up(&mutex) /* ออกจาก critical region*/
down(&s[i]); /* บล๊กไว้ก่อนถ้าใด้ตะเกียบทั้ง 2 ข้าง */
}

Void put_chops(i) /* i คือหมายเลขนักปราชญ์ จาก 0 ถึง N-1*/
{

down( &mutex ); /* เข้า Critical region*/
statel[i]=thinking; /* นักปราชญ์ทานเสร็จแล้ว*/
test(LEFT); /* พิจรณาดูถ้าคนซ้ายทานใด้ตอนนี้*/
test(RIGHT) /* พิจรณาดูถ้าคนขวาทานใด้ตอนนี้*/
up(&mutex); /* ออกจาก Critical region*/
}

void test(i) /* หมายเลขนักปราชญ์จาก 0 ถึงN-1*/
{
if(state[I]==HUNGRY && state[LEFT]!=EATING && state[RIGHT]!=EATING){
state[I] = EATING;
up(&s[I];
}

}
2.9.2 The Readers – Writers Problem
ปัญหาการทานอาหารของนักปราชญ์มีประโยชน์มากสำหรับการเป็นโมเดลของโปรเซสที่ช่วยแก้ปัญหาการใช้รีซอร์สที่แชร์กันอยู่อย่างจำกัด ยังมีปัญหาอีกอย่างคือปัญหา Reader-Writer Problem (เสนอโดย Courtosisปี 1971) เป็นปัญหาเกี่ยวข้องกับระบบคอมพิวเตอร์ที่มีกลุ่มข้อมูลที่ใช้งานร่วมกัน ซึ่งมีโปรเซสอยู่ 2 ประเภท คือโปรเซสที่อ่านข้อมูลร่วม และโปรเซสที่บันทึกหรือเขียนข้อมูล ตัวอย่างเช่นระบบสำรองที่นั่งโดยสารเครื่องบิน ที่อาจจะมีเทอร์มินัลหลายพันหลายหมื่นตัวเพื่อสำรองที่นั่งในฐานข้อมูลเดียวกัน จะเห็นว่าในระบบมีผู้อ่าน (Reader)เป็นจำนวนมากเพื่ออ่านข้อมูล ในขณะเดียวกันก็อาจจะมีผู้เขียน (writer)ได้หลายคนเช่นกันเพื่อเขียนหรือบันทึกข้อมูล หลักการหรือปัญหานี้ก็คือมีผู้อ่านหลายคน (หลายโปรเซส) สามารถเข้าอ่านข้อมูลร่วมกันได้ไม่จำกัดจำนวน แต่สำหรับโปรเซสการเขียนข้อมูลไม่อาจใช้งานร่วมกันโปรเซสอื่นได้ไม่ว่าจะเป็นการอ่าน หรือเขียนข้อมูลการใช้ Monitor หรือการซินโครไนซ์อื่นๆ ไม่สามารถจัดการ Mutual Exclusion ได้ เนื่องจากต้องคำนึงถึงการรอคอยอีกด้วย เพราะผู้อ่านสามารถอ่านข้อมูลได้ตลอดเวลา ซึ่งถ้าผู้อ่านเข้ามาเรื่อยๆ ผู้เขียนก็ไม่สามารถเขียนข้อมูลได้เลยต้องรอไปเรื่อยๆจนกว่าผู้จะไม่มีผู้อ่านเข้ามา ในทำนองตรงข้าม ถ้าให้สิทธิของผู้เขียนมีมากกว่าผู้อ่าน ผู้อ่านก็จะถูกกีดกันไม่ให้ใช้ข้อมูลได้
การแก้ปัญหานี้(ตามโค้ดด้านล่าง)ผู้อ่านคนแรกที่เข้าทาใช้ฐานข้อมูลจะทำปฎิบัตการ Down ในSemaphore db ในขณะเดียวกันจะเพิ่มค่าตัวนับ arc ด้วย ในขณะที่ออกจากฐานข้อมูลจะลดค่าตัวนับ และคนสุดที่ออกไปจะทำปฎิบัติการ up ของ Semphore นอกจากนี้ยังบล็อกไม่ให้ผู้เขียนเข้ามาอีกด้วย แต่ถ้ามีผู้อ่านอยู่ก่อนแล้ว มีผู้อ่านคนต่อไปเข้ามาใหม่ก็สามารถใช้ฐานข้อมูลได้ รวมถึงผู้อ่านรายต่อๆไปด้วย สำหรับผู้เขียน
ถ้าเข้ามาต้องรอต่อไปจนกว่าจะไม่มีผู้อ่านเข้ามา สมมุติผู้อ่านจะเข้ามาทุก 3 วินาที และใช้เวลา 5 วินาทีในการอ่าน
ข้อมูล จากตัวเลขทั้งสองจะทำให้ผู้เขียนไม่สามารถเขียนข้อมูลได้เลย การแก้ปัญหาจะต้องกำหนดเพิ่มเติมเล็กน้อย
กล่าวคือถ้ามีผู้เขียนอยู่ก่อนแล้ว มีผู้อ่านเข้ามาใหม่ ผู้อ่านที่เข้ามาใหม่นั้นจะต้อไปรอคิวต่อจากผู้เขียน ถือหลัก
ที่ว่าใครมาหลังจะต้องต่อคิวเรื่อยไป จะทำให้แก้ปัญหาทั้ง 2 ทาง (ไม่ให้ผู้อ่านหรือผู้เขียนรอนานเกินไป)
Typeset into semaphore;
Semaphore mute = 1 /* ควบคุมการใช้ arc*/
Semaphore db = 1 /* ควบคุมการใช้ฐานข้อมูล*/
Int rc = 0; /* จำนวนโปรเซสที่อ่านหรือเขียน*/


Void reader (void)
{

white ( TURE ) { /*วนรอบ*/
down(&mutex); /* รับสิทธิ์การใช้ rc*/
rc = rc + 1; /* มีผู้เขียนเพิ่มขึ้นอีก 1 คน*/
if(rc = = 1)down (&db); /* ตรวจสอบว่าเป็นผู้อ่านคนแรกหรือไม่*/
up(&mutex); /* ปล่อยสิทธิ์การใช้ rc*/
read_data_base(); /* อ่านข้อมูล rc*/
down(&mutex); /* รับสิทธิ์การใช้ rc*/
rc = rc + 1; /* มีผู้เขียนลด 1 คน*/
if(rc = = o)up (&db); /* ตรวจสอบว่าเป็นผู้อ่านคนสุดท้ายหรือไม่*/
up(&mutex); /* ปล่อยสิทธิ์การใช้ rc*/
read_data_read(); /* ใช้ข้อมูล(นอกเหนือ Critical region*/
}
}


void writer (void)
{
white ( TURE ) { /*วนรอบ*/
thank_up_data(); /* นอกเหนือ Critical region*/
down(&db) /* รับสิทธ์การใช้ฐานข้อมูล*/
white_data_base(); /* เขียนข้อมูล rc*/
down(&mutex); /* รับสิทธิ์การใช้ rc*/
up(&db); /* ปล่อยสิทธิ์การใช้ฐานข้อมูล*/
}

}
2.9.3 The Sleeping Barber Problem
นอกจากปัญหาที่กล่าวมาแล้ว ทั้ง 2 ปัญหา ปัญหาที่ 3 เป็นปัญหาที่เกิดในร้านตัดผมที่มีช่างตัดผม 1 คนเก้าอี้ตัดผม 1 ตัว และมีเก้าอี้นั่งรอคิวจำนวนหนึ่ง ถ้าไม่มีลูกค้า ช่างตัดผมจะนอนพักผ่อนบนเก้าอี้ตัดผม เมื่อลูกค้าเข้ามาในร้านจะปลุกช่างตัดผมมาทำการตัดผม ถ้ามีลูกค้าเดินมาเข้าร้านเพิ่มอีก ก็จะนั่งรอ (ในกรณีที่มีเก้าอี้ว่าง) หรือไม่ก็ออกจากร้านไป (ในกรณีที่ไม่มีเก้าอี้ให้นั่งรอ) ปัญหาก็คือการ เขียนโปรแกรมเพื่อให้ช่างตัดผมและลูกค้าไม่เกิดการ Race Condition ปัญหานี้คล้ายกับการรอคิวเข้ารับบริการของโปรแกรมย่อยหรือโปรซีเดอร์ต่างๆที่เรียกเข้ามา
การแก้ปัญหาจะใช้ 3 Semaphone ดังนี้

เรายังต้องการตัวแปร waiting ที่ใช้นับจำนวนลูกค้าที่รอคิวตัดผม ซึ่งคือสำเนาของ Customer ที่เราจำเป็นต้องมีตัวแปร waiting ก็เนื่องจากเราไม่มีทางทราบจำนวน Semaphore ปจุบันเลย การแก้ปัญหานี้ก็คือลูกค้าที่เข้ามาในร้านจะนับจำนวนลูกค้าที่รอคิว ถ้าจำนวนน้อยกว่าจำนวนเก้าอี้ที่รอ ลูกค้าก็จะอยู่ในร้าน นอกนั้นจะออกจากร้านตัวอย่างโค๊ดด้านล่างนี้ แสดงถึงตอนเช้าช่างตัดผมเข้ามาในร้าน ก็เริ่มเอ็กซิคิวโปรซีเดอร์ barbersทำให้เขาถูกบล๊อกกับ Semaphore customer เนื่องจากค่าเริ่มต้นเป็น 0 (เนื่องจากยังไม่มีลูกค้า) ช่างตัดผมก็นอนรอลูกค้าจนกว่าจะมีลูกค้าคนแรกเข้ามา เมื่อลูกค้าเข้ามา ก็จะมีเอ็กซีคิวต์ customer เริ่มต้องการ Mutex เพื่อเข้าสู่ criticalregion ถ้ามีลูกค้าคนที่ 2 เข้ามาอีก คนนี้จะทำอะไรไม่ใด้จนกว่าลูกค้าคนแรกจะปล่อย Mutex โปรซีเดอร์ customer จะตรวจสอบจำนวนลูกค้าที่รอคิวว่ามีจำนวนน้อยกว่าเก้าอี้ที่ให้รอหรือไม่ ถ้าไม่ (ลูกค้าเกินจำนวนเก้าอี้)ก็จะปล่อยMutex แล้วก็ออกจากร้านไป แค่ถ้ามีเก้าอี้เหลือ โปรซีเดอร์ customer จะเพิ่มค่าตัวแปร waiting หลังจากนั้นจะทำ ปฎิบัติการ Up กับ Semaphore ดังนั้นจะปลุกให้ช่างตัดผมตื่น ตรงนี้เป็นจุดที่ช่างตัดผมและลูกค้าที่ตื่นอยู่ เมื่อลูกค้าปล่อย Mutex เริ่มตัดผม จนเมื่อการตัดผมเสร็จสิ้นลูกค้าออกจากโปรซีเดอร์ และออกจากร้าน จะเห็นว่าโค้ดจะต่างจากตัวอย่างที่ผ่านมาที่ไม่มีการรวนรอบ เนื่องจากลูกค้า 1 คนจะตัดผมเพียงครั้งเดียว แต่ช่างตัดผมจะวนรอบเพื่อให้บริการกับลูกค้าคนต่อไป (ถ้ามีลูกค้า) แต่ถ้าไม่มีก็จะนอนรอต่อไป
#define CHAIR 5 /*จำนวนเก้าอี้ให้ลูกค้ารอคิว*/
typedef int semaphore
semaphore customers = o; /* จำนวนลูกค้าที่คอยคิวบริการ*/
semaphore barbers = o; /* จำนวนช่างตัดผมที่รอลูกค้า*/
semaphore mutex = o; /* สำหรับMutual Exclusion*/
int waiting = 0 /* ลูกค้าที่คอยคิว (ยังไม่ใด้ตัด) */
void barber(viod
{

white ( TURE ) {

down(&customers); /* นอนรอ ถ้าจำนวนลูกค้าเป็น o*/
down(& mutex); /* รับสิทธ์การใช้waiting*/
waiting=waiting - 1 /*ลดค่าตัวนับจำนวนลูกค้าที่คอยคิว1ค่า*/
up( &barbers ); /* ช่าง 1 คนพร้อมที่จะตัดผมแล้ว*/
up( &mutex ); /* ปล่อยสิทธิ์waiting*/
cut_hair(); /* ตัดผม(นอก Critical region*/
}

{
Voild customer( voild )

{
down (& mutex ); /*เข้า Critical region*/
2.10 Thread
โปรเซสที่กล่าวผ่านมาเป็นการเอ็กซิคิวต์โปรแกรมในลักษณะมีการควบคุมเพียง 1 Thread (แต่ละโปรเซสจะประกอบด้วย Thread เพียง Thread เดียวเท่านั้น) แต่ในระบบการปฎิบัติการสมัยใหม่การสมัยใหม่ในแต่ละโปรเซสสามาถมีได้หลายThread อาจจะกล่าวใด้ว่า Thread ก็คือส่วนประกอบย่อยของโปรเซสนั่นเอง จนบางครั้งอาจเรียก Thread ว่า “LightWeight Process” ในรูป 2.19 (ก) คุณจะเห็นโปรเซส 3 โปรเซส แต่ละโปรเซสจะมีแอ็ดเรสเป็นของตนเอง และควบคุมเพียง 1 เท่านั้น แต่ละรูป 2.19 (ข) จะเห็นว่าในแต่ละโปรเซสจะควบคุม3 Thread โดยใช้แอ็ดแรสเดียวกันอยู่



รูป2.19 ( ก ) 3 โปรเซสแต่ละโปรเซสจะมี 1 Thread ( ข ) 1 โปรเซสที่มี 3 Thread [MOS:P82]
Thread เป็นหน่วยพื้นฐานของการจัดสรรการใช้ประโยชน์ของของซีพียู ที่ประกอบด้วย
ภายในโปรเซสจะประกอบด้วย Thread จะมีการแชร์โค้ด,ข้อมูล และรีซอร์ส เช่น ไฟล์, อุปกรณ์ต่างๆเป็นต้น โปรเซสดั้งเดิม (ที่เรียกว่า heavyweight ) ที่มีการควบคุมเพียง 1 Thread แสดงว่าทำงานใด้ 1 งาน แต่ถ้าโปรเซสมีหลาย Thread ( อาจเรียกว่า multithread ) จะทำงานใด้หลายงานในเวลาเดียวกัน รูป 2.20 แสดงส่วนประกอบของโปรเซส ที่เป็น single-thread และ multithread

รูป2.20 โปรเซสที่เป็น single-threaded และ Multithread [OSC6:P130]
ซอฟแวร์ปจุบันที่รันกับเครื่องพีซีสมัยใหม่มีการออกแบบให้เป็น multithread โดยแยกออกเป็น
โปรเซสที่ควบคุมหลายๆ thread เช่น โปรแกรมเวบบราวเซอร์ที่มี thread หนึ่งในการแสดงรูปภาพหรือเขียนข้อความใขขณะที่อีก thread หนึ่งกำลังดึงข้อมูลจากเน้ตเวิร์ค หรืออย่างในโปรแกรมเวิร์ดโปรเซสเซอร์ที่มีหลาย thread โดยที่thread หนึ่งกำลังแสดงภาพกราฟฟิก ในขณะที่ thread ที่สองกำลังรอรับคำสั่งจากคีย์บอร์ดจากผู้ใช้ ในขณะที่ threadที่สามกำลังตรวจสอบคำสะกดและไวยกรณ์ในลักษณะแบ็คกราวนด์ เป็นต้น
2.10.1 ข้อใด้เปรียบของ multithreaded
การที่ระบบปฎิบัติการสนับสนุนระบบ multithread ทำให้มีข้อใด้เปรียบ 4 กลุ่มหลัก ๆ ดังนี้
1.การตอบสนอง : ระบบ multithread ที่โต้ตอบแอปพลิเคชันจะยินยอมให้โปรแกรมยังคงดำเนินต่อไปถึงแม้ว่าจะมีบางส่วนถูกบล๊อกหรือมีการปฎิบัติที่ยาวนานเนื่องจากการเพิ่มการ โต้ตอบกับผู้ใช้นั่นเอง ยกตัวอย่างเช่น โปรแกรมเว็บบราวเซอร์ยังคงโต้ตอบกับผู้ใช้ได้ ในขณะที่มีหนึ่งที่กำลังโหลดรูปภาพอยู่
2.การแชร์รีซอร์ส : โดยปกติ thread จะแชร์หน่วยความจำ และรีซอร์สของโปรเซสอยู่แล้ว ข้อได้เปรียบ ของการแชร์โค้ดจะทำให้แอปพลิเคชันสามารถมีกิจกรรมของ thread ใด้หลายๆกิจกรรมภายในแอ็ดเดรสเดียวกัน
3.ความประหยัด : การจัดสรรหน่วยความจำและรีซอร์สสำหรับการสร้างโปรเซสมีค่าใช้จ่ายมาก ในทางตรงข้าม เนื่องจาก thread แชร์รีซอร์สของโปรเซสทีมันอาศัยอยู่แล้ว ทำให้เกิดการประหยัดในการสร้างthreadและทำการ context switch ของ threadเป็นการยากที่จะวัดความแตกต่างระหว่างการสร้างและคงสภาพโปรเซสที่มีมากกว่าthreadได้ แต่โดยปกติแล้วจะใช้เวลาในการสร้างและคงสภาพโปรเซสสูงกว่าเวลาที่ใช้กับ thread ใน Solaris2 การสร้างโปรเซสจะช้ากว่าการสร้าง thread 30 เท่าและcontext switch จะช้ากว่า 5 เท่า
4.การเอื้อประโยชน์ของสถาปัตยกรรมมัลติโปรเซสเซอร์ : ข้อใด้เปรียบของ multithread ช่วยเสริมสถาปัตกรรมมัลติโปรเซสเซอร์ให้สูงขึ้น ในขณะที่แต่ละ thread สามารถรันขนานกันไปใน
โปรเซสเซอร์อื่นใด้ในโปรเซสที่มี thread เดียวสามารถรันเพียงซีพียูเดียวเท่านั้น ไม่ว่าจะมีกี่ซีพียูก็ตามระบบ multithread ในเครื่องที่มีหลายซีพียูจะเพิ่มประสิทธิภาพในการทำงานพร้อม ๆ กันใด้มากขึ้น ในสถาปัตยกรรมที่มีซีพียูเดียว ซีพียูจะย้ายแต่ละ thread ให้เร็วมากขึ้นเพื่อให้ทำงานเสมือนว่าขนานกันอยู่ แต่ในความเป็นจริงจะรันเพียง thread เดียวเท่านั้นในเวลานั้น ๆ
2.10.2 User และ Kernel Thread
Thread อาจจะแบ่งตามการระดับการสนับสนุนใด้ 2 แบบคือ User Thread และ Kernel Thread ซึ่งเป็นแบบที่น่าสนใจ ดังนี้
• User Thread จะใด้รับการสนับสนุนจาก kernel ด้านบน และอยู่ในไลบรารีของ thread ในระดับของ ผู้ใช้ ไลบรารียังสนับสนุนการสร้าง thread การจัดเวลา และการจัดการ thread โดยไม่ต้องใด้รับการ สนับสนุนจาก kernel เนื่องจาก kernel ไม่ต้องยุ่งเกี่ยวกับ thread ระดับผู้ใช้ การสร้าง thread และ การจัดเวลา thread ทั้งหมดจะกระทำเสร็จสิ้นภายในพื้นที่ของผู้ใช้โดยไม่จำเป็นต้องใช้ kernel ดังนั้น thread ในระดับผู้นำผู้ใช้สามารถสร้างและจัดการใด้อย่างรวดเร็ว อย่างไรก็ตาม ถ้า kernel เป็น single-thread แล้ว thread ระดับผู้ใช้จะบล๊อก system call จนเป็นเหตุให้ทุกโปรเซสถูกบล๊อก ถึงแม้ว่า thread อื่น จะยังคงรันอยู่ในแอปพลิเคชั่นก็ตาม ไลบรารีของ User thread รวมถึง POSIX Pthreads, Mach C-threads และSolaris 2UI-threads
• Kernel thread ที่ได้รับการสนับสนุนโดยตรงจากระบบปฎิบัติการ kernel จะสร้าง จัดเวลา และการจัดการthread ภายในพื้นที่ของ kernel เอง เนื่องจากระบบปฎิบัติการเป็นผู้จัดการเกี่ยวกับการสร้าง และ จัดการ thread เอง จึงทำให้ kernel thread จะสร้างและจัดการใด้ช้ากว่า User thread อย่างไรก็ตาม เพราะ kernel จัดการเกี่ยวกับ thread ดังนั้นถ้า thread เกิดการบล๊อก system call จะทำให้ kernel จัดการนำเอา thread อื่นในแอปพลิเคชั่นเข้ามาเอ็กซิคิวต์แทนใด้ เช่นเดียวกับในสภาวะมัลติโปรเซสเซอร์ที่ kernel สามารถจัด thread ลงในโปรเซสเซอร์อื่นใด้ ระบบปฎิบัติการที่สนับสนุน kernel thread เช่น Window NT,Window XP, Window 2000, Solaris 2,BeOS,และTru64 UNIX


2.10.3 โมเดลของ Multithreading
มีหลายระบบที่สนับสนุนทั้ง User Thread และ Kernel Thread ในโทมเดลที่ต่างกัน เรามาพิจรณาในโมเดล ทั้งสามของ Threading ดังนี้

• โมเดล Many-to-One เป็นโมเดลที่ใช้ Kernel thread 1 หน่วย กับ User thread หลายหน่วย (ดังรูป 2.21) การจัดการ thread จะอยู่ในพื้นที่ของผู้ใช้ ซึ่งมีประสิทธิภาพ แต่ถ้า thread บล๊อก system call โปรเซสทั้งหมดจะถูกบล๊อกไปด้วย เนื่องจากจะมีเพียง thread เดียวเท่านั้นที่เอ็กเซส kernel ในเวลาหนึ่งๆ thread หลาย ๆ thread ไม่สามารถรันขนานกันในระบบมัลติโปรเซสเซอร์ร์ใด้ ระบบที่ใช้โมเดลนี้เช่น Green thread ซึ่งเป็นไลบรารีใน Solaris 2 นอกจากนี้ในไลบรารีของ User thread ในระบบ ปฎิบัติการที่ไม่สนับสนุน Kernel thread จะใช้โมเดล Many-to-One นี้



โมเดล One-to-One เป็นโมเดลที่แต่ละ User thread จะจับคู่กับ Kernel thread ในลักษณะ 1 ต่อ 1
(ดังรูป 2.22) ทำให้สามารถทำงานทำงานพร้อมกันดีกว่าแบบ Many-to-One โดยยอมให้ thread อื่นรันใด้เมื่อ thread บล๊อก system call นอกจากนี้โมเดลนี้ยังยอมให้รันหลาย ๆ thread แบบขนานกันในระบบมัลติดปรเวสเซอร์ใด้อีกด้วย มีข้อที่คำนึงอยู่ข้อเดียวคือ การสร้าง User thread จำเป็นต้องสร้าง Kernel thread ที่สัมพันธ์กันด้วย เนื่องจากการสร้าง Kernel thread เป็นส่วนสำคัญในประสิทธิภาพของแอปพลิเคชัน ระบบที่โมเดลมีข้อจำกัดที่จำนวน thread ที่สนับสนุนในระบบใด้ โมเดลนี้นำมาใช้ในนระบบ เช่น Window NT,Window XP, Window 2000,และ os/2



• โมเดล Many-to-Many เป็นโมเดลที่อาจจะมีจำนวน User thread มากกว่าหรือเท่ากับจำนวน Kernel thread ก็เป็นใด้ (ดังรูป2.33) จำนวน kernel thread อาจจะเป็นตัวกำหนดแอปพลิเคชันเฉพาะ หรือเครื่อง เฉพาะ (แอปพลิเคชันอาจจะมี kernel thread บนมัลติโปรเซสเซอร์มากกว่า kernel thread บนโปรเซสเซอร์เดียว) ในขณะที่โมเดล Many-to-Many ยอมให้ผู้พัฒนาสร้าง User thread ใด้ตามที่เขาต้องการ แต่จะไม่สามารถรันใด้พร้อมกัน เนื่องจาก kernel จะจัดเวลาให้ครั้งละ thread เท่านั้น โมเดลOne-to-One ยอมให้รันพร้อมกันใด้ดีกว่า แต่ผู้พัฒนาต้องระวังว่าต้องไม่สร้าง thread มากเกินไปใน แอปพลิเคชัน ( ในบางครั้งอาจจะจำกัดจำนวน thread ที่จะสร้าง) โมเดล Many-to-Many จะลดข้อจำกัดของโมเดลทั้งสอง กล่าวคือ ผู้พัฒนาสามารถสร้าง User thread เท่าที่จำเป็น และสัมพันธ์กับ Kernel thread ที่สามารถรันแบบขนานในระบบมัลติโปรเซสเซอร์ นอกจากนั้น เมื่อ thread เกิดการบล็อก system call แล้ว kernel จะจัดเวลาเพื่อนำ thread อื่นขึ้นมารันก่อนก็ใด้ โมเดลนี้เป้นตัวอย่างของ
ระบบ Solaris 2,IRIX,HP-UXและ Tru64 UNIX เป็นต้น
2.10.4 การยกเลิก thread
การยกเลิก thread เป็นการทำให้ thread จบการทำงานก่อนที่จะสมบูรณ์ เช่น ถ้ามี หลาย thread
ค้นหาข้อมูลในฐานข้อมูล พร้อมกัน แล้วมี thread หนึ่งให้ผลลัพท์ออกมาแล้ว thread ที่เหลือจะถูกยกเลิกในภาวะอื่นอาจจะเกิดเมื่อผู้ใช้กดปุ่มบนโปรแกรมเว็บบราวเซอร์เพื่อหยุดการดหลดข้อมูล เนื่องจากการโหลดข้อมูลจะใช้ thread แยกกับการกดปุ่มบนคีย์บอร์ด ดังนั้นเมื่อมีการกดปุ่ม Stop บนคีย์บอร์ดจึงทำให้โปรเกรมเว็บบราวเซอร์หยุดการโหลดข้อมูล นั่นเอง thread ที่ถูกยกเลิกอาจเรียกว่า target thread ซึ่งการยกเลิกอาจมี 2 รูปแบบที่ต่างกัน ดังนี้ความยุ่งยากในการยกเลิกจะเกิดในภาวะที่รีซอร์สถูกกำหนดให้ยกเลิก thread หรือมี thread หนึ่งถูกยกเลิกในขณะอยุ่ระหว่างการอัปเดทข้อมูลที่แชร์อยู่กับ thread อื่น สิ่งนี้เป็นกรณีพิเศษของการยกเลิกแบบ Asynchronous ระบบปฎิบัติการมักจะแก้ปัญหารีซอร์สจากการยกเลิก thread แต่ไม่ใช่ทุกรีซอร์ส ดังนั้นการยกเลิกแบบ Asynchronous อาจจะใช้กับรีซอร์สทั่วไปไม่ได้ ในทางกลับกัน การยกเลิกแบบ Deferred จะทำงานโดยมี thread หนึ่งที่กำหนดว่า target thread ใดจะถูกยกเลิก อย่างไรก็ตาม การยกเลิกนี้จะเกิดขึ้นเฉพาะเมื่อ target thread นั้นตรวจสอบว่าตัวเองจะถูกยกเลิกหรือไม่ สิงนี้ยอมให้ thread ตรวจสอบ เพื่อให้ถูกยกเลิกในจุดที่ปลอดภัยถ้ายกเลิก จุดที่ว่านี้ใน Pthread API เรียกว่าCancellation Points ในระบบปบัติการส่วนมากจะยอมให้โปรเซสหรือ thread ถูกยกเลิกแบบ Asynchronous แต่ใน Pthread API ยังสนับสนุนการยกเลิกแบบ Defferred อีกด้วย สิ่งนี้หมายวความว่าระบบปฎิบัติการที่มี PthreadAPI จะยอมให้ยกเลิกแบบ Defferred นั่นเอง
2.11 สรุป
การจัดการโปรเซสของระบบปฎิบัติการเป็นเรื่องที่สำคัญที่สุด เนื่องจากโปรเซสก็คือโปรแกรมที่กำลังเอ็กซิคิวต์ หรือก็คืองานที่ส่งเข้ามาให้ ซีพียูจัดเวลาเพื่อเข้าทำงานนั่นเอง ส่วนประกอบของโปรเซสจะมีหมายเลขโปรเซส,โค๊ดโปรแกรม,ข้อมูล,บล๊อกควบคุมโปรเซส,ตัวควบคุมลำดับการเอ็กซิคิวต์คำสั่งขอโปรเซส และคุณสมบัติของโปรเซส ในโปรเซสนั้นจะมีสถานะแสดงการทำงานของโปรเซสไว้ ซึ่งจะมีสถานะเริ่มต้น,พร้อมทำงานลรันลรอลบล๊อก และสถานะสิ้นสุด โปรเซสจะควบคุมการสร่งโปรเซสใหม่ผ่านทาง System Call ของระบบปฎิบัติการการทำงานของโปรเวสแม่และโปรเซสลูกจะสัมพันธ์กัน นอกจากนี้โปรเซสยังต้องทำงานร่วมกับโปรเซสอื่น ดังนั้นโปรเซสอาจจะมีผลกรทบ หรือใด้รับผลกระทบจากโปรเซสอื่น ก็ได้ การควบคุมให้โปรเซสสองโปรเซสทำงานร่วมกันใด้ทีเรียกว่า การซินโครไนซ์โปรเซสนั้น เป็นเรื่องที่ยุ่งยาก และเป็นหน้าที่ของระบบปฎิบัติการที่จะต้องจัดการ เช่น การแก้ปัญหาการแก้ปัญหาจากการที่โปรเซสเข้าไปใช้รีซอร์สใดพร้อมกันที่เรียกว่า Race Condition ระบบการปฎิบัติการจะต้องป้องกันเข้า Critical Region ให้ดีนั่นเอง.


(waiting<>

ไม่มีความคิดเห็น: