ใส่ “You” เข้าไปใน CPU
(cpu.land)- นี่คือบทนำของโปรเจกต์การเรียนรู้ส่วนบุคคลที่พยายามทำความเข้าใจให้ถึงที่สุดว่า จริง ๆ แล้วเกิดอะไรขึ้น เมื่อรันโปรแกรมบนคอมพิวเตอร์
- คำถามหลักต่อยอดไปสู่เรื่องว่าโปรแกรมถูกรันบน CPU โดยตรงหรือไม่, system call ทำงานอย่างไร, และหลายโปรแกรมทำงานพร้อมกันได้อย่างไร
- นอกเหนือจากการเรียนในมหาวิทยาลัย การหาสื่อเกี่ยวกับ ระบบคอมพิวเตอร์ ที่ครอบคลุมนั้นทำได้ยาก จึงต้องคัดกรองข้อมูลที่ขัดแย้งกันจากแหล่งที่มีคุณภาพต่างกันด้วยตัวเอง
- หลังจากค้นคว้าอยู่หลายสัปดาห์และจดโน้ตยาวเกือบ 40 หน้า ก็ทำให้เข้าใจกระบวนการตั้งแต่เริ่มเปิดเครื่องไปจนถึงการรันโปรแกรมได้ดีขึ้น
- ผู้เขียนบอกว่าแม้แต่ผู้อ่านที่รู้สึกว่ารู้อยู่แล้วก็อาจมีเรื่องใหม่ให้เรียนรู้ และถ้าไม่มีเวลา แนะนำให้อ่านบทที่ 3 ก่อน
จุดเริ่มต้นของการทำความเข้าใจการรันโปรแกรม
- บทความนี้เริ่มต้นจากคำถามว่า เกิดอะไรขึ้นอย่างแม่นยำ เมื่อเรารันโปรแกรมบนคอมพิวเตอร์
- แม้จะมีความรู้ระดับล่างอยู่พอสมควร แต่ก็ยังยากที่จะเชื่อมชิ้นส่วนต่าง ๆ ให้เป็นกระบวนการเดียวกัน
- คำถามแตกออกเป็นสามทาง
- โปรแกรมถูก รันบน CPU โดยตรง จริงหรือไม่
- system call คืออะไร และในทางปฏิบัติมันทำงานอย่างไร
- หลายโปรแกรมทำงานพร้อมกันได้อย่างไร
กระบวนการค้นคว้าและลำดับการอ่าน
- เพราะมีสื่อเกี่ยวกับระบบคอมพิวเตอร์แบบครอบคลุมไม่มาก จึงต้องค้นคว้าจากหลายแหล่งด้วยตัวเอง และแต่ละแหล่งก็มีทั้งคุณภาพและเนื้อหาที่แตกต่างกัน
- จากการค้นคว้าหลายสัปดาห์และโน้ตยาวเกือบ 40 หน้า ทำให้เข้าใจการทำงานของคอมพิวเตอร์ได้ดีขึ้น ตั้งแต่กระบวนการเริ่มต้นไปจนถึงการรันโปรแกรม
- เป้าหมายคือการสร้างบทความอธิบายที่แน่นและครบถ้วนแบบที่ผู้เขียนเองอยากหาให้ได้
- สำหรับผู้อ่านที่รู้สึกว่ารู้อยู่แล้วหรือมีเวลาน้อย ผู้เขียนแนะนำให้อ่าน บทที่ 3 ก่อน
- บทความถัดไปจะต่อด้วย บทที่ 1: The “Basics”
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ผมเป็นคนทำสิ่งนี้เอง ขอบคุณที่อ่านและช่วยชี้จุดแก้ไขหลายอย่างแบบสมกับเป็น Hacker News. Putting the "You" in CPU ยังอยู่ระหว่างทำงานเต็มที่ เดิมทีตั้งใจว่าจะขัดเกลาและเพิ่มเนื้อหาให้มากกว่านี้ แล้วค่อยโพสต์ลง HN ราวสัปดาห์หน้า
ผมอายุ 17 ปี เมื่อ 1 ปีก่อนออกจากโรงเรียนมัธยม แล้วเริ่มทำงานเต็มเวลาให้ Hack Club(https://hackclub.com/) เท่าที่จำได้ก็เขียนโปรแกรมมาตลอด และราว 6 ปีก่อนเริ่มเรียนแบบโฮมสคูลเพื่อจะได้โฟกัสกับการเขียนโปรแกรมและความสนใจอื่น ๆ มากขึ้น
ทุกอย่างเรียนด้วยตัวเอง เลยไม่เคยเรียนวิชาระบบของมหาวิทยาลัย และไม่ค่อยพอใจกับคำตอบของตัวเองต่อคำถามว่า “เมื่อรันอะไรสักอย่างแล้วเกิดอะไรขึ้น” ดังนั้นจึงใช้เวลามหาศาลเรียนให้ลึกที่สุดเท่าที่ทำได้ และระหว่างนั้นก็พบว่า ระบบปฏิบัติการและฮาร์ดแวร์ เป็นเรื่องที่เรียนแล้วสนุกมากจริง ๆ แต่ แหล่งข้อมูลออนไลน์ ในหัวข้อนี้มักแย่มาก
เวลาค้นคว้ามักไปเจอ PDF สไลด์บรรยายจากปี 2014 หรือคำตอบใน StackOverflow ที่จริง ๆ แล้วผิดหรือทำให้เรียบง่ายเกินไปบ่อย ๆ เลยเขียน Putting the "You" in CPU ด้วยความหวังว่าจะเป็นแหล่งข้อมูลที่ดีกว่าสำหรับคนที่อยากเรียนด้านนี้ด้วยตัวเอง ยังไม่สมบูรณ์เพราะต้องเขียนเพิ่มอีกสองสามย่อหน้าเกี่ยวกับ SMP แต่ผมคิดว่ามันดีกว่าแหล่งข้อมูลส่วนใหญ่ที่เคยเห็นมาก ภาพและไดอะแกรมก็เป็นครั้งแรกที่ลองทำเอง และยิ่งไปถึงบทท้าย ๆ ก็ยิ่งดีขึ้น ภาพบางภาพช่วงหลัง ๆ ผมค่อนข้างภูมิใจเลย
ทั้งหมดเป็นโอเพนซอร์สอยู่บน GitHub: https://github.com/hackclub/putting-the-you-in-cpu
บางครั้งหาคำตอบได้จากเธรดใน Reddit แต่โดยมากมีสัญญาณรบกวนปะปนอยู่ และก็สงสัยด้วยว่า Reddit เป็นที่ที่เชื่อถือได้สำหรับเก็บความรู้หรือไม่ ส่วน StackOverflow เน้นคำถามเพื่อแก้งานเฉพาะอย่าง คำตอบจึงสั้นโดยเลี่ยงไม่ได้ เลยไม่ค่อยช่วยเป็นพิเศษ
ความรู้ทั่วไปตอนนี้ผมข้ามการค้นหาของ Google ที่พัง ๆ ไปเข้า Wikipedia ตรง ๆ แต่ Wikipedia เองก็มีขีดจำกัดว่าจะลงลึกหัวข้อทางเทคนิคได้แค่ไหน ผมคิดว่าเราอาจต้องมี วิกิ ที่รวบรวมรายละเอียดโครงสร้างภายในคอมพิวเตอร์อย่างครอบคลุม แหล่งข้อมูลกระจัดกระจายมีเยอะ แต่ถ้ารวมไว้ที่เดียวคงดีมาก
เอกสารนั้นบอกให้รู้มากกว่าว่าโลกตอนนี้เป็นอย่างไร แต่ว่ามันอาจเป็นอย่างไรได้บ้าง การทำ OSDEV ทำให้ได้เรียนรู้เรื่องสนุก ๆ เยอะ เช่น โปรเซสเซอร์ 32 บิตเคยทำ address แบบ 36 บิตด้วย PAE อย่างไร หรือโปรเซสเซอร์ 64 บิตทำ address แบบ 52 บิตเพื่อรีดเงินเพิ่มอย่างไร
ยังได้รู้ด้วยว่าทำไมคอมพิวเตอร์ต้องเริ่มจากโหมด 16 บิตแล้วต้องใช้ท่ากายกรรมแอสเซมบลีระดับระบบเพื่อไปถึง long mode ทำไมโหมด 8 บิตยังเหลืออยู่ ฯลฯ ถ้าชอบเรียนเรื่องการโหลดไบนารี ยังมีเรื่องให้ประหลาดใจอีกมาก ตัวอย่างเช่นในโหมด 64 บิตก็ยังมี memory segmentation เหลืออยู่ แต่ใน long mode จะถูกบังคับให้เป็นการแมปแบบแบน
ผมชอบแหล่งข้อมูลแบบนี้ ช่วงนี้กำลังก้าวเข้าสู่โลก RISC-V และเห็นได้ชัดว่าคนที่อยากผลักดันซอฟต์แวร์เสรีและโอเพนซอร์สไปข้างหน้า จำเป็นต้องมีความรู้แบบนี้เพื่อให้ซอฟต์แวร์ทำงานได้ดีบนคอมพิวเตอร์บอร์ดเดี่ยวรุ่นใหม่ ๆ หลายตัว
ในกรณีของผม ระหว่างเรียนว่าคอมพิวเตอร์ทำงานอย่างไร การกำหนดสถาปัตยกรรมชุดคำสั่ง การทำ simulator กับ assembler และการเขียน assembly เอง เป็นเรื่องที่พบได้ทั่วไป คนที่ขยันกว่านั้นก็อาจ implement ด้วย FPGA หรือเขียน LLVM backend
ฝั่งระบบปฏิบัติการก็เหมือนกัน การเรียนด้วยการ implement kernel เอง หรืออย่างน้อยแก้ไข kernel ที่มีอยู่แล้ว ถือเป็นกระบวนการที่ค่อนข้างพบได้ทั่วไป
ถ้าอยากรู้มากขึ้นว่าคอมพิวเตอร์ทำงานอย่างไรในระดับพื้นฐานมาก ๆ ขอแนะนำหนังสือ Code ของ Charles Petzold จริง ๆ เริ่มจากหลักการแรกสุดแล้วไต่ขึ้นไปถึงระดับที่ค่อนข้างสูง
หนึ่งในจุดที่แหล่งข้อมูลนี้ทำได้ดี คือการชี้ให้เห็น สิ่งที่คาดไว้กับผลลัพธ์การเรียนรู้จริง ในกระบวนการรวบรวมข้อมูล ประมาณว่า “คาดว่าเป็น x เพราะ y แต่จริง ๆ แล้วเป็น a เพราะ b!”
การคลี่คลายความเข้าใจผิดมักเป็นขั้นตอนสำคัญในการก่อรูปความรู้ใหม่ และการที่ผู้เขียนยอมรับว่าผู้อ่านต้องก้าวข้ามความเข้าใจผิดของตัวเองระหว่างประมวลผลข้อมูลนั้นช่วยได้มาก เป็นมิตรต่อผู้อ่านกว่าท่าทีแบบ “นี่คือคำตอบที่ถูก และถ้าคิดต่างก็โง่” มาก
บทที่ 6 อธิบาย ค่าที่ fork() คืนกลับ ผิด ตรงข้ามกับที่เขียนไว้ ควรเป็นว่าฝั่ง parent จะได้ PID ของ child คืนกลับ และฝั่ง child จะได้ 0 คืนกลับ ผมทำ pull request สำหรับส่วนนี้แล้ว
ในคำอธิบายที่ว่า
add eax, 512ถูกแปลเป็น05 00 02 00 00นั้น ระบุว่าไบต์แรก05คือ opcode สำหรับบวกตัวเลข 16 บิตเข้ากับรีจิสเตอร์ EAX และไบต์ที่เหลือคือ 512 (0x200) แบบ little-endianแต่คำอธิบายนี้อธิบายแค่ 3 ไบต์แรกจากคำสั่งขนาด 5 ไบต์ แล้ว
00ที่เหลือคืออะไร เลยสงสัยอยู่ บางทีตั้งใจจะเขียนว่า opcode นี้บวก ตัวเลข 32 บิต เข้ากับ EAX หรือเปล่าaxเป็นบทความที่ดี ในฐานะเว็บเดเวลลอปเปอร์ที่เรียนเอง รู้สึกว่าสื่อแบบนี้มีคุณค่ามาก ช่วยให้หลุดออกจาก abstraction หลายชั้นและเข้าใจอย่างเป็นรูปธรรมมากขึ้นว่าสิ่งต่าง ๆ ทำงานจริงอย่างไร
ดูเป็นสื่อที่ดี เข้าประเด็นหลักค่อนข้างเร็ว และมีความสนุกนิดหน่อย ไม่ได้ให้ความรู้สึกเหมือน “บทสอน monad” เท่าที่คาดไว้ คิดว่าจะอ่านต่อ
เป็นสื่อที่ดี แต่ยังมีข้อสงสัยอย่างหนึ่งเกี่ยวกับเคอร์เนล ใน [0] อธิบายว่า shebang ถูกประมวลผลในเคอร์เนล และเพราะไม่ได้โหลดทั้งไฟล์ แต่ดึงจาก
bufจึงถูกตัดที่ความยาวของbufเสมอเมื่อ 4 ปีก่อน มีคนรำคาญที่เคอร์เนลตัดพาธที่ยาวเกิน 128 อักขระ จึงเพิ่มขนาดบัฟเฟอร์เป็นสองเท่า ทำให้จุดที่ถูกตัดยาวขึ้นเป็นสองเท่า ดังนั้นทุกวันนี้บน Linux ถ้าบรรทัด shebang ยาวเกิน 256 อักขระ ส่วนหลังจาก 256 อักขระจะหายไปหมด
ไม่เข้าใจว่าทำไมถึงตัดพาธไฟล์ของใครสักคนแบบเงียบ ๆ ไม่ใช่ที่ไหนอื่น แต่เป็น เคอร์เนล ที่ไม่ควรทำแบบนั้น พาธไฟล์ยาว 256 ไบต์อาจดูไม่สมเหตุสมผล แต่สตริงพาธ+อาร์กิวเมนต์ยาว 256 ไบต์ วันหนึ่งแทบจะต้องเกิดขึ้นแน่ การทำให้สคริปต์พังแบบเงียบ ๆ เป็นเรื่องผิด
[0]: https://cpu.land/how-to-run-a-program
เมื่อไม่กี่ปีก่อน มีคนพยายามแพตช์เคอร์เนลเพื่อให้ shebang ที่ยาวเกินไปอย่างน้อยก็ล้มเหลวอย่างชัดเจน แทนที่จะล้มเหลวแบบเงียบ ๆ แต่เกิดปัญหากับ NixOS เพราะตามสไตล์ Nix มีการใช้ shebang ที่ยาวเกินไปซึ่งเต็มไปด้วยพาธ nix-store ยาว ๆ อยู่แล้ว ก่อนหน้านั้นมันถูกตัดแบบเงียบ ๆ แต่ก็ยังทำงานได้ จู่ ๆ สคริปต์เหล่านั้นทั้งหมดก็ล้มเหลว ทำให้ ความเข้ากันได้ย้อนหลัง พัง จึงต้อง revert แพตช์นั้น และหลังจากนั้นก็ไม่มีการเปลี่ยนแปลงคล้าย ๆ กันออกมาอีก
ดูรายละเอียดได้ที่ https://lwn.net/Articles/779997/
ตัว shebang เองยังจัดการอาร์กิวเมนต์มากกว่าหนึ่งตัวได้ไม่ดีด้วยซ้ำ ถ้าเขียน
#!/bin/program -args somescriptมันจะถูกแยกเป็น['/bin/program','-args somescript']ซึ่งแทบจะไม่ใช่ผลลัพธ์ที่ต้องการเลย นี่เป็นเฉพาะ Linux และ shebang ก็ไม่ได้พกพาได้ทั่วไปบน Unix ทุกตัวด้วยถ้าต้องการทำเรื่องซับซ้อน ควรใช้
#!/bin/shแล้วexecด้วยอาร์กิวเมนต์ที่ต้องการจากไฟล์ปัจจุบันจะดีกว่าจุดหนึ่งที่ลิงก์ไม่ได้พูดถึงคือสามารถใช้
envเพื่อหาอินเทอร์พรีเตอร์จากพาธปัจจุบันได้ เช่น#!/usr/bin/env python3จะหาpython3ที่ผู้ใช้ใช้อยู่ให้โดยไม่ต้องรู้ตำแหน่งวิธีนี้อาจมีประโยชน์กับสคริปต์ที่จะรันใน virtual environment ซึ่งทำงานโดย override
PATHไปยัง Python interpreter แต่ไม่ควรทำแบบนี้กับโปรแกรมที่ติดตั้งแล้ว เพราะไม่ควรปล่อยให้โปรแกรมที่ติดตั้งแล้วถูกควบคุมว่าใช้ interpreter ใดโดยผู้ใช้ ควรใช้พาธแบบ absoluteenvมีออปชัน-Sที่ช่วยจัดการข้อจำกัดนี้ได้ โดย parse command line จากสตริงเดียว ถึงอย่างนั้น ในฐานะคนที่หลีกเลี่ยงลูกเล่นในบรรทัด shebang มาหลายปี อย่างมากก็ยังดูเป็นแนวปฏิบัติที่น่าสงสัย