Category Archives: Article

All of the our articles.

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

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

การศึกษาลินุกซ์เคอร์เนล ด้วย Raspberry Pi: ทำความรู้จักกับ device tree

Device Tree คืออะไร คนที่เคยเล่น Raspbian OS image คงเคยเห็นไฟล์ .dtb ที่อยู่บน sd card และต้องใช้คู่กับลีนุกซ์เคอร์เนลใน ขั้นตอนการบูตระบบ แต่อาจจะยังไม่รู้ว่ามันคืออะไร ไฟล์ .dtb นี้คือ Device Tree Blob (DTB) หรือ Flattened Device Tree (FDT) ใช้สำหรับการระบุรายละเอียดของฮาร์ดแวร์ของบอร์ดใหักับลินุกซ์เคอร์เนลในช่วงการบูต บน wikipedia อธิบายแบบไม่ยาวมากไว้ว่า “The device tree is a data structure for describing hardware, which originated from Open Firmware. The data structure can hold any kind of data… Read More »

การศึกษาลินุกซ์เคอร์เนล ด้วย Raspberry Pi: พื้นฐานการทำงานของลินุกซ์ดีไวซ์ไดร์เวอร์ (ตอนที่ 3)

สองตอนก่อนหน้าเป็นการพูดถึงสิ่งที่เกี่ยวข้องกับการทำงานระหว่างดีไวซ์ไดร์เวอร์และโปรแกรมฝั่งยูสเซอร์สเปซ ส่วนในตอนนี้เราจะพูดถึงการเชื่อมโยงทั้งสองส่วนเข้าหากัน ซึ่งจะทำให้เราเข้าใจภาพรวมทั้งหมด และเป็นพื้นฐานสู่การเขียนและรวมถึงการดีบัก ลินุกซ์ดีไวซ์ไดร์เวอร์ การอินเตอร์เฟสระหว่างดีไวซ์ไดร์เวอร์และโปรแกรมฝั่งยูสเซอร์สเปซ เนื่องจากว่าเหตุผลเรื่องความปลอดภัยของระบบลินุกซ์แนวคิดเรื่องการติดต่อกับอุปกรณ์บนระบบจึงต้องทำผ่านระบบแฟ้มซึ่งทำให้การควบคุมสิทธิในการเข้าถึงสามารถใช้รูปแบบเดียวกับไฟล์ทั่วๆไปได้ (permission) ข้อดีของวิธีนี้คือทำให้เข้าใจง่ายแยกหน้าที่ของส่วนต่างๆได้อย่างชัดเจน แต่ก็มีข้อเสียคือทำให้มีโอเวอร์เฮด(เวลา)ของการเข้าถึงของโปรแกรมฝั่งยูสเซอร์สเปซไปยังตัวอุปกรณ์ ซึ่งถ้าเทียบกับการเขียนโปรแกรมติดต่ออุปกรณ์แบบที่ไม่มีระบบปฎิบัติการจะเห็นว่าแบบหลังทำงานได้เร็วกว่า แต่โดยทั่วๆไปเวลาที่เสียไปส่วนนี้อาจไม่สนใจก็ได้หากไม่ใช่งานแบบเรียลไทม์ การทำงานผ่านระบบแฟ้มนี้ทำให้เรานำความรู้เกี่ยวกับการทำงานกับไฟล์ในภาษาซี มาใช้ได้ทันที เนื่องจากเหมือนกันเป๊ะ file operations open(),read(),write() และ close()

การศึกษาลินุกซ์เคอร์เนล ด้วย Raspberry Pi: พื้นฐานการทำงานของลินุกซ์ดีไวซ์ไดร์เวอร์ (ตอนที่ 2)

จากตอนที่แล้วที่เราพูดถึง device file โดยเริ่มทำความรู้จักกับ block device และ character device ในตอนนี้ เราก็จะมาทำความเข้าใจถึงคุณสมบัติพิเศษอื่นๆ ของไฟล์ชนิดนี้ หมายเลขหลัก และหมายเลขรอง ของอุปกรณ์ (Device major and minor number) หมายเลขหลัก(major number) และหมายเลขรอง(minor number)ของอุปกรณ์ คือเลขสองชุดที่คั่นด้วยเครื่องหมายคอมม่า ดังที่แสดงในตารางด้านบน ใช้สำหรับเป็นตัวแทนของอุปกรณ์ในฝั่งเคอร์เนลสเปซ เนื่องจากว่าการทำงานกับตัวเลขนั้น โปรแกรมคอมพิวเตอร์จะทำงานได้ง่ายกว่า การทำงานกับตัวอักษร ลองนึกถึงการเขียนโค้ดให้ไล่เช็คกรณีที่ ต้องการหาว่าไดร์เวอร์ตัวไหนต้องมาทำงานให้กับอุปกรณ์นั้น คงจะพอเห็นภาพว่า การค้นหาด้วยตัวเลขมันเหมาะในแง่ของการเขียนโปรแกรมกว่าการค้นหาด้วยตัวอักษรยังไง เรื่องนี้รวมถึงความเร็วในการทำงานของโค้ดด้วย ส่วนในมุมของผู้ใช้ หากต้องมาจดจำว่าอุปกรณ์ใดคือเลขอะไร ก็คงไม่เหมาะเท่าไร เนื่องจากสมองมนุษย์นั้นจดจำตัวอักษรได้ดีกว่าตัวเลข

