2 คะแนน โดย GN⁺ 2023-11-17 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • การรองรับ real-time preemption ของ Linux เป็นงานที่รอการเข้าสู่เมนไลน์มาเกือบ 20 ปี และ Thomas Gleixner เปิดเผยในงาน 2023 Linux Plumbers Conference ว่าอุปสรรคใหญ่สุดท้ายคือ printk()
  • เป้าหมายคือทำให้โปรเซสที่มีลำดับความสำคัญสูงสุดสามารถทำงานได้ด้วย latency ที่สั้นและคาดการณ์ได้ และเพื่อสิ่งนี้ ส่วนแกนหลักจำนวนมากของเคอร์เนลจึงถูกเขียนใหม่มาเป็นเวลานาน
  • printk() สามารถถูกเรียกได้ในทุก context จึงซับซ้อนกว่าการพิมพ์ล็อกธรรมดามาก และ การส่งออกแบบ synchronous ในปัจจุบันก็ขัดกับเป้าหมายด้าน latency ของงานเรียลไทม์
  • ตั้งแต่ปี 2018 มีแพตช์ราว 300 รายการ ที่เข้า upstream แล้วหรือกำลังรออยู่ใน linux-next โดยงานที่ยังเหลือคือการจัดการ handover ของข้อความฉุกเฉินและความปลอดภัยของ console driver
  • เมื่อการจัดระเบียบ printk() เสร็จ และโค้ดเรียลไทม์ส่วนที่เหลือพร้อมใน linux-next ก็อาจรวมเข้าใน merge window เดียวกันได้ แต่ Gleixner บอกว่าจะไม่คาดการณ์ช่วงเวลาที่จะเสร็จอีกแล้ว

งาน real-time preemption ที่ยาวนานเกือบ 20 ปี

  • การรองรับเรียลไทม์ของ Linux ปรากฏใน LWN ครั้งแรกเมื่อปี 2004 และเป็นเวลานานที่ดูเหมือนว่า “อีกนิดเดียวก็จะเสร็จ”
  • แม้ว่า LWN จะเคยใช้ชื่อ the realtime preemption endgame ตั้งแต่ปี 2009 แต่ในงาน 2023 Linux Plumbers Conference นั้น Gleixner มองว่าคราวนี้จุดจบใกล้เข้ามาจริง ๆ
  • สำหรับ Gleixner เอง นี่เป็นงานที่ยาวเกือบ 25 ปี
    • เขาเริ่มทำงานด้านการรองรับเรียลไทม์ของ Linux ตั้งแต่ปี 1999
    • ตัวโครงการเองก็ดำเนินต่อเนื่องมาเกือบ 20 ปี
  • เขาบอกว่าเมื่อทุกอย่างเสร็จจะมี “a big party” แต่ตอนนี้อุปสรรคใหญ่สุดท้ายยังคงเป็น printk()

latency ที่ real-time preemption พยายามลด

  • เป้าหมายของ real-time preemption คือทำให้โปรเซสที่มีลำดับความสำคัญสูงสุดสามารถทำงานได้เสมอด้วย latency ต่ำสุดและคาดการณ์ได้
  • เพื่อให้ทำได้ เคอร์เนลต้องสามารถถูก preempt ได้ในสถานการณ์ให้มากที่สุดเท่าที่เป็นไปได้ และข้อยกเว้นต้องถูกจำกัดให้อยู่ในขอบเขตที่แคบและชัดเจน
  • แนวทางการทำงานพื้นฐานถูกวางไว้นานแล้ว แต่การแก้ปัญหารายละเอียดใช้เวลายาวนาน
  • ระหว่างทางนี้ ส่วนสำคัญหลายส่วนของเคอร์เนลหลักถูกเขียนใหม่ และประโยชน์ของมันก็ขยายไปไกลกว่ากรณีใช้งานแบบเรียลไทม์สู่เคอร์เนลโดยรวม

