6 คะแนน โดย GN⁺ 2026-03-30 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • นิยาม initrd ว่าเป็น หน่วยโปรแกรมที่เคอร์เนลตีความและรันโดยตรง และตีความ Linux ใหม่ให้เป็น อินเทอร์พรีเตอร์ ชนิดหนึ่ง
  • ใช้ kexec, base64, cpio เพื่อสร้าง ดิสโทร Linux แบบเรียกซ้ำที่รีบูตตัวเอง โดย initrd จะรันตัวเองอีกครั้ง
  • หากทำให้สคริปต์ /init พิมพ์ cpio image ของตัวเองออกมา ก็จะเกิด initrd แบบจำลองตัวเองในรูปแบบ Quine
  • อธิบาย โครงสร้างลำดับชั้นของอินเทอร์พรีเตอร์ที่ต่อเนื่องไปจนถึงเคอร์เนล ผ่านโครงสร้างการรัน ELF และ ld.so, binfmt_misc
  • หากใช้ kexec หรือ QEMU ก็สามารถ รัน Linux อีกตัวบน Linux แบบ tail-recursive ได้ ทำให้ทดลองขยายขอบเขตของเคอร์เนล เวอร์ชวลไลเซชัน และอินเทอร์พรีเตอร์ได้

การย้อนวิเคราะห์ rkx.gz และโครงสร้าง initrd แบบเรียกซ้ำตัวเอง

  • คำสั่ง curl https://astrid.tech/rkx.gz | gunzip | sudo sh จะดาวน์โหลดและรัน เชลล์สคริปต์ที่เข้ารหัสแบบ base64 ขนาด 20MB
    • สคริปต์จะตรวจสอบสิทธิ์ root และตรวจเช็กว่ามี kexec, base64, cpio อยู่หรือไม่
    • ถอดรหัสข้อมูล base64 เพื่อสร้าง cpio archive ชื่อ r และแตกไฟล์อิมเมจเคอร์เนลชื่อ k ออกมาจากข้างใน
    • ใช้ kexec โหลด k เป็นเคอร์เนล และ r เป็น ramdisk จากนั้นจึงรัน
  • ภายใน r.cpio มีไฟล์ /bin, /init, k โดย k คือ อิมเมจเคอร์เนล Linux 6.18.18 และ /init อยู่ในรูปเชลล์สคริปต์
    • /init จะเมานต์ /proc แล้วรวมระบบไฟล์ปัจจุบันเป็น cpio ไปไว้ที่ /r ก่อนจะใช้ kexec รัน /k และ /r อีกครั้ง
    • ผลลัพธ์คือกลายเป็น ดิสโทร Linux แบบเรียกซ้ำที่รีบูตตัวเองต่อเนื่อง

มุมมองที่มอง Linux kernel เป็นอินเทอร์พรีเตอร์

  • initrd ไม่ใช่เพียง ramdisk สำหรับบูตเท่านั้น แต่สามารถมองได้ว่าเป็น โปรแกรมที่ Linux kernel ตีความและรัน
    • เช่นเดียวกับ curl | sh หรือ python3 script.py initrd ก็เป็นรูปแบบของโปรแกรมอินพุตที่ถูกเคอร์เนลรัน
    • ดังนั้น Linux kernel จึงทำหน้าที่เป็นอินเทอร์พรีเตอร์ที่ตีความ initrd
  • โครงสร้างนี้คล้ายกับ การปรับให้เหมาะสมแบบ tail-call optimization
    • kexec จะโหลดและรันในพื้นที่หน่วยความจำใหม่ โดยไม่เขียนทับเคอร์เนลก่อนหน้า
    • แต่ละเคอร์เนลจะไม่คงสถานะเดิมไว้ และถูกแทนที่ด้วย “stack frame” ใหม่

Quine และการจำลองตัวเองของ initrd

  • Quine หมายถึง โปรแกรมที่พิมพ์ตัวมันเองออกมา
    • หากสคริปต์ /init ทำ cat /r ในตอนท้าย ก็จะพิมพ์ cpio ที่เหมือนกับตัวเองออกมา
    • ในกรณีนี้จะเกิด Quine ของอินเทอร์พรีเตอร์ Linux initrd
    • เนื่องจากทุกไฟล์อยู่บน tmpfs ใน RAM จึงไม่มี disk I/O จริงเกิดขึ้น

