สวัสดีทุกคน เรากำลังแบ่งปันส่วนที่สองของสิ่งพิมพ์ "ระบบไฟล์เสมือนใน Linux: เหตุใดจึงมีความจำเป็นและทำงานอย่างไร" คุณสามารถอ่านส่วนแรกได้
วิธีตรวจสอบ VFS โดยใช้เครื่องมือ eBPF และ bcc
วิธีที่ง่ายที่สุดในการทำความเข้าใจว่าเคอร์เนลทำงานอย่างไรกับไฟล์ sysfs
คือการดูในทางปฏิบัติ และวิธีที่ง่ายที่สุดในการรับชม ARM64 คือการใช้ eBPF eBPF (ย่อมาจาก Berkeley Packet Filter) ประกอบด้วยเครื่องเสมือนที่ทำงานอยู่ query
) จากบรรทัดคำสั่ง แหล่งที่มาของเคอร์เนลบอกผู้อ่านว่าเคอร์เนลสามารถทำอะไรได้บ้าง การรันเครื่องมือ eBPF บนระบบที่โหลดจะแสดงสิ่งที่เคอร์เนลกำลังทำอยู่จริง
โชคดีที่การเริ่มต้นใช้งาน eBPF นั้นค่อนข้างง่ายด้วยความช่วยเหลือจากเครื่องมือต่างๆ bcc
เป็นสคริปต์ Python ที่มีการแทรกโค้ด C เล็กน้อย ซึ่งหมายความว่าใครก็ตามที่คุ้นเคยกับทั้งสองภาษาสามารถแก้ไขได้อย่างง่ายดาย ใน bcc/tools
มีสคริปต์ Python 80 สคริปต์ ซึ่งหมายความว่านักพัฒนาหรือผู้ดูแลระบบส่วนใหญ่สามารถเลือกสิ่งที่เหมาะสมสำหรับการแก้ปัญหาได้
หากต้องการทราบแนวคิดอย่างผิวเผินเป็นอย่างน้อยว่า VFS ทำงานอย่างไรบนระบบที่กำลังรันอยู่ ให้ลอง vfscount
หรือ vfsstat
. นี่จะแสดงสมมติว่ามีการโทรหลายสิบสาย vfs_open()
และ “เพื่อนของเขา” เกิดขึ้นทุกวินาทีอย่างแท้จริง
vfsstat.py
เป็นสคริปต์ Python ที่มีการแทรกโค้ด C ซึ่งนับการเรียกใช้ฟังก์ชัน VFS เพียงอย่างเดียว
เรามายกตัวอย่างเล็กน้อยและดูว่าเกิดอะไรขึ้นเมื่อเราเสียบแฟลชไดรฟ์ USB เข้ากับคอมพิวเตอร์และระบบตรวจพบ
การใช้ eBPF คุณสามารถดูสิ่งที่เกิดขึ้นได้
/sys
เมื่อเสียบแฟลชไดรฟ์ USB ตัวอย่างที่ง่ายและซับซ้อนแสดงไว้ที่นี่
ในตัวอย่างที่แสดงข้างต้น bcc
เครื่องมือ sysfs_create_files()
. เราเห็นสิ่งนั้น sysfs_create_files()
เปิดตัวโดยใช้ kworker
สตรีมเพื่อตอบสนองต่อความจริงที่ว่าใส่แฟลชไดรฟ์แล้ว แต่ไฟล์ใดที่ถูกสร้างขึ้น? ตัวอย่างที่สองแสดงให้เห็นถึงพลังของ eBPF ที่นี่ trace.py
พิมพ์เคอร์เนล backtrace (-K ตัวเลือก) และชื่อของไฟล์ที่สร้างขึ้น sysfs_create_files()
. การแทรกคำสั่งเดี่ยวคือโค้ด C ที่มีสตริงรูปแบบที่จดจำได้ง่ายซึ่งจัดทำโดยสคริปต์ Python ที่รัน LLVM คอมไพเลอร์ทันเวลา. มันรวบรวมบรรทัดนี้และดำเนินการในเครื่องเสมือนภายในเคอร์เนล ลายเซ็นฟังก์ชั่นเต็มรูปแบบ sysfs_create_files ()
ต้องทำซ้ำในคำสั่งที่สองเพื่อให้สตริงรูปแบบสามารถอ้างอิงถึงพารามิเตอร์ตัวใดตัวหนึ่งได้ ข้อผิดพลาดในโค้ด C ส่วนนี้ส่งผลให้เกิดข้อผิดพลาดที่จดจำได้จากคอมไพเลอร์ C ตัวอย่างเช่น หากละเว้นพารามิเตอร์ -l คุณจะเห็นข้อความ "ไม่สามารถคอมไพล์ข้อความ BPF" นักพัฒนาที่คุ้นเคยกับ C และ Python จะพบเครื่องมือเหล่านี้ bcc
ง่ายต่อการขยายและเปลี่ยนแปลง
เมื่อเสียบไดรฟ์ USB เคอร์เนล backtrace จะแสดงว่า PID 7711 เป็นเธรด kworker
ที่สร้างไฟล์ «events»
в sysfs
. ตามสายจาก sysfs_remove_files()
จะแสดงว่าการถอดไดรฟ์ส่งผลให้ไฟล์ถูกลบ events
ซึ่งสอดคล้องกับแนวคิดทั่วไปของการนับอ้างอิง ขณะเดียวกันก็ชม. sysfs_create_link ()
ด้วย eBPF ขณะเสียบไดรฟ์ USB จะแสดงว่ามีการสร้างลิงก์สัญลักษณ์อย่างน้อย 48 รายการ
แล้วไฟล์เหตุการณ์มีไว้เพื่ออะไร? การใช้งาน disk_add_events ()
และอย่างใดอย่างหนึ่ง "media_change"
หรือ "eject_request"
สามารถบันทึกลงในไฟล์เหตุการณ์ได้ ที่นี่เลเยอร์บล็อกเคอร์เนลแจ้งให้ผู้ใช้ทราบว่ามี "ดิสก์" ปรากฏขึ้นและดีดออก โปรดทราบว่าวิธีการวิจัยนี้ให้ข้อมูลที่เป็นประโยชน์เพียงใดโดยการใส่ไดรฟ์ USB เมื่อเปรียบเทียบกับการพยายามค้นหาว่าสิ่งต่างๆ ทำงานจากแหล่งที่มาเพียงอย่างเดียวได้อย่างไร
ระบบไฟล์รูทแบบอ่านอย่างเดียวเปิดใช้งานอุปกรณ์ฝังตัว
แน่นอนว่าไม่มีใครปิดเซิร์ฟเวอร์หรือคอมพิวเตอร์ของตนโดยการดึงปลั๊กออกจากเต้ารับ แต่ทำไม? เนื่องจากระบบไฟล์ที่ติดตั้งบนอุปกรณ์จัดเก็บข้อมูลทางกายภาพอาจมีการเขียนล่าช้า และโครงสร้างข้อมูลที่บันทึกสถานะอาจไม่ซิงโครไนซ์กับการเขียนไปยังที่จัดเก็บข้อมูล เมื่อสิ่งนี้เกิดขึ้น เจ้าของระบบต้องรอจนกระทั่งการบู๊ตครั้งถัดไปจึงจะเปิดใช้งานยูทิลิตี้ได้ fsck filesystem-recovery
และในกรณีที่เลวร้ายที่สุดคือการสูญเสียข้อมูล
อย่างไรก็ตาม เราทุกคนทราบดีว่าอุปกรณ์ IoT จำนวนมาก รวมถึงเราเตอร์ ตัวควบคุมอุณหภูมิ และรถยนต์ ต่างใช้ Linux ในปัจจุบัน อุปกรณ์เหล่านี้จำนวนมากมีส่วนติดต่อผู้ใช้เพียงเล็กน้อยหรือไม่มีเลย และไม่มีวิธีใดที่จะปิด "หมดจด" ลองนึกภาพการสตาร์ทรถโดยที่แบตเตอรี่หมดในขณะที่มีไฟจ่ายให้กับชุดควบคุม fsck
ในที่สุดเครื่องยนต์ก็เริ่มทำงานเมื่อใด? และคำตอบนั้นง่าย อุปกรณ์ฝังตัวอาศัยระบบไฟล์รูท ro-rootfs
(ระบบไฟล์รูทแบบอ่านอย่างเดียว))
ro-rootfs
ให้ประโยชน์มากมายที่ไม่ชัดเจนกว่าความถูกต้อง ข้อดีประการหนึ่งคือมัลแวร์ไม่สามารถเขียนถึงได้ /usr
หรือ /lib
หากไม่มีกระบวนการ Linux สามารถเขียนที่นั่นได้ อีกประการหนึ่งคือระบบไฟล์ที่ไม่เปลี่ยนรูปแบบส่วนใหญ่มีความสำคัญอย่างยิ่งต่อการสนับสนุนภาคสนามของอุปกรณ์ระยะไกล เนื่องจากบุคลากรฝ่ายสนับสนุนอาศัยระบบท้องถิ่นซึ่งมีชื่อเหมือนกันกับระบบภาคสนาม บางทีประโยชน์ที่สำคัญที่สุด (แต่ร้ายกาจที่สุด) ก็คือ ro-rootfs บังคับให้นักพัฒนาตัดสินใจว่าออบเจ็กต์ระบบใดที่จะไม่เปลี่ยนรูปในขั้นตอนการออกแบบของระบบ การทำงานกับ ro-rootfs อาจเป็นเรื่องที่น่าอึดอัดใจและเจ็บปวด เนื่องจากตัวแปร const มักจะอยู่ในภาษาการเขียนโปรแกรม แต่ประโยชน์ของตัวแปรเหล่านี้จะช่วยพิสูจน์ค่าใช้จ่ายเพิ่มเติมได้อย่างง่ายดาย
การสร้าง rootfs
อ่านอย่างเดียวต้องใช้ความพยายามเป็นพิเศษสำหรับนักพัฒนาแบบฝัง และนี่คือจุดที่ VFS เข้ามามีบทบาท Linux กำหนดให้ไฟล์ต้องอยู่ใน /var
สามารถเขียนได้ และนอกจากนี้ แอปพลิเคชันยอดนิยมจำนวนมากที่ใช้ระบบฝังตัวจะพยายามสร้างการกำหนดค่า dot-files
в $HOME
. วิธีแก้ปัญหาหนึ่งสำหรับไฟล์การกำหนดค่าในโฮมไดเร็กตอรี่คือการสร้างล่วงหน้าและสร้างไฟล์เหล่านั้น rootfs
. สำหรับ /var
วิธีหนึ่งที่เป็นไปได้คือติดตั้งบนพาร์ติชันที่เขียนได้แยกต่างหากในขณะที่ /
ติดตั้งแบบอ่านอย่างเดียว อีกทางเลือกหนึ่งที่ได้รับความนิยมคือการใช้การผูกหรือการซ้อนทับ
ตัวยึดแบบเชื่อมโยงและวางซ้อนกันได้ ใช้งานโดยคอนเทนเนอร์
การดำเนินการคำสั่ง man mount
เป็นวิธีที่ดีที่สุดในการเรียนรู้เกี่ยวกับการเมานต์แบบผูกและซ้อนทับได้ ซึ่งช่วยให้นักพัฒนาและผู้ดูแลระบบสามารถสร้างระบบไฟล์ในพาธหนึ่งแล้วเปิดเผยไปยังแอปพลิเคชันในอีกพาธหนึ่ง สำหรับระบบฝังตัว นี่หมายถึงความสามารถในการจัดเก็บไฟล์ /var
บนแฟลชไดรฟ์แบบอ่านอย่างเดียว แต่มีเส้นทางการซ้อนทับหรือลิงก์ที่เชื่อมโยงได้ tmpfs
в /var
เมื่อโหลดจะช่วยให้แอปพลิเคชันสามารถเขียนบันทึกที่นั่นได้ (เขียนลวก ๆ) ครั้งต่อไปที่คุณเปิดการเปลี่ยนแปลงไปที่ /var
จะหายไป การติดตั้งแบบซ้อนทับจะสร้างความสัมพันธ์ระหว่างกัน tmpfs
และระบบไฟล์ที่ซ่อนอยู่และช่วยให้คุณทำการเปลี่ยนแปลงที่ชัดเจนกับไฟล์ที่มีอยู่ในนั้น ro-tootf
ในขณะที่การเมานต์แบบผูกมัดสามารถทำให้อันใหม่ว่างเปล่าได้ tmpfs
โฟลเดอร์ที่มองเห็นได้ว่าเขียนได้ ro-rootfs
วิธี ในขณะที่ overlayfs
นี่คือสิ่งที่ถูกต้อง (proper
) ประเภทระบบไฟล์ มีการปรับใช้การเมานต์แบบผูกได้
จากคำอธิบายของโอเวอร์เลย์และตัวยึดแบบเชื่อมต่อได้ ไม่มีใครแปลกใจเลย mountsnoop
จาก bcc
.
โทรศัพท์ system-nspawn
เริ่มคอนเทนเนอร์ขณะทำงาน mountsnoop.py
.
มาดูกันว่าเกิดอะไรขึ้น:
ยิง mountsnoop
ในขณะที่คอนเทนเนอร์กำลัง "บูท" แสดงว่ารันไทม์ของคอนเทนเนอร์นั้นขึ้นอยู่กับการเมานต์ที่กำลังเชื่อมโยงอย่างมาก (แสดงเฉพาะจุดเริ่มต้นของเอาต์พุตแบบยาวเท่านั้น)
ที่นี่ systemd-nspawn
ให้ไฟล์ที่เลือกมา procfs
и sysfs
โฮสต์ไปยังคอนเทนเนอร์เป็นเส้นทางไปยังคอนเทนเนอร์นั้น rootfs
... นอกเหนือจาก MS_BIND
แฟล็กที่ตั้งค่าการโยงการเมาท์ แฟล็กอื่น ๆ บนการเมาท์จะกำหนดความสัมพันธ์ระหว่างการเปลี่ยนแปลงในเนมสเปซโฮสต์และคอนเทนเนอร์ ตัวอย่างเช่น การเมานต์ที่เชื่อมโยงสามารถข้ามการเปลี่ยนแปลงไปได้ /proc
и /sys
ลงในคอนเทนเนอร์หรือซ่อนไว้ตามการโทร
ข้อสรุป
การทำความเข้าใจการทำงานภายในของ Linux อาจดูเหมือนเป็นงานที่เป็นไปไม่ได้ เนื่องจากตัวเคอร์เนลเองประกอบด้วยโค้ดจำนวนมหาศาล โดยละทิ้งแอปพลิเคชันพื้นที่ผู้ใช้ Linux และอินเทอร์เฟซการเรียกระบบในไลบรารี C เช่น glibc
. วิธีหนึ่งในการสร้างความคืบหน้าคือการอ่านซอร์สโค้ดของระบบย่อยเคอร์เนลเดียว โดยเน้นที่การทำความเข้าใจการเรียกระบบและส่วนหัวของพื้นที่ผู้ใช้ รวมถึงอินเทอร์เฟซเคอร์เนลภายในหลัก เช่น ตาราง file_operations
. การทำงานของไฟล์มีหลักการ "ทุกอย่างเป็นไฟล์" ทำให้การจัดการไฟล์เป็นเรื่องสนุกเป็นพิเศษ ไฟล์ต้นฉบับเคอร์เนล C ในไดเร็กทอรีระดับบนสุด fs/
นำเสนอการนำระบบไฟล์เสมือนไปใช้ ซึ่งเป็นเลเยอร์ wrapper ที่ให้ความเข้ากันได้ในวงกว้างและค่อนข้างง่ายระหว่างระบบไฟล์ยอดนิยมและอุปกรณ์จัดเก็บข้อมูล การลิงก์และการติดตั้งแบบโอเวอร์เลย์ผ่านเนมสเปซ Linux ถือเป็นความมหัศจรรย์ของ VFS ที่ทำให้การสร้างคอนเทนเนอร์แบบอ่านอย่างเดียวและระบบไฟล์รูทเป็นไปได้ เมื่อรวมกับการตรวจสอบซอร์สโค้ด เครื่องมือหลักของ eBPF และอินเทอร์เฟซ bcc
ทำให้การสำรวจแกนหลักง่ายกว่าที่เคย
เพื่อน ๆ เขียนบทความนี้มีประโยชน์กับคุณไหม? บางทีคุณอาจมีความคิดเห็นหรือข้อสังเกต? และผู้ที่สนใจหลักสูตร Linux Administrator