مقدمه
بدون شک دیوایس تیری (device tree) یک شاه کلید در دنیای لینوکس نهفته است و به شکلی فراگیر در این سیستمها مورد استفاده قرار میگیرد. اما باید قبول کرد که درک مفهوم آن کمی سخت است. در این مقاله از پایگاه دانش هگزالینکس قصد داریم به شکلی خلاصه مفهوم device tree در لینوکس را بررسی کنیم.
کاربرد اصلی device tree در طراحی سیستمهای نهفته است و بسیاری از ادمینهای سرور لینوکس حتی نام آن را نشنیدهاند. حتی بعضی از مدرسین دورههای پیشرفته لینوکس هم آن را نمیشناسند. خب داستان جالب شد. به نظر میرسد که با یک مفهوم لینوکسی ولی خاص منظوره روبرو هستیم. حقیقت این است که دنیای توسعه دهندگان سیستمهای نهفته مبتنی با دنیای ادمینهای سرور کمی متفاوت است. پس نباید از این جهت خردهای به آنها گرفت.
قبل از شروع مقاله لازم است ذکر کنم که این مقاله با توجه به ساختار تراشه Zynq تدوین شده است، اما مفهوم کلی آن برای کلیه کرنلهای لینوکس که از device tree استفاده میکنند، صدق میکند.
تعریف و کاربرد
اجازه بدهید بحث را با یک سوال آغاز کنیم؟ به طور کلی device tree به چه دردی میخورد؟
تصور کنید که بوت لودر لحظاتی پیش کرنل لینوکس را داخل حافظه SD داخلی پردازنده کپی کرده است و بلافاصله به نقطه شروع برنامه کرنل منتقل شده است. در این لحظه رفتار کرنل کاملاً مشابه رفتار یک اپلیکیشن bare-metal است که در حال اجرا روی پردازنده است. اپلیکیشن نیاز دارد تا پردازنده را پیکرهبندی کند. اپلیکشن نیاز دارد تا یک حافظه مجازی را پیکرهبندی کند. اپلیکشن نیاز دارد تا در پیام را کنسول نمایش دهد و … . اما چطور؟ اجرای هر کدام از عملیاتهای فوق نیازمند دسترسی به رجیسترها و نوشتن در آن هاست. خب حالا لینوکس آدرس هر کدام از این رجیسترها را از کجا پیدا میکند؟ لینوکس تعداد هستههای پردازشی CPU را از کجا میفهمد؟ اصلا لینوکس مقدار حافظهای که میتواند برای پردازش در اختیار داشته باشد را از کجا میداند؟
صبر کنید، لازم نیست نگران باشید. قرار نیست همه این سوالات را یکجا جواب بدهیم. یکبار دیگه پاراگراف بالا را بخوانید و بعد ادامه مقاله را مطالعه کنید.
روش شناخته شده و کاملاً مستقیم برای رسیدن به یک راه حل پایدار استفاده از روتینهای بوتی است که به صورت خاص منظوره برای یک پلتفرم خاص در داخل کرنل لینوکس قرار داده میشود. این روتینها توسط پارامترهای پیکرهبندی کرنل لینوکس فعال میشوند. این راه حل یک روش بسیار خوب برای سیستمهایی است که در آنها تقریبا همه چیز ثابت است (مثلا یک رجیستر داخلی درون پردازنده x86)، یا دسترسیهای BIOS درون کامپیوترهای رومیزی. خب تا همین جا معلوم شد که چرا device tree به درد سرورها نمیخورد. اما وقتی صحبت از تغییرپذیر بودن سیستم به میان میآید، بهتر است به کرنل اجازه بدهیم در طول زمان اجرا اطلاعات مورد نیاز را جمع آوری کند و در رابطه با قابلیتهایی که در اختیارش قرار میگیرد، یاد بگیرد. یک مثال خوب برای درک بهتر این مساله ابزارهای جانبی PCI/PCIe متصل به کامپیوترهای رومیزی هستند.
معماری ARM یک دردسر و گرفتاری به تمام معنا برای جوامع لینوکسی است. با وجود اینکه غالب پردازندههای ARM از کامپایلر و روتینهای اجرایی مشابه به هم بهره میبرند، اما با این حال هر تراشه آدرسهای اختصاصی برای دسترسی به رجیسترها، و پیکرهبندی سفارشی شده خودش را دارد. بدتر از همه اینکه هر بورد پردازشی مجموعهای از کامپوننتهای جانبی مخصوص به خودش دارد. برآیند این تنوع شکل گیری یک جنگل پر درخت وحشی از هدر فایلها، پچها و پارامترهای پیکرهبندی در درخت کرنل است!!! هر ترکیب تنها برای پشتیبانی از یک بورد و یک تراشه اختصاصی روی آن بورد قابل استفاده خواهد بود. در چنین شرایطی، وضعیت بسیار آشفته و غیرقابل پشتیبانی میشود. در واقع ما با انبوهی از اطلاعات روبرو میشویم که هیچ کسی نه علاقهای به کار روی آنها دارد و نه از آنها سر در میآورد.
از همه این ها مهمتر، در این روش هر فایل باینری کرنل برای یک تراشه یا بورد خاص کامپایل شده است. گویی برای مادربوردهای هر کدام از کامپیوترهای موجود در بازار است، یک کرنل کامپایل شده است.
با تمام این اوصاف در جوامع لینوکسی همچنان امید به کامپایل یک کرنل برای تمام پردازنده های ARM وجود داشت. همه به دنبال این بودند که روشی ایجاد کنند تا به هر شکل ممکن کرنل قادر به شناسایی سخت افزار باشد و بتواند درایور صحیح را برای سخت افزار مورد نیازش بکار بگیرد. دقیقا همان روندی که در کامپیوترهای شخصی فعلی بکار گرفته میشود. اما چگونه؟
در کامپیوترها رجیسترهای اولیه هاردکُدِد هستند. یعنی مقدار آن ها در زمان ساخت پردازنده به صورت ثابت تعیین شده است. سایر اطلاعات مورد نیاز هم توسط BIOS تامین شده است. بنابراین شناسایی اتوماتیک سخت افزارها زمانی که قطعه نرم افزار کوچک (BIOS) اطلاعات آن را برای ما فراهم کند، نسبتا ساده است. اما پردازنده های ARM از موهبت BIOS بی بهرهاند. از طرفی کرنل لینوکس هم فقط به خودش متکی است.
با این تفاسیر تنها راه حل موجود و قابل انتخاب device tree است. مفهوم device tree تحت عنوان open firmware نیز ارجاع داده میشود. به بیان ساده device tree لزوما یک ساختار داده در فرمت بایت کد (غیرقابل خواندن برای انسان یا زبان ماشین) است که اطلاعات مورد نیاز کرنل در زمان بوت سیستم را در خود جای داده است. بوت لودر چانک اطلاعات را درون یک آدرس مشخص از RAM کپی میکند. بوت لودر این کار دقیقا قبل از انتقال به نقطه شروع اجرای کرنل انجام میدهد.
تا اینجا تعریفی کاملا مبهم از device tree ارائه شده، اما این دقیقاً همان چیزی است در واقعیت وجود دارد: با وجود اینکه یکسری قوانین کلی برای آن وجود دارد، اما عملا هیچ قانون سخت گیرانهایی برای قرار دادن اطلاعات در device tree و نحوه مرتب کردن آنها وجود ندارد. هر روتینی در کرنل ممکن است پارامتری را در هر مسیری از device tree پیدا کند. نحوه سفارشی سازی device tree کاملاً به تشخیص برنامه نویس بستگی دارد. اینکه تمایل دارد چه چیزهایی را به صورت پارامتریک تعریف کند و کدام مسیر بهترین مکان برای قرار دادن آن پارامتر است. البته پذیرش یک ساختار استاتدارد درختی، امکان استفاده از API های مرسوم برای دسترسی به دادههای خاص را فراهم آورده است. ولی همچنان دست طراح به اندازه کافی باز است.
برای اکثر ما، دیوایس تیری device tree جایی است که در آن ما در رابطه با اضافه یا حذف کردن قطعات کوچک سخت افزاری به کرنل اطلاع رسانی میکنیم.
برای اکثر ما، device tree جایی است که در آن ما در رابطه با اضافه یا حذف کردن قطعات کوچک سخت افزاری به کرنل اطلاع رسانی میکنیم. با فرض استفاده از تراشه zynq منظور از قطعات کوچک سخت افزاری PL است، به این ترتیب کرنل میتواند درایور مناسب برای استفاده از آن را اجرا کند یا در صورت حذف عملکرد درایور را متوقف کند. البته سایر اطلاعات اختصاصی سخت افزار (قطعات روی بورد) هم در device tree قرار دارد.
کامپایل
به طور کلی device tree در یک از سه فرمت زیر ارائه میشود:
- فایل متنی با پسوند
dts
(سورس کد) - فیل باینری
blob
با فرمتdtb
(کد ماشین) - یک فایل سیستم در لینوکس در دایرکتوری
/proc/device-tree
در فرایند استاندارد، فایل dts ویرایش و سپس کامپایل میشود و یک فایل dtb تولید میشود. کامپایلر مورد نیاز برای این منظور به صورت اختصاصی با منابع کرنل لینوکس ارائه می شود.
جمع بندی
نکته آخر اینکه، نوع کدنویسی برای device tree به شکلی است که قرار نیست چیزی اجرا شود. مشابه xml
در device tree فقط اطلاعات سازماندهی میشوند. بعضی از معماریها ابزار اتوماتیکی برای تولید device tree از روی یک پروژه xps دارند (مثلا معماری میکروبلیز)، اما چنین ابزاری برای تراشه zynq 7000 وجود ندارد.
2 در مورد “آشنایی با مفهوم Device Tree”
مرسی جالب بود
سلام و عرض ادب
امیدوارم پیروز و سربلند باشید.