ELF, ld.so และลำดับชั้นของอินเทอร์พรีเตอร์

  • ไฟล์รันแบบ ELF มีพาธของ อินเทอร์พรีเตอร์ (ld-linux-x86-64.so.2) อยู่ใน header
    • ตอนรัน เคอร์เนลจะรัน ld.so ก่อน แล้ว ld.so จะโหลดไลบรารีแบบไดนามิกของ ELF และรันโปรแกรม
    • ดังนั้น ELF เองก็อาจมองได้ว่าเป็น ภาษาอินเทอร์พรีเตอร์ ชนิดหนึ่ง
  • /bin/sh ถูกตีความโดย ld.so และ ld.so ก็ถูกตีความโดยเคอร์เนลโดยตรง
    • ld.so เป็น ELF ที่ลิงก์แบบสแตติก จึงสามารถรันโดยเคอร์เนลได้โดยตรง
    • สิ่งนี้ทำให้เกิด กรณีฐาน (base case) ของลำดับชั้นอินเทอร์พรีเตอร์

การรัน CPIO ผ่าน binfmt_misc

  • หากใช้ binfmt_misc จะสามารถ รันไฟล์ที่มี magic byte เฉพาะด้วยอินเทอร์พรีเตอร์ที่กำหนดไว้ ได้
    • ตัวอย่างคำสั่งลงทะเบียน:
      echo ':cpio:M::\x30\x37\x30\x37\x30\x31::/path/to/my/script.sh:' > /proc/sys/fs/binfmt_misc/register
      
    • ด้วยการตั้งค่านี้ ไฟล์ CPIO ที่ chmod +x แล้วจะสามารถรันได้โดยตรง
  • สามารถลงทะเบียนสคริปต์ที่ใช้ QEMU รัน CPIO เป็น initrd ให้เป็นอินเทอร์พรีเตอร์ได้
    • QEMU จะบูตเครื่องเสมือนด้วยเคอร์เนลและ initrd ที่กำหนด
    • ผลลัพธ์คือ อินเทอร์พรีเตอร์ของไฟล์ CPIO ก็คือ Linux kernel ที่ QEMU บูตขึ้นมา

อินเทอร์พรีเตอร์แบบเรียกซ้ำและ “ลูปที่ประหลาดที่สุด”

  • อินเทอร์พรีเตอร์ที่อิง QEMU จะสร้าง stack frame ของสภาพแวดล้อม Linux ใหม่
    • เป็นโครงสร้างที่รัน Linux อีกตัวบน Linux และสามารถซ้อนกันได้จนกว่าจะถึงข้อจำกัดของหน่วยความจำ
    • หากแทนที่ด้วยอินเทอร์พรีเตอร์ที่อิง kexec ก็จะทำให้ การรัน Linux แบบเรียกซ้ำที่ปรับ tail call แล้ว เป็นไปได้
  • หากตั้งค่าให้ลงทะเบียน binfmt_misc ใน /init แล้วรัน /r initrd ที่รันตัวเอง ก็จะสมบูรณ์
    • /r คือ init process ถัดไปในฟอร์แมต CPIO และเมื่อรันก็จะตีความตัวเองอีกครั้ง

บทสรุป

  • initrd ไม่ได้เป็นแค่เครื่องมือบูต แต่เป็น หน่วยโปรแกรมที่ Linux kernel ตีความ
  • เมื่อใช้ kexec และ binfmt_misc ก็จะสามารถ รัน Linux เองซ้ำแบบเรียกซ้ำได้ราวกับเป็นอินเทอร์พรีเตอร์
  • โครงสร้างนี้เป็นแนวคิดเชิงทดลองที่ ลบเส้นแบ่งระหว่างเคอร์เนล เวอร์ชวลไลเซชัน อินเทอร์พรีเตอร์ และโปรแกรมจำลองตัวเอง
  • ซอร์สโค้ดที่เกี่ยวข้องเผยแพร่อยู่ใน GitHub repository ifd3f/rekexec

2 ความคิดเห็น

 
github88 2026-03-31