ทำไม printk() จึงเป็นอุปสรรคสุดท้าย

  • เมื่อโค้ดเคอร์เนลต้องส่งข้อความไปยังคอนโซลและล็อก มันจะเรียก printk() หรือฟังก์ชันที่สร้างอยู่บนมัน
  • แม้จะดูเหมือนการพิมพ์ข้อความธรรมดา แต่ printk() ต้องทำงานได้ใน แทบทุก context
    • มันอาจถูกเรียกได้แม้ใน non-maskable interrupt handler
    • มันอาจถูกเรียกซ้ำจากภายใน printk() อื่นได้
    • ในสถานการณ์ที่ระบบล่ม ข้อมูลที่พิมพ์ออกมาอาจสำคัญมาก จึงยากที่จะจำกัด context ที่เรียกได้
  • ด้วยข้อกำหนดเหล่านี้ ปัญหาเรื่อง concurrency, locking และการจัดการไดรเวอร์จึงพันกันอย่างซับซ้อนใน printk()
  • printk() ของเคอร์เนลปัจจุบันมีโครงสร้างแบบ synchronous เต็มรูปแบบ
    • การเรียกจะไม่คืนค่าจนกว่าข้อความจะถูกส่งไปยังทุกปลายทางที่ตั้งไว้
    • Gleixner อธิบายโครงสร้างนี้ว่า “stupid”
    • โดยเฉพาะระหว่างการบูต แม้ข้อความส่วนใหญ่จะเป็นเพียง noise ธรรมดา ก็ยังต้องรอให้ถูกส่งออกทั้งหมด
  • เวลารอคอยนี้ขัดแย้งโดยตรงกับ latency ที่งานเรียลไทม์พยายามลด
  • นักพัฒนาเรียลไทม์ย้ายการส่งออกของ printk() ไปยังเธรดแยกและทำให้เป็นแบบ asynchronous มานานแล้ว แต่โค้ดนั้นใกล้เคียงกับการแฮ็กหลายอย่างมากกว่าจะเป็นวิธีแก้ปัญหาเชิงพื้นฐาน

การปรับปรุง printk() ใหม่ตั้งแต่ปี 2018

  • ปัญหา printk() ถูกจัดการอย่างจริงจังตั้งแต่ปี 2018 และมีแพตช์ราว 300 รายการ ที่เข้า upstream แล้วหรือกำลังรออยู่ใน linux-next
  • ตอนนี้ยังมีชุดแพตช์สุดท้าย 3 ชุดที่กำลังดำเนินการเพื่อปิดงานนี้
  • หนึ่งในรายละเอียดที่ยากที่สุดคือ กลไก handover
    • เมื่อเคอร์เนลต้องพิมพ์ข้อความฉุกเฉิน เช่น ตอนระบบล่ม มันอาจต้องแย่งการควบคุมจากคอนโซลที่กำลังพิมพ์ข้อความลำดับความสำคัญต่ำกว่าอยู่
    • การทำสิ่งนี้ให้ปลอดภัยในทุก context ไม่ใช่เรื่องง่าย
  • อีกงานหนึ่งคือการทำเครื่องหมาย console driver ที่ไม่สามารถใช้งานได้อย่างปลอดภัยในบาง context
    • ตัวอย่างเช่น หากต้องพิมพ์ข้อความระหว่าง non-maskable interrupt แต่ต้องมีการตั้งค่าโหมดวิดีโอ ก็จะไม่สามารถทำงานได้
  • Gleixner ตอบว่าในช่วงปีที่ผ่านมาไม่มีการเปลี่ยนแนวคิดพื้นฐาน
    • ในเคอร์เนลมี console driver ที่ต้องแก้ไขอยู่ 76 ตัว
    • โค้ด handover ถูกเปลี่ยนให้สามารถอัปเดตไดรเวอร์ทีละตัวได้ แทนที่จะต้องแก้ทุกไดรเวอร์พร้อมกัน
    • การอภิปรายเพิ่มเติมเกี่ยวกับงาน printk() ล่าสุดอยู่ในบทความนี้

