05 · ระบบปฏิบัติการ
บทนี้คือชั้นที่วางอยู่เหนือตัวเครื่องขึ้นมาโดยตรง — ที่ซึ่งโปรเซสเซอร์ตัวเดียวถูกทำให้ทำตัวเหมือนมีอยู่เต็มห้อง และที่ซึ่งเครื่องเลิกเป็นสมบัติของโปรแกรมใดโปรแกรมหนึ่ง
เครื่องเดียวแสร้งทำเป็นหลายเครื่องอย่างปลอดภัยได้อย่างไร
ระหว่างเดือนมิถุนายน 1985 ถึงมกราคม 1987 ผู้ป่วยหกคนที่รักษาด้วยเครื่องฉายรังสีชื่อ Therac-25 ได้รับรังสีเกินขนาดอย่างรุนแรง อย่างน้อยสามคนเสียชีวิต ตัวเครื่องไม่ได้เสียในแบบที่วิศวกรมองเห็นได้ เครื่องรุ่นก่อนหน้ามีกลไกล็อกฮาร์ดแวร์ (hardware interlock) — อุปกรณ์ทางกายภาพที่ทำให้การตั้งค่าอันตรายเกิดขึ้นไม่ได้ — และใน Therac-25 อุปกรณ์เหล่านั้นถูกถอดออก เพราะจะให้ซอฟต์แวร์ตรวจสอบแทน Nancy Leveson และ Clark Turner พบว่าซอฟต์แวร์ตรวจสอบจริง แต่งาน (task) ที่ทำงานพร้อมกันสองงานใช้ตัวแปรร่วมกันโดยไม่มีการซิงโครไนซ์ ผู้ปฏิบัติงานที่พิมพ์เร็วจึงเปลี่ยนโหมดการรักษาได้ หลังการตรวจสอบอ่านค่าไปแล้ว แต่ก่อนฮาร์ดแวร์เคลื่อนที่เสร็จ (Leveson & Turner, IEEE Computer, July 1993) ทั้งสองยังบันทึกไว้ด้วยว่าเครื่องรันอยู่บนอะไร — "ระบบนี้ไม่ได้สร้างขึ้นด้วยระบบปฏิบัติการหรือ executive มาตรฐาน แต่ real-time executive ถูกเขียนขึ้นมาเฉพาะสำหรับ Therac-25" — โดยโปรแกรมเมอร์คนเดียว เขียนด้วยภาษาแอสเซมบลี ตลอดเวลาหลายปี
ประโยคสุดท้ายนั้นคือเหตุผลที่บทนี้มีอยู่ ระบบปฏิบัติการ (operating system) ไม่ใช่ชั้นอำนวยความสะดวก มันคือคำตอบสะสมของปัญหาสามข้อที่อันตรายถึงตายหากต้องแก้ใหม่จากศูนย์ — เครื่องเดียวถูกแบ่งกันใช้อย่างไร สิ่งที่ทำงานพร้อมกันคงความสอดคล้องไว้อย่างไร และอะไรรอดจากการถูกปิดเครื่อง Remzi และ Andrea Arpaci-Dusseau เรียกสามข้อนี้ว่า การทำเสมือน (virtualization) การทำงานพร้อมกัน (concurrency) และความคงอยู่ของข้อมูล (persistence) (Operating Systems: Three Easy Pieces) ไอเดียที่ร้อยทั้งหมดเข้าด้วยกันคือ ระบบปฏิบัติการเป็นเครื่องที่โกหกอย่างระมัดระวัง บริการทุกอย่างที่มันเสนอคือความเท็จเกี่ยวกับชั้นข้างล่าง รักษาไว้ดีพอจะพึ่งพาได้
คำโกหกแรกคือเรื่องเลขคณิต แล็ปท็อปของคุณมีโปรเซสเซอร์ไม่กี่ตัว แต่รันโปรแกรมอยู่นับร้อย แต่ละตัวเขียนขึ้นราวกับมีเครื่องทั้งเครื่องเป็นของตัวเอง ระบบปฏิบัติการทำ multiplex — รันโปรแกรมหนึ่งไปสองสามมิลลิวินาที บันทึกค่าในทุกรีจิสเตอร์ โหลดค่าของอีกโปรแกรมเข้ามา และปล่อยให้ตัวจับเวลาฮาร์ดแวร์ขัดจังหวะมัน เพื่อไม่ให้โปรแกรมไหนปฏิเสธที่จะคืนเครื่องได้ คำโกหกที่ใหญ่กว่าคือเรื่องหน่วยความจำ เครื่อง Atlas ที่ Manchester ซึ่งรันในปี 1962 เปิดตัว "ระบบเก็บข้อมูลชั้นเดียว" (one-level storage system) — โปรแกรมอ้างที่อยู่ในพื้นที่เก็บข้อมูลที่ใหญ่กว่าหน่วยความจำจริงมาก และฮาร์ดแวร์ย้าย page ระหว่าง core กับ drum อย่างเงียบๆ (Kilburn, Edwards, Lanigan & Sumner, 1962) ทุกโพรเซส (process) นับแต่นั้นได้รับเรื่องแต่งเดียวกัน — หน่วยความจำเสมือน (virtual memory) อย่างที่ลูกหลานทุกตัวเรียกกัน คือพื้นที่ที่อยู่ส่วนตัว เริ่มจากศูนย์ ดูเหมือนเป็นของมันเองทั้งหมด โปรแกรมสองตัวถือ "address 4096" เดียวกันได้โดยไม่มีวันเจอกัน
คำโกหกปลอดภัยก็ต่อเมื่อมันถูกตรวจสอบจากข้างในไม่ได้ ตรงนี้เองระบบปฏิบัติการต้องพึ่งชั้นข้างล่าง เคอร์เนล (kernel) ไม่ได้มีสิทธิพิเศษเพราะฉลาดกว่าโปรแกรมของคุณ มันมีสิทธิพิเศษเพราะโปรเซสเซอร์ปฏิเสธคำสั่งเครื่องบางตัว เว้นแต่มีการตั้ง mode bit ไว้ Multics นิยามวงแหวนการป้องกัน (ring) แบบนี้ไว้แปดชั้น x86 รับไอเดียนี้มาด้วยสี่ชั้น และระบบกระแสหลักใช้จริงสองชั้น — เคอร์เนลกับผู้ใช้ (Multics protection) instruction set ของบทที่ 04 ไม่รู้ว่าโพรเซส (process) คืออะไร มันรู้แค่ว่าคำสั่งเครื่องบางตัวถูกห้ามในโหมดนอก และการพยายามรันมันทำให้เกิด trap ทุกเรื่องเกี่ยวกับ isolation ความปลอดภัย และ multi-tenancy สร้างขึ้นจากท่าทีเดียวของฮาร์ดแวร์ท่านี้
ปัญหาที่สอง การทำงานพร้อมกัน (concurrency) คือสิ่งที่ฆ่าผู้ป่วย Therac พอสองสิ่งทำงานพร้อมกันและแตะข้อมูลก้อนเดียวกัน ความถูกต้องเลิกเป็นคุณสมบัติของฝ่ายใดฝ่ายหนึ่งลำพัง Edsger Dijkstra มอบเครื่องมือพื้นฐานให้วงการนี้ในปี 1965 คือ semaphore ในบันทึกชื่อ "Cooperating Sequential Processes" และใช้มันสร้างระบบ THE ที่แบ่งเป็นชั้น (CACM, May 1968) แต่ mutual exclusion แลกความล้มเหลวแบบหนึ่งไปกับอีกแบบหนึ่ง โพรเซสที่ถือล็อก (lock) ตัวหนึ่งไว้ระหว่างรอล็อกตัวที่สอง อาจรอไปตลอดกาล Coffman, Elphick และ Shoshani ตั้งชื่อเงื่อนไขสี่ข้อที่ต้องเป็นจริงพร้อมกันทั้งหมดจึงจะเกิด deadlock — mutual exclusion, hold-and-wait, no preemption และ circular wait — ใน ACM Computing Surveys ปี 1971 (System Deadlocks) ตัดข้อใดข้อหนึ่งออก deadlock ก็เป็นไปไม่ได้ นั่นยังคือวิธีจัดการกับมันจนถึงทุกวันนี้ สังเกตว่าระบบปฏิบัติการเสนออะไรให้ ไม่ใช่การกำจัดความยาก แต่เป็น primitive ที่ถูกต้องพอจะต่อยอดได้ การอ่าน Therac-25 อย่างตรงไปตรงมาคือ วิศวกรที่ปฏิเสธเครื่องมือเหล่านี้ กำลังนั่งคิดค้นปี 1965 ขึ้นมาใหม่คนเดียว ภายใต้ deadline
คำโกหกที่สามคือ ที่เก็บข้อมูลจดจำได้ มันจำได้จริง — แต่ไฟถูกตัดได้ระหว่างคำสั่งเครื่องสองตัวใดๆ รวมถึงสองตัวที่จะทิ้งดิสก์ไว้ในสถานะ (state) ที่สมเหตุสมผล คำตอบของระบบปฏิบัติการคือ fsync ซึ่งจะ return ก็ต่อเมื่อข้อมูลไปถึงอุปกรณ์แล้ว และระบบไฟล์ (file system) ที่จด journal ความตั้งใจของตัวเองไว้ เพื่อให้ operation ที่ค้างอยู่ครึ่งเดียวถูกเล่นซ้ำหรือย้อนกลับได้ นามธรรม (abstraction) ตัวนี้รั่วอย่างหนัก — fsync บนไฟล์ใหม่ไม่ได้ทำให้ directory entry ของไฟล์นั้นคงทนถาวรไปด้วย คุณต้อง sync ไดเรกทอรีแม่ด้วย เมื่อ Pillai และเพื่อนร่วมงานตรวจสอบว่าแอปพลิเคชันที่ใช้กันแพร่หลาย 11 ตัวมีพฤติกรรมอย่างไรเมื่อเครื่อง crash พวกเขาพบช่องโหว่ตอน crash ที่ต่างกัน 60 จุด (All File Systems Are Not Created Equal, OSDI 2014) โปรแกรมพวกนี้ไม่ใช่ของมือสมัครเล่น ผิว (surface) บอกว่า "บันทึกแล้ว" มีแต่ชั้นข้างล่างเท่านั้นที่รู้ว่าจริงหรือเปล่า
กฎการออกแบบข้อเดียวยึดทุกอย่างไว้ด้วยกัน คือแยก กลไก (mechanism) ออกจาก นโยบาย (policy) เคอร์เนลจัดหากลไกให้ทำสิ่งหนึ่ง และปล่อยการตัดสินใจว่าจะทำอะไรให้เป็นเรื่องของสิ่งที่อยู่เหนือมันขึ้นไป Hydra ระบุเรื่องนี้เป็นหลักการออกแบบตั้งแต่ปี 1974 (Wulf et al., CACM 17, no. 6) และ "Hints for Computer System Design" ของ Butler Lampson ทำให้มันเป็นสามัญสำนึกภายในปี 1983 เหตุผลนั้นง่าย — นโยบายเปลี่ยนเร็วกว่ากลไกมาก และเคอร์เนลที่ฝังความชอบเรื่อง scheduling หรือแบบจำลองความปลอดภัยไว้ตายตัว ต้องถูกเขียนใหม่ทุกครั้งที่ความเห็นขยับ อินเทอร์เฟซที่ได้ออกมาแคบเมื่อเทียบกับสิ่งที่มันแบกไว้ — ตาราง system call ของ x86-64 ใน Linux source ปัจจุบันมี 385 รายการ (kernel source, checked 1 August 2026) ทุกอย่างที่คุณเคยรันผ่านประตูบานนั้นมาแล้ว
คนในเรื่องนี้สืบทอดกันมาโดยตรง Fernando Corbató สาธิต Compatible Time-Sharing System ที่ MIT ในปี 1961 และนำทีม Multics ต่อ ความทะเยอทะยานของมันคือขายการคำนวณอย่างที่ขายไฟฟ้า — สาธารณูปโภคที่เปิดตลอดเวลา แบ่งกันใช้ระหว่างคนแปลกหน้า Bell Labs ถอนตัวในปี 1969 และเรื่องเล่าของ Dennis Ritchie ว่าอะไรเกิดขึ้นต่อมานั้นไม่หรูหรา ทาง Labs ปฏิเสธจะซื้อเครื่องทดแทน ดังนั้น "จึงไม่ต้องใช้เวลานานนักที่ Thompson จะหา PDP-7 เครื่องหนึ่งที่แทบไม่มีใครใช้เจอ" ("it did not take long, therefore, for Thompson to find a little-used PDP-7") (Ritchie, The Evolution of the Unix Time-sharing System) Ken Thompson เขียน Unix ตัวแรกลงเครื่องนั้นในเวลาประมาณหนึ่งเดือน ในฤดูร้อนปี 1969 ขณะภรรยาไม่อยู่บ้าน — "โดยพื้นฐานแล้วคือคนคนเดียวทำเป็นเวลาหนึ่งเดือน มีแค่ผมคนเดียวเท่านั้น" ("essentially one person for a month, it was just myself") เขาเล่าไว้ในภายหลัง (oral history, 1989) Multics พยายามระบุทุกอย่างไว้ล่วงหน้า ส่วน Unix เล็กเพราะไม่มีงบให้ใหญ่ และตัวที่เล็กกว่านั่นเองที่แพร่ออกไป Thompson กับ Ritchie ได้รับรางวัล Turing Award ในปี 1983 ส่วน Corbató ได้รับในปี 1990
เชื่อมโยง
ชั้นล่างคือบทที่ 04 ซึ่งจัดหาทุกอย่างที่ทำให้ภาพลวงตานี้บังคับใช้ได้จริง — โหมดสิทธิพิเศษ การขัดจังหวะของตัวจับเวลา และฮาร์ดแวร์แปลที่อยู่ เหนือขึ้นไป บทที่ 06 คอมไพล์โปรแกรมโดยอิงกับ abstraction ของโพรเซสที่อธิบายไว้ในบทนี้ บทที่ 07 พาสัญชาตญาณเดียวกันขึ้นไปอีกชั้น — ซ่อนการตัดสินใจที่จะเปลี่ยนแปลง เผยแพร่แค่อินเทอร์เฟซ — และบทที่ 08 ส่วนใหญ่คือเรื่องของแอปพลิเคชันหนึ่งตัวที่ปฏิเสธจะเชื่อการรับประกันความคงอยู่ของข้อมูล (persistence) ของชั้นนี้ และสร้างมันขึ้นมาใหม่เอง บทที่ 10 ถามว่าจะเกิดอะไรขึ้นกับทั้งหมดนี้ เมื่อ "เครื่องเดียว" กลายเป็นเครื่องนับพัน และบทที่ 11 มองพรมแดนของเคอร์เนลเป็นเส้นที่ผู้โจมตีพยายามข้าม
คำถามที่ยังเปิดอยู่
เคอร์เนลยังเป็นที่ที่ถูกต้องสำหรับวางพรมแดนนี้อยู่หรือไม่ microkernel, unikernel และ virtual machine ที่อยู่ในเคอร์เนล ต่างขยับเส้นนี้กันไปคนละทาง และยังไม่มีตัวไหนยุติข้อถกเถียงได้ abstraction ของโพรเซสถูกออกแบบมาสำหรับเครื่องที่ฮาร์ดแวร์สำคัญคือ CPU แต่ทุกวันนี้งานส่วนใหญ่เกิดขึ้นบนตัวเร่ง (accelerator) ที่เคอร์เนลแทบไม่ได้จัดคิวให้ และทำไมหลังผ่านมาหกสิบปี การทำงานพร้อมกัน (concurrency) ยังเป็นจุดที่วิศวกรที่รอบคอบมีโอกาสผิดพลาดมากที่สุด
อ่านต่อ
- Remzi H. Arpaci-Dusseau & Andrea C. Arpaci-Dusseau, Operating Systems: Three Easy Pieces, version 1.10, 2023 — บทความที่อธิบายสามชิ้นส่วนนี้ได้ชัดเจนที่สุด อ่านฟรีออนไลน์
- Dennis M. Ritchie & Ken Thompson, "The UNIX Time-Sharing System," Communications of the ACM 17, no. 7 (July 1974): 365–375 — การออกแบบที่ระบบส่วนใหญ่ยังเลียนแบบอยู่จนถึงทุกวันนี้
- Nancy G. Leveson & Clark S. Turner, "An Investigation of the Therac-25 Accidents," IEEE Computer 26, no. 7 (July 1993): 18–41 — เคสที่วิศวกรระบบทุกคนควรอ่านสักครั้ง