ไม่รู้อะไรแต่กลับกล้าพูดจริงๆ.. อยากให้เลี่ยงบทความแบบนี้ครับ

 
GN⁺ 2026-03-30
ความคิดเห็นจาก Hacker News
  • อ่านบทความนี้แล้วทรมานมากเพราะมี ความเข้าใจผิด เยอะเกินไป
    cpio archive ไม่ใช่ filesystem ผู้เขียนใช้ initramfs ซึ่งมีพื้นฐานอยู่บน tmpfs Linux สามารถแตก cpio ลง tmpfs ได้ archive ของไฟล์และไดเรกทอรีไม่ได้เป็นโปรแกรมในตัวมันเอง
    แค่บางอย่างดูคล้ายกัน ไม่ได้แปลว่ามันเหมือนกัน โปรแกรมไบนารี ถูกรันบน CPU และถ้ามี interpreter อยู่ มันก็ซ่อนอยู่ในสภาพแวดล้อมฮาร์ดแวร์ นั่นอยู่นอกขอบเขตของเคอร์เนล
    การรัน shell script ต้องมี shell ที่ใช้ตีความสคริปต์นั้น ผู้เขียนละส่วนนี้ไปและสับสนระหว่างเคอร์เนลกับโปรแกรม shell
    Linux สามารถคอมไพล์ได้โดยไม่ต้องมี initramfs หรือ ramdisk และก็ยังสามารถรัน userland ของ filesystem ได้อยู่
    คำว่า “Linux initrd interpreter” เป็นคำอธิบายที่ผิดมากจริงๆ

    • ไฟล์ ELF เองก็อาจไม่ใช่โปรแกรมในตัวมันเอง เพราะ ELF บางตัวเป็น ไลบรารีแบบไดนามิก ที่ไม่มี entry point เช่นเดียวกับที่ ELF บางตัวรันได้ ก็อาจมองได้ว่า CPIO บางตัวก็รันได้เหมือนกัน สุดท้ายแล้ว ld.so แตก ELF ลงหน่วยความจำและรัน entry point ส่วนเคอร์เนลก็แตก initramfs และรัน entry point ซึ่งเป็นแนวคิดที่คล้ายกัน
    • ไฟล์ init ใน cpio คือโปรแกรมที่ถูกตีความจริงๆ และไฟล์ที่เหลือก็ทำหน้าที่เป็นหน่วยความจำที่โปรแกรมนั้นใช้
    • โปรแกรมไบนารีรันบน CPU ก็จริง แต่ไฟล์โปรแกรมเองเป็น โครงสร้างแบบ archive ที่ประกอบด้วยหลาย section CPU ไม่สามารถเข้าใจไฟล์โปรแกรมได้โดยตรง Linux จะตั้งค่า address space ที่โปรแกรมจะรัน จากนั้นจึงกระโดดไปยังที่อยู่ที่ program counter ชี้อยู่ metadata section ของ ELF เป็นตัวกำหนดกระบวนการนี้
    • อย่างน้อยก็ยังสบายใจได้ว่าไม่ใช่บทความที่ AI เขียน
  • ระบบปฏิบัติการทุกตัวไม่ได้ทำหน้าที่เป็น interpreter ของ machine code ด้วยสิทธิ์ระดับเคอร์เนลหรอกหรือ?

    • คิดว่าไม่ใช่ OS ไม่ได้ตีความแต่ละคำสั่งเองโดยตรง แต่มันส่งต่อให้ CPU ไปประมวลผล
    • OS คือ อินเทอร์เฟซ ที่ทำให้ใช้ทรัพยากรของระบบได้ CPU เป็นตัวตีความ machine code และ OS สามารถสั่งได้ว่า CPU จะรันอะไร
    • ในกรณีนี้อาจมองว่าเป็น interpreter สำหรับไฟล์ CPIO ได้
  • บทความนี้โอเคถ้ามองเป็น mental model ว่า “Linux คือ interpreter” แต่ถ้ารับตามตัวอักษรก็ผิด
    ถ้าไม่มองว่าเป็นการตีความในระดับคำสั่งของ CPU แต่เป็นบทบาทของเคอร์เนลในการ ประสานงาน รูปแบบการรันอย่าง ELF, shebang script และ initramfs ก็จะสมเหตุสมผลกว่า ความสับสนน่าจะมาจากการปนกันของความหมายสองแบบของคำว่า ‘interpreter’

  • ประเด็นสำคัญไม่ใช่ว่าอุปมาเปรียบเทียบนี้ถูกไหม แต่คือมันแสดงให้เห็นว่าแนวคิดเรื่อง ‘การรัน’ นั้นขึ้นกับสภาพแวดล้อมมากแค่ไหน

  • “ทุกอย่างคือ interpreter?”

    • ใช่ แต่ compiler เป็นข้อยกเว้น
  • Theta Combinator ของ Turing

    • ไม่ค่อยเข้าใจว่ามันเกี่ยวอะไรกับบทความนี้ ฉันไม่คุ้นกับแนวคิดของ functional programming
  • ในบทความก่อนหน้าของซีรีส์นี้ ผู้เขียนบอกว่าไม่อยากใช้ object storage ของ Contabo เลยสร้างอิมเมจ VPS เอง
    ฉันคิดว่ามี จุดสมดุล ระหว่างการใช้เวลา 50 ชั่วโมงเพื่อประหยัด 1.50 ดอลลาร์ต่อเดือน กับอีกสุดขั้วคือใช้เงิน 250,000 ดอลลาร์ไปกับ token
    ถ้ารับภาระค่า infra ไม่ไหว ปัญหาอาจอยู่ที่ ปัจจัยทางสังคม มากกว่าความสามารถทางเทคนิค การหมกมุ่นกับการรัน Doom ผ่าน curl ดูไม่ค่อยก่อผลผลิตเท่าไร

    • ฉันก็เคยเป็นแบบนั้นมาก่อน VPS เดือนละ 5 ยูโรแพงเกินไป ฉันเลยปิด instance ไว้ก่อนจนกว่าจะมีเงิน แล้วสำรอง root filesystem ไว้ในโน้ตบุ๊กของแม่ ต่อมาฉันลง Terminal IDE บน Kindle แล้วเล่นกับ busybox และ gcc ขอบคุณ Spartacus Rex ที่ทำให้เส้นทางอาชีพของฉันเริ่มต้นขึ้น
    • ที่ผู้เขียนพูดเป็นมุก เหตุผลจริงอยู่ในย่อหน้าถัดไปเลย — “ผมคิดว่ามันเป็นทริกที่สนุก และถ้าเอาไปลงบล็อก ผมก็ได้เรียนรู้ คนอ่านก็ได้เรียนรู้ และยังได้ internet point ด้วย เป็น win-win
    • สิ่งที่สำหรับบางคนดูไม่เกิดผลผลิต การได้สนุกกับ ความสนใจเฉพาะทาง กลับสำคัญต่อสุขภาพจิต สำหรับฉันที่เป็น ADHD มันยิ่งเป็นกิจกรรมที่จำเป็น
    • คำพูดว่า “ถ้าจ่าย 1.50 ดอลลาร์ไม่ไหว ก็ไม่ใช่มืออาชีพ” ฟังดูแปลก มืออาชีพนิยามจากการได้รับค่าตอบแทน ไม่ใช่นิยามจากรายจ่าย
  • ถ้าเปิด man ld.so จะเห็นว่ามันระบุชัดว่า dynamic linker ที่เก็บอยู่ใน section .interp ของ ELF จะถูกเรียกใช้งาน ชื่อ section เองก็น่าสนใจ

  • Linux มีประโยชน์มากในฐานะ อินเทอร์เฟซที่ตั้งโปรแกรมได้ Windows ก็ทำได้เหมือนกัน แต่รู้สึกว่า Linux เหมาะกว่า
    ฉันคิดว่า GUI บน Windows ดีกว่า แต่ GNOME หรือ KDE ก็ใช้งานไม่สะดวกเหมือนกัน เลยใช้ fluxbox, icewm และบางครั้งก็ xfce หรือ mate-desktop ทุกวันนี้ฉันชอบสภาพแวดล้อมที่เรียบง่ายและเร็ว งานส่วนใหญ่ทำผ่าน command line และการแก้โค้ด

    • ถ้าต้องการสภาพแวดล้อมที่เร็วและเรียบง่าย ชุด Sway + foot ดีมาก จัด workspace ด้วย keybind แล้วใช้งานได้สบายโดยแทบไม่ต้องพึ่งเดสก์ท็อป
    • ไม่เห็นด้วยว่า GUI ของ Windows ดีกว่า GNOME กับ KDE ก็ไม่ค่อยดีเหมือนกัน แต่ Windows หนี WM อันซับซ้อนของ Microsoft ไม่ได้ โดยส่วนตัวฉันคิดว่าอินเทอร์เฟซสาย mpx/mux เช่น 9wm, cwm, dwm ดีกว่าสาย Xerox มาก ใกล้กับปรัชญาของ Engelbart มากกว่าและโดยรวมสะอาดกว่ามาก