เอาต์พุตแบบ asynchronous และเงื่อนไขการรวมเข้าเมนไลน์

  • เมื่อ Masami Hiramatsu ถามว่าข้อความเคอร์เนลใดบ้างที่ควรถูกพิมพ์แบบ synchronous นั้น Gleixner ตอบว่าเกือบทุกอย่างควรถูกทำให้เป็น asynchronous
  • เอาต์พุตแบบ asynchronous ช่วยลด latency ที่เกิดจากการเรียก printk() และยังทำให้สามารถมี kernel thread แยกสำหรับแต่ละคอนโซลได้
    • คอนโซลที่เร็วสามารถทำงานตามความเร็วของตัวเองได้ โดยไม่ต้องรอคอนโซลที่ช้าที่สุด
  • โค้ดถูกเปลี่ยนให้แน่ใจว่าข้อความสำคัญจะถูกคัดลอกลง message buffer อย่างสมบูรณ์ก่อนที่บรรทัดแรกจะเริ่มถูกพิมพ์
    • นี่เป็นมาตรการเพื่อรับมือกรณีที่ console driver ที่มีข้อบกพร่องทำให้ทั้งระบบพัง
  • เพื่อให้ลำดับการพิมพ์ปลอดภัยขึ้น ระบบจะเขียนไปยังคอนโซลที่รู้ว่าปลอดภัยก่อน
    • ตัวอย่างเช่น หากมี persistent-memory store ก็จะบันทึกข้อความก่อนส่งไปยังอุปกรณ์จริง
    • นี่เป็นวิธีรักษาเนื้อหาที่พิมพ์ไว้ แม้ไดรเวอร์ที่มีปัญหาจะทำให้ระบบล่ม
  • Gleixner บอกว่างานเข้าใกล้ความเสร็จแล้ว แต่เพราะ printk() คาดเดาได้ยาก เขาจึงจะไม่พูดถึงกำหนดเวลาสำเร็จอีกต่อไป
  • ถึงอย่างนั้น เขาก็ระบุว่ายังหวังให้โค้ด real-time preemption ส่วนที่เหลือเข้าสู่เมนไลน์ก่อนครบวาระ 20 ปีในช่วงปลายปี 2024
  • เมื่อ Clark Williams ถามว่าหลังจากแพตช์ printk() เข้า upstream แล้ว จะใส่โค้ดเรียลไทม์ส่วนที่เหลือใน merge window เดียวกันหรือไม่ Gleixner ตอบว่า “yes” แบบมีเงื่อนไข
    • หากโค้ดทั้งหมดถูก staged ไว้ใน linux-next และดูพร้อม ก็อาจลองทำได้

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

 
GN⁺ 2023-11-17
ความคิดเห็นบน Hacker News
  • QNX ทำเรื่องนี้ได้ถูกทางมาตั้งแต่หลายสิบปีก่อนแล้ว ไมโครเคอร์เนลมีขอบเขตสูงสุดสำหรับทุกงานที่ตัวเองทำ และโค้ดก็มีแค่หลักหมื่นบรรทัดเท่านั้น
    ไมโครเคอร์เนลทำแค่การจัดสรรหน่วยความจำ, การ dispatch CPU และการส่งข้อความระหว่างโปรเซส ส่วนที่เหลือทั้งหมดรวมถึงไดรเวอร์และ logger อยู่ใน user space และสามารถถูก preempt ได้โดยเธรดที่มี priority สูงกว่า
    เคอร์เนล QNX ไม่จัดการสตริง ไม่มีการพาร์ส ไม่มีการฟอร์แมต ไม่มีข้อความ Linux บวมเกินไปสำหรับงานเรียลไทม์ และต้องทำให้โค้ดเคอร์เนลหลายล้านบรรทัดทั้งหมด preempt ได้ โครงสร้างเองจึงไม่เหมาะกับเรียลไทม์ นั่นแหละคือเหตุผลที่ต้องใช้เวลา 20 ปีในการแก้

    • ตัวอย่างสมัยใหม่คือ seL4 เท่าที่ทราบ มันไม่ทำ dynamic memory allocation และยังผ่านการ formal verification สำหรับคุณสมบัติหลายอย่างด้วย
      ผลงานสำคัญที่สุดต่อการออกแบบเคอร์เนลน่าจะเป็นการใช้ capability อย่างทั่วถึง เพื่อส่งมอบอำนาจควบคุมออกไปยัง user space ได้อย่างปลอดภัยแต่ยังยืดหยุ่น
    • QNX ไม่ได้ใช้ใน ระบบ infotainment ของรถยนต์หรอกหรือ? อยากรู้ว่าใช้ที่อื่นที่ไหนอีก
      ตัวการบวมของเคอร์เนลเองไม่ได้ทำให้กังวลมากนัก Linux ได้รับเวลาพัฒนาจำนวนมาก และแม้เดสก์ท็อปจะไม่ได้มี priority สูงเท่าเซิร์ฟเวอร์ แต่งานที่ทำเพื่อสร้างเคอร์เนลประสิทธิภาพดีสำหรับอุปกรณ์พกพาอะไรทำนองนั้นก็น่าจะเป็นประโยชน์ต่อผู้ใช้เดสก์ท็อปด้วย
    • เคอร์เนล SDP 8 ปัจจุบันมี 15,331 บรรทัด รวมคอมเมนต์และ Makefile แล้ว
    • ดูเหมือนฟังก์ชัน main ที่จัดโครงสร้างดีในภาษา C หรือภาษาตระกูล C main แค่ประสานการเรียกฟังก์ชันอื่น ๆ และถึงตรงนี้เคอร์เนล QNX จะทำการ initialize น้อยกว่า แต่แนวคิดใหญ่ ๆ ก็คล้ายกัน
      ผมไม่ใช่นักพัฒนาเคอร์เนล แต่แนวทางที่รักษาให้มันเรียบง่ายแบบนี้ดูดี
    • ประมาณ 90% ของ “เคอร์เนลหลายล้านบรรทัด” คือ ไดรเวอร์อุปกรณ์ ถ้าจะรันบนฮาร์ดแวร์ใด ๆ ก็ตาม สุดท้ายไมโครเคอร์เนลก็ยังต้องมีอยู่ดี
  • มีตัวอย่างที่แสดงให้เห็นว่าแม้ระบบกำลังจะตาย เคอร์เนลก็ยังพยายามส่งข้อความ log ออกไปให้ได้ และในสภาพแวดล้อมใช้งานจริงมันถูกใช้กันอย่างไร
    https://netflixtechblog.com/kubernetes-and-kernel-panics-ed6...

  • ถ้าปัญหานี้ถูกแก้ได้ ผมสงสัยว่าจะสามารถแทนที่บางชุดฮาร์ดแวร์/ซอฟต์แวร์ที่สร้างมาเพื่องานเรียลไทม์ได้พอสมควรหรือไม่ ตอนนี้มีตัวเลือกชิป ARM และ x86 ที่ราคาถูก ใช้พลังงานต่ำ และ clock สูงอยู่มาก
    เพราะ clock สูงมาก แม้จะมีบางครั้งที่พลาด ก็อาจมี cycle เหลือเยอะจนความเป็นเรียลไทม์แบบสมบูรณ์มีความสำคัญน้อยลงในหลายกรณี ผมรู้ว่ามันไม่หรูและไม่มีประสิทธิภาพนัก แต่บางครั้ง ของสำเร็จรูปทั่วไป ก็ชนะความแม่นยำได้เหมือนกัน

    • งานที่ต้องการ hard real-time ไม่สามารถพอใจกับคำว่า “แม้พลาดก็มี cycle เหลือเยอะ” ได้ และไม่ใช่แค่เรื่อง cycle ของ CPU ด้วย
      งานที่ทำผิดพลาดเพียงงานเดียวอาจยึดเคอร์เนลไว้จนทำงานที่มีประโยชน์ไม่ได้ แก่นของ hard real-time คือ “ไม่มีอะไรขัดขวางการรันงานสำคัญนี้ได้” ในวงการรถยนต์หรืออากาศยาน ระบบควบคุมต้องทำงานได้ไม่ว่าจะอยู่ในสถานการณ์ใด
    • แอปพลิเคชันที่มีข้อกำหนดเรียลไทม์จริง ๆ มักมีข้อกำหนดเข้มมากจนไม่สามารถยอมรับโอกาสล้มเหลวแม้เล็กน้อยมากได้ ลองนึกถึง avionics, อุปกรณ์การแพทย์, รถยนต์, แอปพลิเคชันทางทหาร
      ถ้าจำเป็นต้องเรียลไทม์จริง ๆ ก็คือจำเป็นจริง ๆ และไม่มีคำว่า “ใกล้เคียงพอ” ทั้งนี้เป็นเพียงความรู้สึกในฐานะคนนอก
    • เวลาสร้างแอปพลิเคชันเรียลไทม์ด้วยชิป ARM พลังงานต่ำ clock สูง จะไม่ใช้ระบบปฏิบัติการเลย สำหรับงานแบบนั้น x86 ก็ไม่ถูกนำมาพิจารณาด้วย
      ระบบปฏิบัติการ แม้จะเป็น RTOS ก็รบกวนมากเกินไป ไม่รู้ว่าการเปลี่ยนแปลงนี้จะเปลี่ยนอะไรได้บ้าง แต่ทั้งนี้ขึ้นอยู่กับแอปพลิเคชัน และกรณีที่ต้องการแค่ “เกือบเรียลไทม์” ก็มีมาก จึงอาจมีประโยชน์กับงานแบบนั้น
    • ถูกต้อง แต่สิ่งนี้คงไม่ได้ทำให้ความจำเป็นของ คอร์เฉพาะ หายไปอย่างมหัศจรรย์ อาจลงเอยด้วยการสั่ง scheduler ให้วางงานเรียลไทม์ที่ห้าม preempt ไว้บนคอร์ LITTLE เพียงคอร์เดียวเท่านั้น
  • การถกเถียงตรงนี้เน้นไปที่การแยกแยะระหว่างแอปพลิเคชันเรียลไทม์แบบ “hard” กับ “soft” ใน hard real-time มีแนวโน้มสูงว่าเราไม่อยากใช้ระบบปฏิบัติการทั่วไปอย่าง Linux ตั้งแต่แรก ส่วนใน soft real-time อย่างวิดีโอคอนเฟอเรนซ์หรือการเล่นเสียง ต่อให้กระตุกบ้างหรือเฟรมตกไปไม่กี่เฟรมก็ไม่ใช่เรื่องใหญ่
    ตรรกะคือ RT Linux จะเป็นโซลูชันที่ทรงพลังสำหรับงาน soft real-time แบบนั้น แต่กรณีใช้งานแบบ soft ที่เสนอมาก็ทำได้ด้วย embedded Linux อยู่แล้วในตอนนี้ ไม่ใช่ว่าการเล่นวิดีโอหรือเสียงแบบ latency ต่ำด้วยซอฟต์แวร์เคยเป็นไปไม่ได้ และเมื่อ 20 ปีก่อนก็ทำได้แล้ว
    ปัญหาเกิดขึ้นเมื่อระบบที่ยุ่งมากมี I/O ที่ preempt ไม่ได้แทรกเข้ามาบ่อย ๆ แต่ในสภาพแวดล้อม embedded กรณีแบบนั้นพบได้ยาก การทำให้เคอร์เนล preempt ได้เต็มรูปแบบและให้การควบคุม scheduling มากขึ้นมีเหตุผลที่น่าเชื่อถือ แต่เหตุผลนั้นไม่ค่อยเกี่ยวกับการที่ Linux ควรแทนที่ระบบปฏิบัติการเรียลไทม์ขนาดเล็กที่สุดหรือโค้ด bare-metal
    มันใกล้เคียงกับการดูแลสุขอนามัยที่ดีมากกว่า และทำให้ได้ระบบปฏิบัติการที่ทำงานได้ดีขึ้นเมื่อมีโหลด แม้กับแอปพลิเคชันที่ไม่ใช่เรียลไทม์

  • เป็นข่าวดี แต่แม้เคอร์เนล Linux จะกลายเป็นแบบเรียลไทม์แล้ว ฮาร์ดแวร์ก็มีโอกาสสูงที่จะยังไม่เป็นเรียลไทม์ เพราะแคชและเวทมนตร์ซับซ้อนภายใน CPU
    ฮาร์ดแวร์ขนาดใหญ่และซับซ้อนไม่เหมาะกับเรียลไทม์จริง ๆ ดังนั้น AbsInt และเครื่องมือวิเคราะห์เวลาทำงานกรณีเลวร้ายที่สุด (WCET) จึงมักจัดการกับสถาปัตยกรรม CPU ที่เรียบง่ายเป็นหลัก 8051 น่าจะอยู่ยงคงกระพันไปตลอดจริง ๆ อนึ่ง ยังมี Zephyr RTOS ด้วย

    • เท่าที่รู้ ฟีเจอร์ของ CPU สมัยใหม่ไม่ได้ขัดขวางการใช้งานแบบเรียลไทม์ หากอะไรก็ตามมีขอบเขตบนและอนุมานได้ ก็สามารถใช้สร้างระบบเรียลไทม์ได้
      แค่สมมติสถานการณ์อย่างไม่มี cache hit เลยและอยู่ภายใต้โหลดสูงสุด เป็นต้น ถ้ากำหนดขอบเขตบนของเวลาที่ใช้ได้ก็ไม่มีปัญหา
    • ผมคิดว่ามันค่อนข้างมีประโยชน์บนบอร์ดระดับไมโครคอนโทรลเลอร์ “ใหญ่ ๆ” อย่าง Raspberry Pi ที่นั่นมีวัฒนธรรมแบบเรียลไทม์อยู่ระดับหนึ่ง และแม้จะไม่ได้ bit banging ด้วย CPU โดยตรง เมื่อมองจากภายนอกทุกอย่างก็ยังตรงเวลา
      เช่น timer รับอินพุตจาก quadrature encoder แล้วส่ง interrupt เฉพาะตอน wrap หรือเชื่อมระบบ GPIO เข้ากับ DMA เพื่อสตรีมหน่วยความจำไปยังขา output โดยไม่ต้องให้ CPU เข้ามาเกี่ยวข้อง ก็ทำได้ จะสตรีมไป DAC หรือทำ DMA transfer จาก ADC ไปยังหน่วยความจำก็ได้ สิ่งเหล่านี้มักเลี่ยงแคชเพื่อให้ได้ latency ที่คาดการณ์ได้
    • SpaceX ใช้ โปรเซสเซอร์ x86 ในจรวด เฮลิคอปเตอร์โดรนขนาดเล็กที่ NASA ส่งไปดาวอังคารก็ใช้คอร์ ARM ที่ “ค่อนข้างใหญ่” พอจะรัน Android รุ่นเก่าได้
    • คำกล่าวว่าฮาร์ดแวร์ขนาดใหญ่และซับซ้อนไม่เหมาะกับเรียลไทม์จริง ๆ นั้นไม่จำเป็นต้องถูกเสมอไป มีคอร์เรียลไทม์ระดับสูงอย่าง Arm Cortex-R82 อยู่
      ในความเป็นจริง ระบบเรียลไทม์จำนวนมากต้องประมวลผลและรวบรวมข้อมูลจากเซนเซอร์ที่เพิ่มขึ้นเรื่อย ๆ จึงทรงพลังขึ้นเรื่อย ๆ
    • 68000 ต่างหากคือราชาตัวจริงของเรียลไทม์
  • ตอนเป็นมือใหม่ ผมเคยเจอสัมภาษณ์งานน่าหงุดหงิดจำนวนมากที่ผู้สัมภาษณ์ไม่รู้ว่า เรียลไทม์ จริง ๆ แล้วคืออะไร หลายคนพลาดแนวคิดในบทความที่ว่า “และ latency ที่คาดการณ์ได้” และดูเหมือนจะคิดว่าเรียลไทม์แปลว่าแค่ “เร็ว”

    • ผมอยากตัดคำว่า “ต่ำสุด” ออกไปเลยด้วยซ้ำ แก่นของเรียลไทม์คือ งานมี ขอบเขตบน ที่คาดการณ์ได้ นั่นหมายความว่าโดยเฉลี่ยแล้วมันอาจช้ากว่าระบบที่ไม่ใช่เรียลไทม์ก็ได้
      ถ้าควบคุมระบบเบรกของรถยนต์ “latency เฉลี่ย 50ms แต่สูงสุด 80ms” อาจยอมรับได้ แต่ “latency เฉลี่ย 1ms แต่ยืดออกไปได้ไม่จำกัด และอาจใช้เวลาหลายวินาที” ยอมรับไม่ได้
    • อย่างคำพูดเก่า ๆ ที่ว่า “real time” ไม่ใช่ “real fast” แม้การแบ่งระหว่าง hard real-time กับ soft real-time จะทำให้เรื่องนี้พร่าเลือนไปบ้าง แต่ผมคิดว่านักพัฒนาซอฟต์แวร์จำนวนมากก็ยังไม่เข้าใจจริง ๆ ว่าเรียลไทม์คืออะไร
  • การทำ logging แบบ synchronous ก่อปัญหาอีกแล้ว ที่บริษัทเราเคยเจอเรื่องคล้ายกันเพราะ GLOG (ไลบรารี logging ของ Google) เช่น ถ้า stdout เป็นไฟล์ ก็อาจถูก block ที่ disk I/O ได้
    ตอนบริการของเราหยุดค้างเกิน 100ms สาเหตุ 90–99% คือ GLOG

    • ผมคุยเรื่อง logging แบบนี้กับเพื่อนร่วมงานบ่อย ๆ “เรามี API แบบ best-effort กับ API แบบรับประกันการส่งถึง” “ขอแบบรับประกันการส่งถึง!” “ถ้าอินเทอร์เฟซ logging แบบรับประกันการส่งถึง offline หรือช้า บริการจะหยุดชะงักนะ รับได้ไหม?” “ไม่ได้ หยุดชะงักไม่ได้!”
      “ถ้าเป็นสิ่งที่ต้องบันทึกเป็น log ให้ได้ แต่บันทึก log ไม่ได้ คุณจะทำอย่างไร?” ช่วงหลังผมแค่ชี้ไปที่ CAP theorem แล้วบอกว่า logging ก็เหมือน distributed system อื่น ๆ อาจเพราะมีบทความ Wikipedia ที่มีรูปสามเหลี่ยมกับคำว่า “theorem” คนเลยยอมรับกันมากขึ้น
    • เคยมีครั้งหนึ่งที่ syslog server หยุดทำงานแล้วทำให้สภาพแวดล้อม production ทั้งหมดหยุดนิ่ง เรากำลังส่ง log เข้าไปทาง TCP และการ block นั้นแพร่กระจายไปทั่ว production
      หลังจากนั้นจึงเปลี่ยนเป็นส่งผ่าน UDP เพราะเสีย log บางส่วนยังดีกว่าเสีย production ทั้งหมด
    • ในไลบรารี logging ของ $MSFT ก็เคยมีปัญหาประเภท “ไลบรารี logging ทำทุกอย่างพัง” เช่นกัน ลองนึกภาพว่ามี 100 เธรด แต่ละเธรดมี logging buffer ขนาด 300MB
      แน่นอนว่ามันถล่มหน่วยความจำ และทำให้เซิร์ฟเวอร์ crash แม้บน SKU ที่แพงที่สุดของ Azure App Service
    • ถ้าความพร้อมใช้งานของผลิตภัณฑ์ขึ้นอยู่กับ ±100ms แปลว่าการออกแบบผิดอย่างลึกซึ้ง และผมไม่คิดว่านั่นเป็นความผิดของไลบรารี logging ผู้ใช้คงไม่สนใจหรอกว่าหลังจากกดปุ่มแล้วจะใช้เวลาเพิ่มอีก 100ms กว่าจะเสร็จ
  • ทำให้นึกถึงอดีตขึ้นมาทันที ประมาณ 17–18 ปีก่อน ผมเคยคอมไพล์เคอร์เนลสำหรับ Debian ด้วย RT_PREEMPT เพื่อใช้กับอุปกรณ์วิทยาศาสตร์ที่ต้องการ timing ที่แน่นขึ้น
    latency และ jitter น่าประทับใจมาก หลังจากนั้นแทบไม่ได้คิดถึงมันอีกเลย แต่ถ้าทำแอปพลิเคชัน embedded ด้วย Raspberry Pi แล้วไม่อยากย้ายไปใช้ไมโครคอนโทรลเลอร์ที่ใช้ RTOS ก็น่าจะมีที่ให้ใช้เยอะ

    • ที่พูดถึง Raspberry Pi น่าสนใจดี วันสองวันก่อนผมเห็นบทความว่า RpiOS เริ่มและทำงานอยู่บน RTOS
      ผมเคยเห็นข้อเสนอมาก่อนว่าให้รัน Linux เป็น task หนึ่งของ RTOS จึงน่าสนใจเป็นพิเศษ แนวทางคือสิ่งที่ต้องมี deadline แบบ hard real-time ให้รันบน RTOS และไม่ให้ได้รับผลกระทบจาก latency ที่ระบบ virtual memory อาจก่อขึ้น ผมจำไม่ได้ว่านี่เป็นแค่ไอเดียหรือมีการ implement จริง และก็เคยเห็นการกล่าวถึงว่า RpiOS อยู่บน RTOS แค่ครั้งเดียว เลยสงสัยอยู่
  • สงสัยว่าสิ่งนี้มีความหมายอย่างไรสำหรับผู้ใช้ทั่วไป อยากรู้ว่าเป็นฟีเจอร์ที่เปิดใช้เฉพาะในสถานการณ์ที่เฉพาะเจาะจงมาก ๆ หรือว่าจะช่วยให้คนทั่วไปได้ใช้ ระบบที่ตอบสนองได้ดีกว่าเดิม ด้วยหรือไม่

    • เท่าที่เข้าใจ real-time ทำให้ระบบช้าลง การจะเป็น real-time ได้ต้องมีการจัดสรรเวลาให้ทุกอย่าง
      แต่ละงานจะได้รับงบประมาณ X และต้องไม่เกินนั้น ถ้ากรณีที่ดีที่สุดเร็ว แต่กรณีที่แย่ที่สุดช้า ก็หมายความว่าระบบต้องถือสมมติฐานตามกรณีที่แย่ที่สุดเสมอ
    • RT ไม่ได้ปรับปรุง latency เสมอไป แต่ให้ขอบเขตบนที่ตายตัวกับงานบางอย่าง อย่างไรก็ตาม งานที่จำเป็นเพื่อทำให้ RT เป็นไปได้สามารถปรับปรุง latency ในกรณีทั่วไปได้อย่างแน่นอน
      ตัวอย่างของการหลีกเลี่ยงการเรียก printk() แบบ synchronous นั้นตรงจุดพอดี และแม้ไม่เปิด RT ก็ควรช่วยปรับปรุง latency ภายใต้ภาระโหลดได้ ผมมองว่าเคอร์เนล RT ที่ถูก upstream เต็มรูปแบบจะไม่ทำงานต่างจากเคอร์เนลทั่วไป เว้นแต่จะรันโปรเซส RT จริง ๆ เหตุผลที่ใช้เวลานานในการ upstream คือจำเป็นต้องมีการประนีประนอมเพื่อทำให้ RT เป็นไปได้ และตามบทความบอกว่าตอนนี้การประนีประนอมแบบนั้นเหลือไม่มากแล้ว
    • ถ้า “ผู้ใช้ทั่วไป” หมายถึงผู้ใช้เดสก์ท็อป ก็ไม่ได้เปลี่ยนแปลงมากนัก แต่สำหรับ อุปกรณ์ฝังตัว อย่างระบบควบคุมอุตสาหกรรมและอุปกรณ์โทรคมนาคม นี่เป็นเรื่องใหญ่
      เพราะเมื่อจำเป็นต้องใช้ real-time scheduling ก็จะสามารถใช้เคอร์เนล mainline รุ่นล่าสุดได้
    • เท่าที่เข้าใจคือ Linux กลายเป็นตัวเลือกในสถานการณ์ที่ต้องใช้ RTOS สิ่งนี้มีไว้สำหรับระบบสำคัญอย่างการบินและอุปกรณ์การแพทย์ และแทบไม่มีผลต่อผู้ใช้ทั่วไป
    • ผู้ใช้ปลายทางบนเดสก์ท็อปที่พบได้บ่อยที่สุดซึ่งจะได้ประโยชน์คือคนที่ทำ งานด้านเสียง เพราะในงานนั้น latency โดยเฉพาะ jitter อาจเป็นปัญหาน่าปวดหัวพอสมควร
  • อยากรู้ว่าคิดอย่างไรกับ Xenomai[1] ใช้มาหลายปีโดยไม่มีปัญหา
    บน BeagleBone Black ปกติจะได้ jitter ระดับหลายร้อยนาโนวินาที และถือว่าเป็น real-time แบบ “hard” สามารถ schedule งานเป็นคาบในระดับหลายสิบไมโครวินาทีได้โดยไม่เคยพลาดเลย
    ต่างจาก Real-Time Linux ที่พยายามทำให้ Linux เอง preempt ได้ Xenomai โดยแก่นแล้วเป็นเคอร์เนลของตัวเอง และรัน Linux เป็นงานหนึ่งที่อยู่ด้านบน มันมี ABI ให้รันงานที่ผู้ใช้สร้างขึ้นขนานกับ Linux หรือที่ priority สูงกว่าได้ ตัวอย่างเช่นสามารถเลี่ยงปัญหา printk() ได้ เพราะ Xenomai ไม่สนใจและยอม context switch ออกจาก printk เพื่อรันงานของผู้ใช้
    ข้อเสียคือภายใน context ของ Xenomai จะเรียก system call ทั่วไปไม่ได้ จริง ๆ ก็ทำได้ แต่แน่นอนว่าจะทำลายโมเดล real-time ตัวอย่างเช่นถ้าเรียก printf() หรือ malloc() ภายในงาน Xenomai มันจะไม่สามารถถูก preempt ได้ ABI ของ Xenomai จำลองสิ่งที่น่าจะจำเป็นในแง่ system call ไว้ให้มากที่สุด และถ้าคุณพอใจที่จะจัดการ heap allocation โดยตรงเอง มันก็ทำงานได้ดีมาก
    [1]: https://xenomai.org/