การศึกษาลินุกซ์เคอร์เนล ด้วย Raspberry Pi: ลินุกซ์เคอร์เนลโมดูล

By | April 12, 2017

การแบ่งชนิดของเคอร์เนล

โมโนลิทิก เคอร์เนล (Monolithic kernel) เป็นเคอร์เนลที่รวมการทำงานทุกส่วนไว้ด้วยกันทั้งหมด ทำให้การทำงานร่วมกันระหว่างส่วนต่างๆรวดเร็ว เพราะไม่มีโอเวอร์เฮดของการสื่อสารระหว่างส่วนต่างๆในข้อมูลที่ส่งให้กัน ทำให้การประมวลผลเร็ว แต่มีข้อเสียหลักคือเมื่อเคอร์เนลล่มจะทำให้ระบบทั้งหมดจะหยุดการทำงานทันที

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

ลินุกซ์เป็นระบบปฏิบัติการแบบโมโนลิทิก เคอร์เนล (Monolithic kernel) คือรวมการทำงานทุกส่วนอยู่ในไบนารี่เดียวกัน แต่รองรับการทำงานแบบโมดูลในการปรับเปลี่ยน/เพิ่มเติม ความสามารถในขณะทำงานได้

ลินุกซ์เคอร์เนลโมดูล

คือโค้ดส่วนเล็กๆที่สามารถโหลดเข้าไปในเคอร์เนลและถอดออกจากเคอร์เนลได้โดยที่ไม่จำเป็นต้องรีบูตระบบ จุดประสงค์เพื่อให้สามารถปรับเปลี่ยนความสามารถของเคอร์เนลได้ตามความต้องการ ซึ่งช่วยให้มีลินุกซ์เคอร์เนลความยืดหยุ่นในการทำงานมากขึ้น แม้ว่าจะเป็นโมโนลิทิกเคอร์เนล

ตัวอย่างเช่น หากเรามีระบบที่ไม่ต้องการให้ผู้ใช้งานสามารถแก้ไข้ข้อมูลที่อยู่บนแฟลซเมมโมรี่ เราอาจปรับแต่งให้ตัวแฟลซเมมโมรี่ไดร์เวอร์ทำได้แค่การอ่านข้อมูล แต่เนื่องจากผู้ผลิตก็ยังจำเป็นต้องใช้ฟังก์ชั่นการเขียนแฟลซเมมโมรี่ ในขั้นตอนการโปรแกรมเฟิรมแวร์ลงไปในตัวอุปกรณ์ กรณีนี้เราอาจต้องแยกแฟลซเมมโมรี่ไดร์เวอร์ ออกมาเป็นโมดูลภายนอก สองโมดูล คือโมดูลที่สามารถเขียนแฟลซได้ และโมดูลที่ทำได้เฉพาะอ่านแฟลซเมมโมรี่ เมื่อต้องการโปรแกรมเฟิรมแวร์ลงไปในตัวอุปกรณ์ จึงค่อยโหลดโมดูลที่สามารถเขียนแฟลซได้ ส่วนเฟิรมแวร์ที่ติดไปให้ผู้ใช้ก็จะมีแต่โมดูลที่ทำได้เฉพาะอ่านแฟลซเมมโมรี่ จากตัวอย่างนี้จะเห็นว่า การใช้ความสามารถของโมดูลมาช่วยปรับเปลี่ยนความสามารถของเคอร์เนล จะทำให้มีความสะดวกในทำงานขึ้นมาก

ประโยชน์ทางอ้อมอีกอย่างหนึ่งของการใช้ลินุกซ์เคอร์เนลโมดูล คือการนำมาใช้ในขั้นตอนการพัฒนาลินุกซ์ดีไวซ์ไดร์เวอร์ ซึ่งจะช่วยให้นักพัฒนาลดเวลาในการทดสอบโค้ดเนื่องจากไม่ต้องบูตระบบทุกครั้งเวลาที่จะทดสอบไดร์เวอร์ เพราะสามารถถอดไดร์เวอร์โมดูลเก่าแล้วโหลดโมดูลใหม่เพื่อทดสอบได้ทันที เมื่อได้โมดูลที่ทำงานได้สมบูรณ์แล้วจึงเปลี่ยนไปเป็นแบบรวมไว้ในตัวเคอร์เนลภายหลังได้