การศึกษาลินุกซ์เคอร์เนล ด้วย Raspberry Pi: พื้นฐานการทำงานของลินุกซ์ดีไวซ์ไดร์เวอร์ (ตอนที่ 1)

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

ทดลอง u-boot porting แบบเบื้องต้น บน QEMU mini2440

จากประสบการณ์ในการทำงานด้านการออกแบบระบบสมองกลฝังตัวของแอดมินที่ผ่านมา เราอาจจะเจอกับงานที่มีการเปลี่ยนแปลงไปจากบอร์ดอ้างอิง (Reference design) เนื่องจากเหตุผลเรื่องราคา ความต้องการของลูกค้า อายุของผลิตภัณฑ์ในตลาด แนวโน้มของเทคโนโลยี่ หรือข้อจำกัดเรื่องกฎหมายสิ่งแวดล้อม โดยเฉพาะอุปกรณ์พวกหน่วยความจำต่างๆ เช่น nand flash หรือ SDRAM ซึ่งอาจจะทำให้เฟิร์มแวร์ ของบอร์ดอ้างอิงทำงานไม่ได้บนบอร์ดต้นแบบ ทำให้เราต้องมีการแก้ไขโค้ดต้นฉบับให้สามารถทำงานกับบอร์ดที่ออกแบบมาใหม่ได้ การแก้ไขนี้จะมากน้อยขนาดไหน อยู่ที่โชคของแต่ละคนนะครับ ในบางงานเราอาจจะได้เป็นนักพัฒนากลุ่มแรกในโลกที่ได้ทำงานกับอุปกรณ์นั้น ข้อดีคือ มันท้าทายมาก ข้อเสียคือมันใหม่จนเราไม่สามารถปรึกษาใครได้เลย แม้กระทั่งกับ FAE (Field Application Engineer)

ขั้นตอนการทำงานของโปรแกรมยูบูต (u-boot execution flow)

หลังจากที่ได้ทำความเข้าใจกับขั้นตอนการสร้างยูบูต (compile-time) แล้ว เราลองมาทำความเข้าใจกับขั้นตอนการทำงานของโปรแกรมยูบูต (run-time) กันต่อเลยดีกว่า โดยทั่วไปสามารถแบ่งขั้นตอนการทำงานของโปรแกรมยูบูต ได้เป็นสองรูปแบบ คือ แบบมีการย้ายตำแหน่งของยูบูต (Relocation) กับแบบไม่มีการย้ายตำแหน่งของยูบูต (Non relocation) ซึ่งมีขั้นตอนโดยรวมแทบจะเหมือนกันเว้นแต่ส่วนการย้ายยูบูตไบนารี่โค้ด แม้ว่าในโค้ดเวอร์ชั่นปัจจุบัน (2016.xx) จะใช้การย้ายตำแหน่งของยูบูต (Relocation) เป็นส่วนใหญ่ แต่เรายังสามารถพบลักษณะการบูตโดยไม่มีการย้ายตำแหน่งของยูบูต ในระบบขนาดเล็ก เนื่องการทำงานของโค้ดส่วนนี้ มีความน่าสนใจศึกษาอยู่ ในบทความนี้จึงขอแบ่งรูปแบบของการทำงานโดยใช้เรื่องนี้เป็นหลักนะครับ ก่อนจะทำความเข้าใจการทำงานทั้งหมด ขออธิบายเรื่องการย้ายตำแหน่งยูบูตให้เข้าใจกันก่อน

การใช้งานยูบูตบนแพลตฟอร์มที่มี ROM ขนาดเล็กด้วย ยูบูตฉบับย่อส่วน (u-boot SPL/TPL)

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

ทำความเข้าใจ ขั้นตอนการคอมไพล์ u-boot ตอนที่ 2

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

ความแตกต่างและหน้าที่ของไดเรคทอรี่ /proc และ /sys

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