ขอบเขตของคำว่าเคอร์เนลโมดูลจะจำกัดอยู่ที่ความสามารถในการโหลดเข้าไปในเคอร์เนลและถอดออกจากเคอร์เนลได้เท่านั้น ไม่ได้หมายรวมถึงว่า ลินุกซ์ดีไวซ์ไดร์เวอร์คือโมดูล เนื่องจากว่าดีไวซ์ไดร์เวอร์สามารถเขียนทั้งในรูปแบบรวมเข้าไว้ด้วยกัน (built-in) และแบบโมดูล (**จริงๆ เกี่ยวกับเรื่องนี้แอดมินเองก็ไม่อยากฟันธงเท่าไหร่นัก เพราะเมื่อได้ลองรีวิวเคอร์เนลโค้ดไปเรื่อยๆ ก็จะพบว่ามีการเขียนคอมเม้นในโค้ดของโมดูลเกี่ยวกับไดร์เวอร์ไว้หลายจุด จนเหมือนกับว่ามันคือเรื่องเดียวกัน)

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

แสดงตัวเลือกเกี่ยวกับการรองรับโมดูลของเคอร์เนลคอนฟิก

แสดงเมนูย่อยเกี่ยวกับการรองรับโมดูลของเคอร์เนลคอนฟิก

ตัวอย่างรูปแบบโค้ดของลินุกซ์เคอร์เนลโมดูล

อธิบายโค้ด
(emb.c: บรรทัด 25 – 31) เป็นฟังก์ชั่นแรกของโมดูล(initialization entry point) ที่จะถูกเรียกเมื่อ <1> โมดูลถูกโหลดเข้าไปในเคอร์เนล หรือ <2> ตอนที่เคอร์เนลรันฟังก์ชั่น do_initcalls() ในกรณีที่เป็นแบบ built-in

ในโค้ดตัวอย่างนี้แสดงเฉพาะการใช้ฟังก์ชั่น printk() พิมพ์ข้อความออกมาทางเคอร์เนลคอนโซลเท่านั้น แต่จุดประสงค์ของฟังก์ชั่นนี้จริงๆคือใช้สำหรับสร้างส่วนที่จำเป็นในการทำงานของโมดูล เช่นการลงทะเบียนกับเคอร์เนลเป็น อุปกรณ์แบบลำดับ (character device) หรือการสร้างแพลตฟอร์มดีไวซ์ (platform device) ก่อนจะไปสู่ขั้นตอนการตรวจสอบอุปกรณ์ (device probing)

ฟังก์ชั่นนี้จะถูกเรียกโดย มาโคร module_init() อีกทีหนึ่ง ทำให้ชื่อของฟังก์ชั่นสามารถเปลี่ยนได้ตามใจผู้พัฒนา

ฟังก์ชั่นที่ถูกเรียกโดยมาโคร module_init() นี้จำเป็นต้องคืนค่า 0 เท่านั้น หากคืนค่าอื่นที่ไม่ใช่ 0 เคอร์เนลจะตีความหมายว่าขั้นตอนเริ่มต้นทำไม่สำเร็จ และจะยกเลิกกระบวนการโหลดโมดูลนี้ ซึ่งเราสามารถใช้เงื่อนไขนี้ ทำให้โค้ดตัดสินใจได้ว่าโมดูลต้องถูกโหลดไปทำงานหรือไม่ หากไม่มีอุปกรณ์ตามที่กำหนดในระบบ

(emb.c: บรรทัด 33 – 36) เป็นฟังก์ชั่นสุดท้ายของโมดูล(exit entry point) ที่จะถูกเรียกเมื่อโมดูลถูกถอดจากเคอร์เนล และจะไม่ถูกเรียกให้ทำงาน ในกรณีที่เป็นถูกคอมไพล์เป็นแบบ built-in

(emb.c: บรรทัด 38) มาโคร MODULE_AUTHOR สำหรับเครดิตเกี่ยวกับผู้เขียนโมดูล โดยอาจมีหรือไม่มีที่อยู่อีเมล์ และสามารถประกาศชื่อผู้เขียนโมดูลได้มากกว่าหนึ่งคน

(emb.c: บรรทัด 39) มาโคร MODULE_DESCRIPTION เป็นคำอธิบายเกี่ยวกับหน้าที่โมดูล

(emb.c: บรรทัด 40) มาโคร MODULE_LICENSE เป็นส่วนประกาศชนิดของใบอนุญาต สำหรับเคอร์เนล รุ่น 4.x สามารถ รองรับชนิดของใบอนุญาต ได้ตามตารางด้านล่าง

ชนิดของใบอนุญาตคำอธิบาย
GPLGNU Public License v2 or later
GPL v2GNU Public License v2
GPL and additional rightsGNU Public License v2 rights and more
Dual BSD/GPLGNU Public License v2 or BSD license choice
Dual MIT/GPLGNU Public License v2 or MIT license choice
Dual MPL/GPLGNU Public License v2 or Mozilla license choice
ProprietaryNon free products

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

Leave a Reply

Your email address will not be published. Required fields are marked *