#2 (2025-01-30)
טוען נגן…
תמלול
אז היום אנחנו נעשה את שיעור מספר 1.
רק אז ככה שהכול יהיה מסודר.
זה ה-VS Code של השיעור. אז יש לנו היום שיעור יחסית עמוס. חלק מהחלק התיאורטי כבר העברתי לשיעור הבא כי לא היה לי מקום. רציתי שנעשה גם קצת מעשי.
בואו נתחיל.
## Introductory Recap: What is Linux?
אז יש לי חוב קטן שאני חייב לכם מהשיעור הקודם, קלטתי שלא דיברנו על מה זה Linux. אז אנחנו נעשה את ה-introduction המהיר הזה ונחזור לאיפה שהתחלנו.
אז Linux זה בעצם איזושהי ירושה של מערכות Unix שהיו בשנות ה-80 וה-90. היה בחור בשם Linus שגם, שהוא גם הממציא של Git, שאתם נהנים ממנו כרגע בקורס אני רואה. בקיצור, הבחור החליט שלא מתאים לו המערכת הפעלה והתבסס על מערכת Unix ובנה עליה את כל המערכת שנקראת Linux. הוא עד היום מתחזק את הקוד, דוחף פיצ'רים ובודק PR-ים. אז אם אתם רוצים PR-ים, Pull Requests, אם אתם רוצים מישהו ברמה שיבדוק לכם את הקוד, אתם מוזמנים לנסות לדחוף ל-open source של Linux.
ובעצם אחד הדברים שהוא נשא על דגלו זה הרבה מאוד open source ו-licensing. יש פה גם הסבר על כל מיני רישיונות כי זה מחובר בד בבד עם המערכת הפעלה וגם עם כל העולם הזה של ה-Embedded Linux.
אז השתמש ב-license של GPL ופיתח עליו איזשהו community מאוד גדול ו-ecosystem. בעצם ה-Linux Foundation, אנחנו רואים אותו כמעט בכל מקום. רוב הקומפוננטות שניגע בהם בקורס פותחו על ידי Linux Foundation. אם אנחנו מדברים על U-Boot, על Yocto, המערכת הפעלה עצמה - Linux.
ובשנים האחרונות יש איזה מערכת הפעלה חדשה שבאה להחליף כל מיני מערכות הפעלה קטנות, RTOS-ים בעיקר. אם יצא לכם לשמוע על Zephyr, אז זה גם איזושהי מערכת שהיא גם חלק מה-Linux Foundation.
המטרה וה-use cases של Linux זה בעצם להיות תחליף חינמי לשרתים, ל-desktops וכל מיני מכשירים. והסיבה שזה מתאים למערכות embedded זה כי זה גם פלקסיבילי, גם משהו שהוא יחסית reliable.
האמת שבשנים האחרונות היציבות של Linux עלתה דרסטית. אני זוכר שכשאני התחלתי, כל הזמן היו קורסות לי המערכות. הייתי עובד על ה-Raspberry עם Ubuntu, לפני שזה נקרא Raspbian, והיית פשוט שובר את הראש, כל הזמן הייתה מתרסקת לי.
הרבה פעמים משתמשים ב-Linux למערכות כמו ראוטרים. אם אני אספר על הטרמינל בגילת, אז הוא היה ממש ב-Linux vanilla לפני שעברנו אותו ל-Yocto. מערכות אוטומטיות, בכל מיני מחשבי רכב, אז יש מימושים של Linux, או כל מיני וריאציות של Unix יותר ישנות, רצות ברכב עצמו. קונטרולרים תעשייתיים, הרבה פעמים משתמשים בהם, ומכשירי IoT, לרוב מכשירי IoT שלא מבוססי סוללה או כאלה שהם טיפה יותר חזקים, גם כן משתמשים הרבה פעמים ב-Linux.
למה בעצם Linux ל-embedded? היכולת לעשות קסטומיזציה ל-kernel ול-user space, ובעצם להשתמש ב-software stack ספציפי שמתאים לך. העבודה עם ה-Device Tree, שעוד לא נגענו בה, אנחנו ניגע בה בהמשך, בעצם נותנת לנו יכולת לשנות דברים בזמן מאוד קצר.
מכיוון שזאת מערכת הפעלה שהיא open source, הקורפורייטים מאוד אוהבים את זה כי הם אוהבים משהו שלא עולה להם כסף ומאוד קל להם לפתח עליו, אז הרבה מאוד משתמשים בזה.
יש היום חברות שעושות licensing ל-Linux. אם אתם מכירים את Red Hat, שהם באמת הראשונים שעשו את זה, לקחו את המערכת הפעלה עצמה, תרמו כסף מאוד גדול ל-Foundation עצמו (אם אני לא טועה הסכום הוא 500 מיליון דולר או משהו כזה), והם בעצם - זה המודל העסקי שלהם: הם מציעים תמיכה, הם מציעים רישיונות. אני יכול להגיד שבחברה הנוכחית ובאבא, שניהם השתמשו ומשתמשים ב-Red Hat, וזה קטע מצחיק.
עוד חברה שעשתה licensing זה Canonical, החברת אם של Ubuntu. אז גם הם עשו licensing ל-Linux. חברה אחת, Parallel Wireless, נפטרה מ-Red Hat ועברה ל-Ubuntu, ואחרת עשתה את המסלול ההפוך. אלה שתי החברות בעיקר שמשתמשות ושעשו licensing. כשמקבלים licensing זה בעצם אתה צריך לשלם כדי שיהיה לך אפשרות להשתמש במערכת הפעלה עצמה.
## Open Source Licensing & Linux
Stability ו-Security - גם כן שתי החברות האלה ועוד הרבה חברות בעצם משתמשות ב-Linux מכיוון שזה יציב ובטוח.
הרישיון של Linux, הרישיון שבעצם under Linux, אז זה open source licensing. זאת אומרת שכל דבר שאתה משפר במערכת הפעלה או בשימוש שלך, אתה חייב לדחוף בחזרה לתוך ה-Foundation, לתוך ה-branch עצמו. זאת אומרת, אם יש לי איזשהו feature מיוחד שאני שים על המערכת עצמה, על ה-branch, כאילו אני לוקח branch של Linux ויש לי איזה feature שאני מוסיף, אני מחויב בחוק להחזיר אותו בחזרה לבפנים, בעצם לפתוח Pull Request ולקבל אישור כדי להכניס את זה.
אז למה להשתמש ב-open source? בעצם מהסיבה שאמרתי כאן. ברגע שכולם משתמשים וכולם תורמים וכולם מכניסים לתוך הקוד, אז יש לנו בעצם קוד יותר רובסטי, עם הרבה יותר פונקציונליות. בעצם זה מבטיח לנו שיתוף פעולה ושקיפות לגבי מה שקורה בקוד, ובעצם איכות קבוצתית על ידי הקהילה שנוצרת.
שני סוגים עיקריים של licensing שמשתמשים וגם אנחנו נשתמש בהם: יש לנו Copyleft, זאת אומרת שכל שינוי שנעשה למוצר שלקחת על אותו license בעצם צריך להישאר על אותו ה-license, זאת אומרת שאי אפשר לשנות את זה. ובעצם הצורך, כמו שאמרתי, זה בעצם שימוש במוצר (הכוונה למוצר היא יותר לקוד) פתוח לכולם.
יש לנו Permissive, בעצם האפשרות להשתמש בזה ולשנות את ה-restrictions. הכוונה שאפשר פשוט להשתמש ולשים את זה אצלך באיזושהי צורה שרוצים ולא חייבים אפילו להשאיר את זה על אותו license. אפשר להשתמש בזה לשימוש עצמי, אני חושב שאפילו לשימוש תעשייתי. הכוונה שאין צורך לדווח על השינוי של ה-license. זה בעצם נותן לנו אפשרות להשתמש בקוד או במוצר הזה בצורה יותר גמישה ובלי באמת שום חובה לקהילה שממנה לקחנו את זה.
אז Linux משתמש ב-GPL v2, שזה GNU General Public License גרסה 2. זה בעצם משתמש ב-Copyleft, זאת אומרת שכל דבר שתרצה להוסיף יהיה חייב להיות לפי ה-license של Linux. מה שזה אומר זה שכל manufacturer או כל חברה שרוצה להשתמש בזה ורוצה להוסיף ל-open source חייבת להכניס את זה בחזרה ל-kernel ולמערכת הפעלה.
## Agenda
אז מה יהיה אחרי כל זה, מה יהיה לנו היום?
נדבר על System Calls, קצת נחפור בתוך מה זה הדבר הזה System Calls שמניע לנו את כל המערכת. נדבר על User Mode מול Kernel Mode, נדבר על ה-Supervisor Call, על ה-SVC. נסתכל על מה זה System Call Table.
נדבר על מה ההבדל בין API ל-ABI.
נעשה איזושהי סקירה על קבצים ועל File System, נדבר על הרשאות קבצים, ELF.
ניכנס טיפה ל-SoC, מה זה הדבר הזה שאנחנו נרוץ עליו, או שנעשה עליו אמולציה.
ונסביר קצת על ה-Toolchain שאנחנו מתקינים. אנחנו בעצם הולכים להשתמש בשני toolchains: אחד זה יהיה ה-toolchain שנתקין היום, ושני זה יהיה toolchain של Yocto.
נעשה איזשהו introduction ל-BeagleBone, ל-BeagleBone SoC reference manual. אם תסתכלו, יש לנו תיקייה חדשה של docs. אז אני ממליץ, ואנחנו נפתח ונעבור על הקבצים היום, ואני גם ממליץ לקרוא אותם בזמנכם הפנוי. אחד מהם זה קובץ, טוב, נגע בו מאוחר יותר.
נראה איך משתמשים ב-Minicom. בעצם זה איזשהו תחליף לינוקסי של PuTTY או, אני לא זוכר איך קוראים לשני, שמשתמשים בו. אפשר גם להשתמש ב-MobaXterm אם זה נוח לאנשים, אבל אני אשתמש בקורס כרגע ב-Minicom.
ונעשה Toolchain Installation ונכין את עצמנו לשבוע הבא. אוקיי?
אז לפני כל זה, אני זוכר שהיו כל מיני דיבורים בקבוצה על לקנות את הבורדים. אתם התקדמתם עם זה? מישהו עשה משהו?
אני כתבתי על זה בקבוצה וכמה אמרו שהם רוצים, אני לא יודע. אבל לגבי עניין המכס וזה, נראה לי הזמנתי פעם אחרונה משהו מחו"ל, לא יודע מתי.
אני רק אסביר לגבי המכס. כשמזמינים מאתרים כמו Mouser או DigiKey, גם אם רשום ישראל, זה מגיע דרך FedEx מאוד מהר, וביכול להיות שזה יתקע במכס. במיוחד אם זה מעל 75 דולר, שזה כמעט בדיוק הגבול. ואז צריך לשלם עוד כמה עמלות, עמלת עמיל מכס, עמלת... לא יודע, ומע"מ.
אני חושב שבסופו של דבר, במיוחד אם צריך להזמין שתיים כדי זה, שווה להסתכל על תלמיר, פשוט תבדקו אם זה במלאי והוא שולח את זה אפילו יותר מהר. בכל מקרה, אין שום חובה להשתמש בחומרה של הקורס. זה נטו בשבילכם, במיוחד אם אנחנו נעשה בשיעור הבא את ה-boot sequence, אז נוכל לעצור ולראות בכל שלב. אני אעשה את זה על המסך ואראה לכם, אבל אם אתם תרצו להשתתף תוך כדי, אז סבבה.
תלמיר זה מה שאתה שלחת שם? זה מה שאתם מצאתם שם, ששלחתם?
כן, זה איזשהו ספק של אלקטרוניקה מחיפה, ניו טכנולוגיה, מביא Raspberry Pi...
אה, מעולה.
הוא לא הרבה יותר יקר, אני חושב שזה אותו דבר. אתה אומר עדיף בגלל המכס. סבבה. לא חושב שזה הבדל גדול. שוב, לא חייב להשתמש בחומרה, אפשר להשתמש באימולטור גם ככה.
טוב, נמשיך הלאה.
## System Call
אז ככה: System Call. מה זה בעצם System Call? System Call זה בעצם entry point שאנחנו משתמשים בה כדי ניגש ל-kernel. בעצם זה מאפשר ל-user space לבקש איזשהו service, איזשהו שירות מה-kernel עצמו, אם זה גישה לחומרה או process management. האינטרפייס בין user space לבין kernel נעשה על ידי read, write, open, close, fork, get something. אנחנו נראה עוד הרבה. הרבה פעמים מקצרים את זה ל-sys call.
והשימושים של זה ב-embedded הם בעצם משתמשים בזה כדי לנגשת לחומרה...
רוב הקונספט של תקשורת מול החומרה, זה GPIO-ים, כל מיני פריפריאלים שאנחנו משתמשים, I2C ו-SPI. משתמשים ב-kernel management abstraction.
אם אנחנו משתמשים באיזשהם קבצים עם ה-path (וזה בסדר אם אתם לא מבינים מה אני אומר כי אני אסביר את זה עוד כמה רגעים), משתמשים ב-path של sys או dev ל-devices.
לדוגמה, כאשר אני כותב עם write לתוך איזשהו GPIO ספציפי, אני אלך לקובץ שנמצא ב-path שנקרא `/sys/class/gpio` וכולי.
## What Happens When Invoking a System Call?
אז בעצם מה קורה כאשר אני קורא ל-system call?
בעצם שלב אחרי שלב, מה קורה שקוראים ל-system call?
ב-user space מותקג איזשהי קריאה לרפר. בעצם glibc נותנת לנו איזשהו wrapper של open, ואז כשאני פותח את הקובץ, אז אותה פונקציית wrapper הזאת בעצם מטריגה איזשהו software interrupt.
software interrupt, זה לא באמת software interrupt. אז בעצם אנחנו משתמשים ב-Supervisor Call (SVC), זה איזשהו interrupt של המערכת כדי לגשת ממנו לתוך ה-instruction של ה-ARM.
בעצם כדי שאני אוכל לגשת ל-kernel, אני צריך ליצור איזשהו event שיעביר אותי ממצב של user mode, שבו אני נמצא ב-user space, למצב של kernel mode, שבו אני יכול לגשת לחומרה ולמערכת ההפעלה עצמה.
אז ברגע שאני עושה את זה, אני בעצם עובר ל-kernel mode, ה-CPU עובר מ-user mode, שזה מצב unprivileged, ל-kernel mode שזה מצב privileged.
ה-exception vector table, בעצם מה שמסנכרן את ה-interrupts ל-handlers שלהם, מעביר אותי לתוך ה-SVC, מה שנקרא בקוד SWI.
ומשם אני קורא לשימוש עצמו של ה-system call. אם זה write, אז אני בעצם אלך לפי ה-system call table לפונקציה שמתאימה ל-write.
בהתאמה, אנחנו נראה בהמשך, המערכת הפעלה מעבירה על רגיסטר ARM ספציפי שנקרא R7, היא מעבירה עליו איזה service היא רוצה להשתמש לפי המספר. בעצם זה ממש המספר של הפעולה ב-assembly.
ומשם אנחנו קופצים, עדיין במצב של privileged mode, לבצע את הפעולה, ואנחנו חוזרים אחרי שאנחנו עושים את זה.
## Class Document
אם אנחנו, וזה בדיוק הזמן, אם אנחנו נפתח את הקובץ שנקרא ARM_Cortex-A8_TRM, אתם יכולים לפתוח אותו ישירות מכאן. אפשר להוריד אותו ככה ולשים אותו איפה שתרצו. נפתח אותו ואני אראה. אלה המסמכים שאנחנו נשתמש בהם בקורס.
יש לנו את ה-reference manual של הארכיטקטורה שלנו. ה-SoC שלנו משתמש ב-A8, ב-instruction set של A8 של ARM.
ויש לנו את ה-reference manual עצמו של ה-SoC. בעצם איך הם קוראים לזה? MPU, Microprocessor Unit. זה השם שמשתמשים בו ב-Texas. אפשר להשתמש גם ב-SoC, זה לא משנה, MPU ו-SoC זה אותו דבר. זה פשוט לא MCU וזה לא CPU. וגם על זה נדבר למה.
עוד שנייה אנחנו נצלול לתוך ה-A8, אבל בקטנה כדי שנראה בעצם את הבלוקים של ה-MPU.
סימנתי להראות בעצם את ה-control block של ה-MPU שלנו. תגידו לי שאתם באמת רואים. אז אני אלך ל-3.
וזה בעצם מה שיש לנו בתוך ה-Microprocessor Unit. אז יש לנו שכבות של L1 cache, שזה בעצם זיכרון מאוד מהיר. יש לנו את ה-ROM, אנחנו נדבר עליו קצת יותר בהרחבה בשיעור הבא. יש לנו כל מיני memory bus שמשתמשים בהם כדי להעביר...
- רגע, זה ה-BeagleBone מה שאנחנו עכשיו מסתכלים?
- זה ה-MPU של ה-BeagleBone, AM335x.
- אה, זה הארכיטקטורה של ה-MPU שלו?
- כן, זה ה-reference manual. בעצם פה יש את כל המידע על ה-MPU, על ה-CPU שמריץ את המערכת, מריץ את הבורד עצמו.
אני אראה לכם קצת table of contents. זה לא קובץ קטן, כמו שאתם רואים יש פה 4,000 עמודים. יש איזשהו הסבר כללי על המערכת. מניסיון, קבצים כאלה זה נחמד לקרוא, כאילו מרפרוף עליהם. יש דברים שלא צריך, אולי Graphics Accelerator אנחנו לא באמת צריכים. Real-Time Unit זה דווקא כן מעניין, אנחנו נגע בזה בהמשך. איך עובדים ה-interrupts, בעצם טבלת ה-interrupts של המערכת. General-Purpose Memory Controller, זה גם משהו שנגע בו כבר בשיעור הבא. אותו דבר גם עם ה-DDR Controller. ברגע שנדבר על כל המערכת אז נגע בזה. Power, Reset, and Clock Management.
ו-peripheral management... peripherals. LCD Controller אם אנחנו רוצים להתחבר. ויש פה עוד הרבה דברים: PWM, USB, UART לפריפריאלים, טיימרים ו-I2C. זה נחמד לקרוא את הדברים האלה וגם נחמד ללמוד את זה.
- מטורף שזה 4,000 עמודים.
- לא צריך לדעת כל עמוד ועמוד, והרבה מזה זה רק טבלאות. הטבלאות של רגיסטרים וטבלאות של מה הולך לאן. אם אנחנו נפתח I2C...
- כאילו רוב הסיכויים שמישהו כאילו שהיה לו את הכוח לקרוא את ה-4,000 עמודים כבר כתב את ה... אני מניח, את ה-device tree שמתאים לדבר הזה, ואתה כאילו מקבל את זה ב-Yocto...
- נכון, נכון.
- כאילו אין לי בעיה עם זה שאנחנו קוראים את זה עכשיו, אבל כאילו סביר להניח שאנחנו לא צריכים להתעמק כי מישהו עשה את העבודה בשבילנו, אני מניח.
- אתה צודק וגם לא.
אם אנחנו משתמשים ב-features ספציפיים ב-device tree, אז עדיף שנדע מה ה-constraints שלנו שלא נעשה שטויות. אף אחד לא קורא את זה לגמרי. הייתה תקופה שעבדתי בירושלים, אז ברכבת הייתי קורא את הדבר הזה כי אין קליטה ואין זה. שווה לעבוד על זה. אני יכול להגיד שבקורס bare metal שייפתח, אנחנו באמת נצלול לתוך הדבר הזה כי אנחנו באמת נכתוב את הדברים. אבל פה אנחנו לא, אנחנו רק מודעים לזה ורוצים לדעת את הפונקציונליות. אנחנו כן נדבר על SVC מכאן, ואנחנו כן נדבר על איך אני יודע מאיפה עולה המערכת. אתה צודק ש-Yocto מגדיר את הכל, אבל כדי שנדע מה אנחנו עושים, אז אני רוצה שנסתכל על זה.
המסמך השני שנסתכל עליו זה המסמך של הארכיטקטורה עצמה. אני חייב להגיד שזה כן כדאי להכיר, לפחות ברמה הפונקציונלית. אני גם לא חושב שצריך לצלול פה. אני יכול להגיד שלי היה איזשהו באג מאוד גדול עם בעיית caching, שהוא נפתר רק מצלילה לארכיטקטורה ומציאת פתרון ל-cache invalidation. cache invalidation, רק לשם ה-name dropping, cache invalidation זה בעצם אפשרות להגדיר את ה-cache, את הזיכרון המהיר של ה-CPU, כ-dirty ואז בעצם לעשות לו flushing לתוך ה-RAM וככה לשתף אותו בין פרוססים. זה היה הבאג וזה היה גם הפתרון שהיה.
בסדר, אז בואו נחזור למצגת. אלה הקבצים, שימו אותם בצד, אנחנו הולכים לחזור אליהם עוד איזה רגע.
## User Mode vs. Kernel Mode
שני המסמכים, אז בעצם ה-ARM Cortex TRM (Technical Reference Manual) הוא בעצם רוצה להגיד לנו מה נמצא בתוך הארכיטקטורה עצמה. שימו לב שלבד הוא לא מגדיר את כל הארכיטקטורה. אם אני זוכר נכון, יש לו קובץ משלים, נראה לי שנקרא ARMv7 לדעתי, שאנחנו לא נגע בו. והמסמך הספציפי של ה-CPU... במקרה שלנו, MPU. במקרה שלנו רואים מה נמצא בו, שזה קצת פריפריאלים, memory interfaces, וקצת system integration.
בואו נדבר על מה זה user mode ו-kernel mode.
user mode זה בעצם המצב שבו אני נמצא כאשר אני לא יכול לגשת לחומרה ול-memory עצמו. מי נמצא שם? ספריות, אפליקציות. בעצם כשאני אומר שאני בשכבת האפליקציה, עובד בשכבת האפליקציה, אז אני נמצא בדרך כלל ב-user mode. אני לא יכול לגשת לחומרה כמו שדיברנו, אני לא יכול לעשות דברים, אבל אני יכול כן להשתמש במערכת. אני נמצא בעצם במצב של user, כמשתמש במערכת ההפעלה עצמה.
מה זה נותן לנו? זה נותן לנו הגנה. בעצם ברגע שהמערכת שלי נופלת, או האפליקציה שלי נופלת, אני לא מפיל את כל המערכת. כמו שאם מישהו עבד עם מיקרו-קונטרולר, ברגע שנופל המיקרו-קונטרולר, בעצם אני כבר ישר צריך לרסט את הכל. אז אני יכול בעצם לראות איך אני מעלה מחדש את המערכת, מעלה מחדש רק את ה-user process או מה שספציפי שנפל.
יש לנו מצד שני את ה-kernel mode. בעצם יש לו הרשאות מלאות על הכל, יכול לגשת לחומרה כמובן. כדי שאני אוכל לעבור, כדי שיהיה אפשר לעבור בין user mode ל-kernel mode, אני צריך להיות במצב של interrupt. בעצם שזה בעצם איזשהו event שמגיע מה-CPU עצמו, מה-MPU. צריך להתרגל להגיד MPU כנראה כי זה לא לגמרי CPU. בעצם איזשהו event שמגיע מה-MPU, יכול להיות טרנזקציה של פקטה אם אנחנו מדברים בעולם ה-networking, זה יכול להיות שגיאת I2C, whatever. זה יכול להיות memory controller שהגיע לאיזשהו event, ואז אני בעצם קופץ ל-handler שלו בתוך ה-interrupt table ומבצע את הפעולה, וגם על הדרך אני עובר ל-kernel mode.
בעצם אז למה אנחנו משתמשים בשני מודלים? אחד, סיקיוריטי והפרדה מלאה, ככה שאפליקציות לא יוכלו לגשת לחומרה ולדפוק אותה. ואנחנו ככה מורידים את האפשרות שיהיו לנו באגים ואת ההשפעה של באגים על המערכת, כי בעצם ב-user mode אנחנו מוגנים מנפילה של כל המערכת. בנוסף, אנחנו גם מפרידים את ה-processes של ה-kernel ושל ה-user space, ואנחנו בעצם מונעים מאחד להשפיע על השני ולפגוע ב-kernel בצורה שהיא לא מתוכננת.
## Supervisor Call
נמשיך הלאה. אז בעצם Supervisor Call (SVC).
ה-SVC instruction זה בעצם איזשהו assembly ספציפי שהוא אומר לי, נותן לי אפשרות להריץ... בעצם לעבור מ-user mode ל-kernel mode ובעצם לבקש ממני איזשהו service שאני רוצה שהמערכת תיתן.
ברגע שאני קורא לפקודת ה-assembly הזאת, שאנחנו נראה איך היא נראית עוד רגע, אני בעצם מבקש...
גם interrupt למערכת, וגם אני מבקש מה-kernel לבצע בשבילי משהו, אם זה I/O כלשהו שאני רוצה להפעיל, או memory allocation.
אם תסתכלו בתוך המסמך של הארכיטקטורה, ב-section 2.15.1, אז בעצם תראו את ה-syntax של ה-command עצמו. 2.15.1, אז זה ה-SVC. ובעצם הוא מעביר את ה-program counter שלנו, בעצם את המקום שבו אנחנו רצים, להיות ה-SVC value שלך, שבעצם נעשה אוטומטית.
כל זה מקונפג לנו בעליית המערכת. כשהמערכת מעלה את ה-kernel, אז ה-kernel מעלה איזושהי טבלה, שבה נמצאים כל ה-SVCs שהוא הולך להשתמש בהם, והם כבר מוגדרים. הם מוגדרים דינמית, אבל זה בעצם סטטית.
ב-section 8 יש לנו בעצם הסבר מתוך המסמך ארכיטקטורה מה זה Supervisor Call. Supervisor Call בעצם נותן לי software interrupt instruction, נותן לי אפשרות לבקש supervisor function, בעצם פונקציה שתעביר אותי ל-kernel mode ותבצע את מה שאני רוצה בשבילה.
הפעולה הזאת תישמר את ה-program counter לתוך איזשהו רג'יסטר שנקרא CPSR, תכף נראה אותו. וביציאה הוא מבטל את כל ה-interrupts כשאני רץ, וביציאה אני בעצם מחזיר את ה-program counter של ה-CPU ישירות מהרג'יסטר הזה שנקרא CPSR. אם מעניין יותר על הארכיטקטורה אנחנו יכולים לעשות את זה, אבל זה קצת מחוץ ל-scope של השיעור.
בעלייה של ה-kernel, כשנגדיר את ה-kernel, ואנחנו אולי נסתכל בקובץ הזה אם זה באמת מעניין אתכם, נקרא kernel_entry, שמה מוגדרים כל ה-SVC handlers. בתוך רג'יסטר R7 של הארכיטקטורה הזאת ספציפית, אנחנו מעבירים את ה-system call number, בעצם שה-interrupt handler קורא ל-system call number הזה ספציפית, ואנחנו משתמשים באינדקס שאנחנו נראה בתוך איזושהי טבלה שנקראת system call table, ובאמצעותה אנחנו מריצים את הפעולה.
אז איך זה קורה? נחזור שוב. באפליקציה אנחנו קוראים ל-write מ-glibc. ה-glibc wrapper שבו אנחנו משתמשים טוען את הרג'יסטרים R7 ו-R1, תלוי במה שאנחנו רוצים להשתמש, ב-R7 ו-R1, ומפעיל את ה-SVC handler. בעצם ה-SVC handler, שנקרא SWI כי זה משהו מארכיטקטורות ישנות יותר, הולך ל-R7, קורא 4. מה זה 4? 4 זה sys_write בטבלה. והארגומנט שאני מקבל כ-R1, שזה לרשום מבפנים את המספר 1.
ה-kernel עצמו עושה את הפעולה של ה-write וחוזר עם התשובה לתוך R0, שזה ה-return value, וה-user space מקבל את התשובה בחזרה.
## System Call Table
ה-system call table זה בעצם איזושהי טבלה בתוך המערכת. הטבלה ממופה סטטית, ולכל entry בטבלה יש איזושהי פונקציית kernel שמתאימה לכך.
אם נגיע בקורס של kernel drivers, אז אנחנו גם באמת נעשה override לפונקציונליות הבסיסית של הפעולות האלה כדי לממש דרייברים משלנו בצורה הזאת, כלומר read, write ו-whatever.
מה שמעולה בטבלאות האלה, שהן architecture-specific. זאת אומרת שאם יש לי איזושהי ארכיטקטורה שלא תומכת באיזושהי system operation, אז זה לא יהיה שם, ככה שאני מונע את כל ה-overhead הזה. הטבלה עצמה היא read-only, אי אפשר באמת לכתוב אליה. ובגדול זה הכל function pointers של המערכת עצמה. ומה שזה נותן לי, זה בעצם איזשהו stability של כל ה-binaries של ה-kernel.
איך זה נראה? יצרתי את הדבר היפה הזה, sequence diagram. יצא לכם ליצור כזה sequence diagram?
אז זה בעצם מה שקורה: glibc נותן לי open, אני מעביר SVC עם 0, זאת אומרת ה-interrupt הראשון. הוא קורא ל-interrupt handler, מה-SWI שזה השם של ה-handler, קורא ל-open מתוך ה-sys_call_table. sys_open נקרא, ואז אני מתחיל לפעפע בחזרה את ה-return code או error code.
## APIs vs. ABIs
בואו נדבר על API מול ABI ב-Linux.
בעצם API זה איזשהו סט של פונקציות, פרוטוקולים וכלים שבונים לנו את האפליקציה עצמה. בעצם זה מגדיר איך נשתמש בזה ואיך הקומפוננטות אמורות לתקשר. הפוקוס העיקרי זה בעצם source code compatibility. זאת אומרת שלא תהיה לנו בעיית לינקים, שלא תהיה לנו בעיית API, שהדברים שאני מכניס הם לא באמת הדברים שאני משתמש, קריאה לפונקציה עם פרמטרים לא נכונים.
דוגמאות של APIs שמשתמשים: open שהסברנו מה קורה בה רק עכשיו, read, write, printf שגם בעצם קורא ל-read וכולי. אנחנו נראה את הדוגמה.
מצד שני יש לנו ABI, שזה בעצם קריאה לבינארים ספציפיים. זאת אומרת שאני כבר קורא לקובץ הרצה ברמה הזאת. זה בעצם הגדרה של איך לקרוא לקבצי הרצה. אנחנו בעצם מחפשים את הצורה שבה עובדים עם קובץ הרצה. לדוגמה man cd ייתן לי את הקובץ הרצה הספציפי שמשתמשים.
אם נעשה פה איזושהי דוגמה ל-man open, נרשום Linux, אז אנחנו נקבל, נראה איך משתמשים ב-man, ב-open. אז open ספציפית זה API, זה מגדיר לנו את זה כ-API. אבל אם אנחנו נשתמש ב-man cd, אז cd זה כבר בינארי, ונראה איך משתמשים בו ומה הפרמטרים שצריכים להעביר לו.
היום אני חייב להגיד שזה קצת... פעם היית צריך לעבור כל yellow page כזה ולקרוא, היום משתמשים בצ'אט או משתמשים ב-ChatGPT או ב... איך קוראים למודל הסיני החדש? DeepSeek? DeepSeek, אה, DeepSeek.
אז היום כבר משתמשים בזה, הוא גם נותן דוגמאות. לפעמים הוא קצת מחרטט, אז כדאי לעבור עליו, אבל לפעמים הוא יוצר בעיות כמו הבעיה שהייתה לכם עם התגים של ה-clone single-branch, אבל הוא גם נותן פתרונות, אז זה בסדר.
בעצם השימוש ב-ABI נותן לי איזשהו interface בין התוכנית עצמה לבינארי, בעצם להרצה של בינארי. אני משתמש ב-conventions של איך אני הולך לקרוא לו, בעצם איך הפונקציונליות הולכת להיקרא, מה ה-data types, מה ה-system call mechanism, והוא מבטיח לי בעצם איזשהו binary compatibility של הקוד עם המערכת הפעלה. זאת אומרת שאם אני בונה איזשהו קוד, אם אני בונה איזושהי אפליקציה שמשתמשת בבינארי, כמעט תמיד תהיה לי הבטחה שזה יעבוד. זאת אומרת שאם אני אקרא מהתוכנית שלי ל-change directory בתצורה מסוימת, סביר מאוד שזה יעבוד, וזה בעצם ה-compatibility שהוא מבטיח לי.
אז למה API ו-ABI הם חשובים?
API בעצם נותן לנו איזושהי פשטות הפעלה. זאת אומרת שאני יודע איך אני הולך להשתמש ואני יכול תמיד להשתמש באותה קומפוננטה באותה צורה. ובעצם הוא מאפשר פורטביליות על כמה מערכות שונות, כל עוד אותו API הוא נתמך באותה גרסת API.
ABI זה אותו דבר, רק ברמת הבינארי, ובעצם אני יכול להבטיח שהקובץ שהוא כבר מקומפל יהיה תואם לאותה ארכיטקטורה, ובעצם הוא מאפשר לי לרוץ במקביל גם בשפות שונות. ואני לא בעצם תלוי ב-API של השפה, אלא אני יכול להשתמש בבינארי הזה ספציפית, אם מ-C, או מ-Assembly, C++, ועוד שפות שתומכות בהפעלה של בינארי, שזה כמעט כולן. אם אתם מכירים, ב-Python יש גם system, או אני חושב גם ב-Java, בכולם יש בעצם אפשרות לקרוא.
## Code Examples
בואו נעשה שתי דוגמאות קוד של איך קוראים ל-API ו-ABI שעושים אותו דבר. אתם מוזמנים לעקוב. אני יכול להגיד שאולי התמיכה של המערכת לא תהיה אותו דבר כמו בשיעור הקודם, אבל זה זמני עד שאני אקבל את המחשב החדש.
אז code examples, יש לנו את 00_api_usage. בואו נפתח את זה. אז בעצם פה אנחנו משתמשים באיזשהו API שנקרא printf, אם שמעתם עליו. printf זה print format. בעצם ברגע שאני עושה `\n`, אני בעצם עושה flush לדבר הזה לתוך ה-stdout. stdout זה ה-standard output file descriptor בעצם, ה-file descriptor שבעצם נפלט למסך.
בואו נלקמפל את זה. לכם יש את הקיצורים מהשיעור הקודם, אני חייב להגיד שפה הקיצורים לא מוגדרים. התעצלתי מלהגדיר כמה דברים בסביבה. אז בואו נעבור לתוך ה-build, לתוך תיקיית build. מבחינתכם עכשיו אתם פשוט עושים Ctrl+F7, נכון? בתוך ה-build, בואו נעשה `make clean` כדי שאני אמחוק פה את הכל.
אני מוחק פה את הכל, אז בעצם תיקיית ה-binaries שלי ריקה ואין פה שום דבר. נעשה `cmake ..` או Ctrl+F8 מה שהגדרתי לכם שיהיה clean. Ctrl+F7 זה `cmake ..` בעצם לעשות את ה-configure. אם אנחנו נסתכל שנייה על ה-output, אז אנחנו יוצרים פה כמה קבצים. אני לא משתמש ב-g++, זו טעות הדפסה, אני משתמש ב-GCC.
בעצם אני מייצר שתי תוכנות, שני בינארים. נכנסים לתוך lesson_1 ו-lesson_0, בעצם אלה התיקיות. וברגע שאני אעשה `make`, אז הוא יבנה לי את הכל. אני אעשה refresh פה, אז יש לי גם את ה-Hello World מהשיעור הקודם. לא, בעצם זה ה-Hello World של ה-suggested solution שעשינו.
ועכשיו יש לי את שני ה-API ו-ABI usage. אז אם אני אלך ואני אריץ את זה, אז אני אעשה אחד אחורה, bin, שזה הקיצור של ה-binaries שלנו, ו-lesson_1, 00_api_usage עצמו והקובץ. ברגע שאני אריץ, אז יהיה לי "hello, world!". זה ה-API usage.
ומצד שני, אני אעשה את ה-ABI usage, שזה אם אתם רואים אני קורא ל-unistd.h, שנותן לי את היכולות להשתמש ב-system calls. אני משתמש ב-write, שבעצם write אני הולך לכתוב לתוך ה-standard output, stdout. אני הולך לכתוב 12 תווים, שזה 13 כולל ה-`\n`. שיהיה ככה, נראה מה יקרה.
וברגע שאני אריץ את זה, מישהו רוצה להגיד מה יהיה? מישהו עוקב? בעצם ייתן לי את אותו output, ובעצם השתמשתי ב-wrapper של printf, פשוט ראיתי מה קורה.
אז נקבל...
Hello ABI.
בוא נראה מה קורה, אני חושב שאם אני אעשה 13,
אז זה כבר תהיה בעיה למרות שהוא... טוב, לפי השורת קוד הלא נכונה,
אז אם אני רוצה לעשות את זה שוב, בוא נראה אם יש לי F7, F7 יש לי,
אז אני בונה הכל,
ו-Hello ABI, אותו דבר, אני אשאיר את זה 12, מה שנקרא.
אז זה דוגמה של API מול ABI, ומה זה בעצם נותן לנו...
רגע... לא, סליחה, זה הולך לכאן,
נרכז ככה.
אז בעצם כשאני משתמש ב-stdio, אני משתמש באיזשהו API של standard input and output operations,
בעצם יש לי כל מיני פעולות של input ו-output, ו-printf, שזה print format, זה פונקציה שמשתמשת, שמשתמשת ב-API הזה,
ומה שהיא בעצם עושה, היא ממירה את ה-API הזה ל-ABI שראינו אותו, שזה ה-write,
לתוך ה-
לתוך ה-file descriptor הראשון של... ה-file descriptor מספר 1,
שהוא ה-standard output. בעצם file descriptor מספר 2, אם אני לא טועה,
אז הוא ייתן לי את ה-stderr.
אפשר לנסות,
אז...
נגדיר באמת את ה-configure, לא, אני לא צריך נראה לי את ה-configure בעצם.
F7,
וב-ABI usage, אז הנה הוא מראה לי מאיפה השגיאה, כי אני בעצם משתמש...
בעצם... אני משתמש ב-stderr, שגם מודפס למסך.
אם אני כבר אשתמש במשהו אחר, אני יכול לסכן פה, אני יכול לסכן משהו כי אני לא זוכר אם זה 3, אבל אלו הדוגמאות. בעצם,
זה מה שקורה. file descriptor 1 זה ה-system call עצמו.
אז כאילו בבסיס הכל ABI, וה-API פשוט קורא ל-ABI? זה הכל?
במקרים האלה הספציפיים, לא בכל מקרה, זה לא איזשהו כלל קבוע. אם אנחנו משתמשים ב-stdio, אז אני כן קורא בסוף ל-unistd, write,
ו-read. לכל הפעולות של write ו-read, רק פשוט יש להם איזושהי עטיפה יפה בתוך... לא יודע אם להגיד עטיפה יפה, יש עטיפה.
יש עטיפה בתוך stdio.
טוב,
אתם רוצים לעשות איזושהי הפסקה? אני הבנתי שאני לא מוציא אתכם לפיפי, זה מה שאמרו לי במסדר השיעורים.
או שרוצים להמשיך כולם בהרמת יד?
בואו נעשה איזושהי הפסקה של 5 דקות.
נחזור ב-17:57.
יאללה, מקובל.
אני אעשה הפסקה של ה-share.
טוב, אני חזרתי.
נמתין עוד איזשהם כמה שניות, עד איזה סוף ה...
סוף הדקה,
ואז נמשיך.
ונצלול קצת למה זה Files ומה File System, ואיך אנחנו משתמשים בה.
אוקיי, אני רואה שכל מי שפתח מצלמה הוא איתנו, אז אנחנו ממשיכים. רואים שוב את המסך, נכון?
כן.
## Files and Filesystem
מעולה. אז בעצם Files ו-Filesystem.
בעצם ב-Linux, מערכת הפעלה... לא, לא בדיוק הכל, אבל הרוב... כמעט הכל.
הרוב... הבסיס, בעצם רמת האבסטרקציה ב-Linux אומרת שהכל זה file. Everything is a file, זה הפילוסופיה שלהם. בעצם, זאת אומרת ש...
קבצים זה קבצים, תיקיות הן קבצים, devices הם קבצים,
pipes ו-sockets, שזה דרכים לעשות העברה של דאטה בין processes, זה גם כן קבצים.
מה שזה נותן, זה נותן בעצם איזשהו interface שהוא אחיד והכל. בעצם אני משתמש באותם system calls שראינו,
ומשתמש על אותם הקבצים בתצורות שונות. זה נותן לנו בעצם איזושהי אבסטרקציה כזאת שאנחנו לא צריכים
לחשוב איך אני עובד בצורה שונה עכשיו עם device, אני פשוט פותח קובץ ומשתמש בו באותה צורה.
בתוך ה-kernel כל file descriptor ממופה לאיזשהו int.
int, הכוונה למספר שהוא native ל-CPU. אם אנחנו משתמשים ב-ARM 32-bit, אז int הוא 32-bit, אז בעצם יש לנו...
$2^{32}$ לחלק ל-2,
כי זה signed number
מספרים. משתמשים בזה, file descriptors הם חיוביים.
הייצוג שלהם חיובי.
ומקצרים את זה ל-FD.
מובן שאנשים אומרים file descriptor, אבל יש אנשים שאומרים FD.
זה בהתחלה מבלבל, אבל בסדר.
חלק גדול מהמערכת של Linux בעצם משתמשת בפתיחה, כתיבה, סגירה, ובעצם מניפולציה של ה-file descriptors עצמם.
כשאנחנו עושים open, אנחנו מקבלים בחזרה file descriptor. נגיע לזה בהמשך.
מה שזה נותן לנו בעצם, זה איזשהו סוג מבוסס טקסט. אני לא עכשיו יוצר איזשהו משהו אחר, רוב הקונפיגורציות נמצאות אצלי באיזשהו מקום... באיזשהו path טקסטואלי, שזה ה-/etc.
נותן לי היררכיה בתוך ה-filesystem, זאת אומרת ש...
הקבצים של המערכת נמצאים בתוך תצורה של עץ כזאת,
שאנחנו באיזשהו...
באיזשהו structure של עץ, שאנחנו בעצם משתמשים, שה-/ זה בעצם השורש של העץ.
כדי להגיע ל-file אני חייב לפתוח אותו,
ואני יכול לפתוח file לקריאה, לכתיבה, ולקריאה וכתיבה.
אז קבצים ו-filesystem. קבצים רגילים.
קובץ רגיל זה בעצם איזשהו רצף של בייטים שהם שמורים על דיסק.
יכול להכיל מידע טקסטואלי, יכול להכיל מידע בינארי בעצם, מידע של הרצה,
שזה יכול לקרוא ה-CPU, ואיזושהי אינפורמציה אחרת.
הסוגים: אז text files, זה בעצם קובץ שקריא לבני אדם, סקריפטים, קובצי קונפיגורציה, כמו לדוגמה JSON, XML, וכדומה.
קבצים בינאריים, זה בעצם קבצים שהמכונה יכולה לקרוא, אבל זה גם קובץ טקסטואלי.
לעומת...
לעומת...
Windows, שבו קובץ טקסטואלי חייב להסתיים ב-txt או באיזשהו extension מסוים,
ב-Linux קובץ טקסטואלי יכול להיות כל דבר, ואני חושב שיש פה איזה דוגמה.
לא, האמת שרשמתי אותם כ-txt, אבל...
אבל גם אם לא היה פה את המילה txt בסוף, זה היה אותו דבר, זה היה יכול להיות קובץ טקסטואלי בהמשך.
הוא מזהה את זה לבד כאילו? את הסוג?
זה פשוט קובץ, הוא לא צריך להזהות אותו, בשניהם זה פשוט קובץ רגיל.
אוקיי?
פה יש איזשהו משהו שאומר explicitly שזה text, וזה אולי גם cross-machine compatible, לא מה ששני...
ב-Windows אם רוצים לפתוח קובץ שהוא... קובץ שהוא ללא סיומת, אתם משתמשים ב-Nano, ב-Notepad++?
בסוף כולם משתמשים.
אוקיי, בעצם איך משתמשים בזה? אז אנחנו נראה פה איזושהי דוגמה קטנה.
אז כדי לפתוח קובץ...
כדי לפתוח קובץ, נכנס שנייה ל-lesson 1.
כדי לפתוח קובץ, אז אני עושה touch. touch זה הפקודה, ואני קורא לזה file...
file_1.
file_1, בעצם...
בלי שגיאות כתיב, touch
file_1, ואז נוצר לי קובץ file_1.
אוקיי, אם אני רוצה לכתוב לתוך file_1, אני יכול להשתמש ככה, אני אכתוב כל מיני דברים,
ואז אם אני רוצה לקרוא ממנו, אז אני...
אז אני יכול לקרוא ולעשות לזה preview, ולעשות cat file_1, לראות מה נמצא שם, נמצא מה שזרקתי שם.
ואם אני רוצה לראות איזה סוג של file זה, אז file_1... file
file file_1,
אז אני יכול לראות שהוא קובץ בצורת ASCII.
הכנתי פה עוד איזו דוגמה לעוד סוגים של קבצים שאפשר לראות...
למשל קובץ CMakeLists.txt, אם אנחנו נלך אחורה, אז קובץ CMakeLists.txt שלנו הוא גם קובץ ASCII. לעומת זאת, אם אנחנו נלך לקובץ שיצרנו מקודם, ל-ABI usage, אז ה-ABI usage בעצם הוא כבר ELF. מה זה ELF? נדבר על זה עוד רגע, אבל הוא קובץ בינארי. גם מראה לנו איפה הוא מלונקג', ולמה הוא מצביע, ועם מה אפשר להשתמש בו, כי אנחנו בעצם מקמפלים עם debug flags.
עוד איזשהו קובץ, בעצם יש לנו את תיקיית documents, אוקיי? תיקיית documents שיצרנו קודם. אז אם אני אלחץ עכשיו Enter, אז הוא יראה לי שזה בעצם directory, אבל היא קובץ, נכון? אז הכל קבצים. אלה אלו איזשהם פקודות בסיסיות שמשתמשים בהן בשביל זה. stat משתמשים כל הזמן, משתמשים באיזשהו אח גדול שלו ב-tail כדי לעקוב אחרי קובצי לוגים.
וקבצים בינאריים, אז אם אנחנו נסתכל על עוד איזשהו קובץ בינארי, אז לפני השיעור קצת נשבר לי ליצור תגים כל פעם מחדש. אני גם לא בטוח שאני אמשיך להשתמש בזה. יצרתי איזשהו סקריפט שמייצר לי תגים, אז `tools/create_tag.sh`. בעצם אנחנו רואים שזה shell script, זה קובץ ASCII שהוא גם executable.
## File Location and Inode
בוא נמשיך הלאה. אז File Location ו-Inode.
File Location זה בעצם ה-path שמצביע על הקובץ בתוך ה-filesystem. זה בעצם האבסטרקציה של ה-filesystem, העץ הזה שדיברנו עליו קודם, אז הוא מצביע לתוך איזשהו קובץ אחר.
ה-Inode זה איזשהו data structure, איזשהו מבנה נתונים שמשתמשת בו המערכת קבצים כדי לאחסן את ה-metadata של הקובץ עצמו. בעצם לכל קובץ יש איזשהו metadata, שזה המידע שמייצג את הקובץ. וה-metadata שמשתמשים ב-Inode זה בעצם ה-file size, ה-permissions – למי הוא שייך, ה-timestamps – מתי משתמשים, בעצם creation, modification ו-access, ובעצם כתובת של איפה הוא נמצא על הדיסק.
אוקיי? למה Inode זה איזשהו קובץ, ובעצם איזשהו קובץ מיוחד בתוך ה-filesystem? בעצם זה נותן לי אפשרות לנהל את ה-file ואת ה-data שלו. בעצם זה file שהוא metadata על file אחר. זה נותן למערכת ההפעלה, ל-filesystem עצמו, להבחין בין סוגים של הקבצים על ידי שם, או על ידי permissions, ובגדול גם על ידי כנראה timestamps, זה תלוי לפי מה שגוזרים. ויש גם את ה-inode ID, בעצם איזשהו ID שיש לכל קובץ והוא שונה בין אחד לשני.
בוא נסתכל שנייה על הקבצים. אז אם אנחנו נעשה `ls` – לא יודע אם אתם יכולים לקרוא את זה מכאן, אולי ככה רואים יותר טוב: `ls -i` ל-`lesson_1/01_abi_usage`, ויש לנו את `file_1`. אז הוא יציג לנו את ה-inode ID שלו, אוקיי? שזה המספר הזה, והשם של הקובץ עצמו.
ואם אני אעשה את אותו דבר עם `stat`, אז אני כבר אקבל את כל המידע שאמרנו, שזה השם, הגודל, כמה בלוקים בזיכרון הוא תופס – או אני חושב באיזה בלוק הוא נמצא, אני כבר לא נכנס לזה – פה זה באיזה סקטור בזיכרון הוא נמצא, את ה-inode ID ראינו כבר, מה הרשאות גישה – אנחנו נדבר על האוקטליות האלה ועל הרשאות גישה – מה ה-user ID שזה שייך לו – user ID נקרא AAA כי BBB לא עבד כמו שצריך – מתי ניגשו אליו פעם אחרונה, מתי יצרו אותו, מתי הוא שונה. change ו-modify, בעצם אני לא יודע מה ההבדל, אבל זה נראה כמו אותו דבר.
זה ה-inode. בעצם מה שהוא בא לתת לנו זה מידע על ה-file עצמו.
## Directories and Links
עכשיו בוא נדבר קצת על קבצים מסוג directory, שזה קובץ תיקייה, ו-links.
Directories זה בעצם תיקיות. סוג של file ספציפי שהוא בעצם מכיל עוד קבצים ו/או עוד directories, עוד תיקיות. לכל directory יש את הרשימה של filenames ששייכים לה, כולל ה-inode הספציפי שלהם, שזה המספר של ה-inode עכשיו.
Links: יש שני סוגים של links שמשתמשים בהם: יש לנו hard link ו-soft link, מה שנקרא בעברית symbolic link.
Hard link יוצר כמה קבצים עם אותו inode number. בעצם אי אפשר להעביר אותו בין filesystem ל-filesystem, ואנחנו נראה דוגמה עוד רגע.
Symbolic link בעצם יוצר קובץ שהוא מצביע על קובץ אחר. זה כמו איזשהו pointer כזה שאפשר לחשוב עליו. אפשר להעביר אותו בין כמה filesystem, ואנחנו נראה את הדוגמאות.
אז ככה, בוא נלך ל-`lesson_1`, ב-code examples, ונראה את ה-links.
אז רגע, אני רק אסיר את זה... אוקיי. נכנס ל-directory שנקראת `lesson_1`, בתוך ה-directory הזאת – `code_examples`, `02_links`. ניצור את ה... אז תיכף אנחנו נראה את ההבדל. אז אנחנו ניצור את ה-file הזה שנקרא `original.txt`. `cat` יראה לי מה יש בפנים, נכון? כמו שהגענו מקודם, אז בעצם מה שיש בפנים זה `"Hello, Hard Link"`, כמו שעשינו עם פקודת `echo` הזאתי.
ואז אני משתמש בפקודה עצמה, שהיא `ln`, שהיא יוצרת hard link, אוקיי. מייצרת links, אפשר ליצור איתה גם... ואני עושה link בין `original.txt`, שזה הקובץ שכתבנו, לבין קובץ שנקרא `hardlink.txt`. אם אני אעשה `ls -i` לקבצים הספציפיים האלה, אז אני אוכל לראות שיש להם את אותו inode number, אוקיי? זאת אומרת שזה בעצם אותו קובץ, זה לא קובץ שמצביע על קובץ אחר, זה ממש אותו קובץ.
ולעומת זאת, אם אני אלך ואצור symbolic link, soft link, שזה בעצם משתמשים בו הרבה פעמים כדי לעשות redirection, בעצם מתנהג כמו pointer ב-C. אז `ln -s` ל-symbolic link, או soft, אנחנו ניצור soft link, אוקיי. ואנחנו נראה עכשיו איך זה נראה.
אז בעצם יש לנו שני קבצים, יש לנו את הקובץ `original.txt` ויש לנו את הקובץ הזה. עכשיו אם אני אעשה `ls -l`, אז אני רואה בעצם ש-`softlink.txt` מצביע לתוך `original.txt`. ואם אני אעשה אותו דבר כולל hard link, אז יהיו לנו פה שלושה קבצים.
הקובץ הראשון הוא hard link, אוקיי? והקובץ המקורי הוא `original.txt`. יש לנו את אותו קובץ שנקרא בשם אחר, שזה `hardlink.txt`, שבעצם מצביע לאותו קובץ, ויש לנו soft link שהוא מצביע לתוך קובץ שנקרא `original.txt`. אוקיי? ואם אנחנו רוצים להסתכל על כל ההבדל, בעצם עשינו את זה רגע. אז זה מה שיש לנו.
## Special Files
בואו נדבר על special files, בעצם קבצים מיוחדים. אולי דיברנו על חלק מהם. בעצם יש קבצים שמציגים hardware devices, כמו `/dev/sda`, בעצם זה מגדיר לנו את ה-mount של ה-disk.
Block devices – בעצם כל מיני קבצים שנותנים לנו גישה לבלוקים ספציפיים של דיסקים.
Character devices – בעצם קבצים שנותנים לי גישה ל-stream של מידע, כמו מקלדת, זה איזשהו גישה רציפה של stream של מידע. כשנדבר על streams לעומת datagrams אנחנו נדבר על איך זה נראה.
בואו נעשה איזשהי דוגמה ונראה איזשהו קובץ מיוחד, קובץ שהוא בעצם מראה לי את ה-special device של I2C במערכת. אז יש לנו פה `i2c-0`. אם הסתכלתי על כל הקבצים, ואם אני אכתוב עליו, אני לא יודע מאיפה זה יצא כי אין פה שום I2C controller או שאני אדפוק משהו ב-CPU, אבל אם אני אכתוב עליו במחשב שלי, בעצם אני יכול לכתוב ישירות לתוך ה-`i2c-0`, שזה נראה שיש תמיכה מה-CPU של Intel.
אם אני רוצה לראות את ה-devices שיש לי, ונשמור את זה לעוד רגע שנתחבר ל-BeagleBone עצמו, אז אני אעשה `ls /dev`, ואני רואה פה כל מיני קבצים: `tty` זה קבצים שהם שמורים ל-USB, `sda` זה הקבצים של הדיסק, `serial` זה איזשהו פורט סריאלי, אני באמת לא יודע מה קורה כי זה בתוך ה-VM עצמו.
Hugepages – משהו שזה מגניב שיש את זה, בעצם זה איזשהו קונספט שנותן קישור בין סקטורים לסקטורים הרבה יותר גדולים, אבל זה למערכות עיבוד דאטה גדול, זה לא בסקופ של הקורס הזה.
את הקבצים האלה אפשר להפעיל מ-user space באמצעות ה-standard file operations, וכשנגיע בהמשך הקורס למצב, אנחנו גם נראה איך כותבים, קוראים ומשתמשים ב-`ioctl` על הקבצים האלה כדי לעשות מניפולציות לחומרה עצמה ולקבל את הבקשות מה-user.
עוד קובץ מסוים שאנחנו מכירים, בעצם קבצים של socket, או קבצים של סמפור, גם יוצר לנו קובץ אם הוא משותף.
## Filesystems
ונדבר עכשיו על ה-filesystem עצמו. בעצם ה-filesystem זה הצורה שבה הקבצים מאורגנים בתוך מערכת ההפעלה, ונותן לנו אפשרות לנהל את איך הדאטה שמור, מתקבל ומגיע.
כמה filesystems שמשתמשים בהם הרבה, וגם אנחנו נשתמש לפחות בחלק מהם:
ext4 – זה בעצם ה-default ל-Linux distributions. לא יודע אם קודם לכן יצא לעבוד עם ext3, אבל ext4 זה איזשהי גרסה חדשה יותר ומשתמשים בה, זה בעצם כל ה-filesystem נמצא עליה.
XFS – זה איזשהו filesystem ל-high performance.
ויש לנו NFS, שזה filesystem שהוא מגיע מה-network עצמו.
איך אני בעצם עושה mount ל-filesystem לתוך ה-directory tree, לתוך בעצם מה שאנחנו רואים כאן? אז אני משתמש ב-`mount`, המקום שאליו אני הולך לעשות את זה – ה-device שאני הולך לעשות לו mount ולתוך המקום עצמו.
הייתי רוצה להראות לכם איך משתמשים קצת בזה, אז אם אין לכם את הבינארי שנקרא `tree`, אז `sudo apt install tree` פשוט יראה לנו את זה. לחלקנו יש `tree`, ואם אנחנו נעשה `sudo tree / -L 1` – רק ל-level 1 – אנחנו נראה את כל ה-filesystem שיש לנו. אז יש לנו את `bin`, `boot`, `dev` – זה development, `home` – זה ל-user עצמו, לכל מיני ספריות, יש לנו את `media` שאנחנו יכולים להשתמש ברמקולים וכאלה, `mnt` – filesystem שעשינו לו mount למערכת עצמה, `opt`, `proc` – מידע על המערכת, `root` – זה ההתחלה, `tmp`, user עצמו, `var` – כל מיני דברים עצמם.
## Namespaces
Namespaces: Namespaces זה בעצם דרך לעשות וירטואליזציה לדאטה. משתמשים בזה בעיקר בשביל דוקרים ו-Kubernetes, שזה גם דוקר. בעצם הוא יוצר לי איזושהי הפרדה בין filesystem אחד ל-filesystem אחר, יש לי הפרדה ל-process ID, הפרדה היא גם ברמת ה-network – בעצם אני כביכול משתמש ב-NIC נפרד, NIC וירטואלי, ובעצם אני מקבל לשם את הפקודות. ה-IPC שאני משתמש זה בעצם IPC שהוא מופרד לחלוטין, ואותו דבר גם ה-username וה-user ID.
עושים את זה באמצעות mount ויוצרים בעצם איזשהו mount ל-namespace חדש, ויש לנו את ה-lsns שהוא מראה לנו את הרשימה של ה-namespaces.
## Permissions
בואו נדבר על Permissions.
Permissions בעצם נותן לנו את האפשרות לעשות קונטרול, לתת לנו לשלוט על מי יכול לגשת.
אמרתי לך לא להרשות לי לקרוא כל השיעור כי המשקפיים שלי מלוכלכות.
בעצם Permissions נותנים לנו שליטה על הגישה של מי יכול לקרוא, להריץ, ולכתוב לתוך הקובץ.
בעצם יש לנו שלושה סוגים של Permissions: יש לנו את ה-Read, שזה ב-r, יש לנו בעצם את האפשרות לקרוא מהקובץ עצמו. יש לנו את ה-Write, שזה בעצם להוסיף או לשנות את הקובץ. ואת האפשרות להריץ, שזה ה-x.
יש לנו שלושה Permission Classes שבעצם אנחנו משתמשים בהם ב-Linux: יש את ה-Owner, שזה ה-user שאליו שייך ה-file עצמו. ה-Group, שזה ה-user group שממנו משתמשים. וה-Others, שזה כל שאר ה-users.
אז איך בעצם הנוטציה של ה-Permissions? אז יש לנו Symbolic Notation. בעצם הסמלים שמייצגים, אז יש לנו rwx, בעצם שלושה מספרים אוקטליים, סליחה, שלושה מספרים אוקטליים שכל אחד מהם אומר משהו מבחינת ה-Permissions.
הראשון הוא ה-Others, השני הוא ה-Group, והשלישי הוא ה-Owner.
אז אם אנחנו רואים בדוגמה פה, אז יש לנו rwx, זה בעצם ה-Owner עצמו. אם אנחנו מסתכלים מצד שמאל לימינה, אז ה-rwx אומר שה-Owner יכול לקרוא, לכתוב, ולהריץ. ה-Group יכול לכתוב ולהריץ, והאחרים יכולים רק לקרוא.
יש לנו עוד נוטציה, Numeric Notation, שבעצם לכל שלשה כזאתי יש מספר שהוא מ-0 עד 7, מספר בבסיס 8, מספר אוקטלי, שכל מספר מייצג את ה-Permissions.
אז יש לנו: Read זה 4, Write זה 2, ו-Execute זה 1.
אז לדוגמה, התצוגה הסמלית הזאתי בעצם אומרת 754. אז 7 זה 4 + 2 + 1. 5 זה 4 + 1 (r ו-x). ו-4 זה r, ו-0, 0.
איך אני בעצם משנה את התצורה של ה-Permissions?
ה-Permissions עצמם משתנים באמצעות הפקודה שנקראת chmod.
וכל כך קשה לא להתבלבל בין שמוד ל-chmod. אני כותב את הפקודה הזאתי כבר כמה שנים, אבל כל פעם מחדש רושם chmode עם e, ואז כזה: אה, אוקיי, זה chmod.
כשאני משתמש, אני יכול להשתמש בשתי התצורות שדיברנו: אני יכול לעשות chmod ל-u+x, בעצם אני אשנה רק את ה-Permissions של ה-Owner עצמו, שזה השלושה הראשונים, ויוסיף להם executable, ש-+x זה executable.
או שאני יכול לתת ישר את התצורה המספרית עם chmod של 777, ואז אני אהפוך את כולם להיות read, write, executable.
יש לכם ב-exercises דוגמה שאתם יכולים לעשות. לא משהו רציני, רק למצוא את הפקודה הנכונה.
ויש לנו גם chown, בעצם change owner.
בזה אני יכול לשנות את ה-Owner מ-user group ל-user אחר. אני עושה chown עם השם של ה-user או נקודתיים ה-Group והקובץ עצמו, ואז זה בעצם משנה את ה-Owner.
איך אני יודע מי ה-Owner של file? עם `ls -la`.
אז אני רואה פה שה-Owner הוא aaa וה-user group היא גם באותו user group של aaa.
אם אנחנו נסתכל על hard links, אנחנו נראה ש-hard link הוא לא קובץ הרצה, גם אי אפשר להריץ אותו. אבל לעומת זאת symbolic link הוא איזשהו קובץ הרצה.
מאוד מעניין.
משהו אחד שאני חייב לכם ממקודם, כסתכלנו על הקובץ I2C, אז אם אתם רואים שפתאום הצטרף לנו פה איזשהו c, אז ה-c זה בעצם אומר שזה character device, זה בעצם איזשהו device שמחברים אליו ואנחנו מקבלים stream של מידע.
## Executable and Linkable Format (ELF)
הקובץ הבינארי של Linux שמשתמשים בו נקרא ELF, בעצם זה קיצור של Executable and Linkable Format.
זה בעצם נותן איזשהו פורמט שהוא סטנדרטי לכל קבצי ההרצה.
את הפורמט של ה-ELF אני יכול להריץ, אני יכול להשתמש בו כ-object, כ-shared library.
בעצם object code זה איזשהו קוד שהוא כבר מקומפל שאני יכול פשוט לחבר אותו. או shared library, שזה קובץ שהוא... נדבר על זה בשקופית הבאה.
ו-core dump, מה שיוצא כשהמערכת שלנו נופלת.
מאוד משתמשים בו והוא מאוד משותף בתוך הקהילה. הרבה פעמים משתמשים בו, בעצם יש לו אפשרות גם לתת את ה-metadata, כי יש לו header משלו, וגם בעצם הוא קובץ הרצה לכל דבר ועניין.
הוא מאוד נוח כך שאפשר להריץ אותו בכמה ארכיטקטורות שונות, זה לא קובץ בינארי ספציפי לארכיטקטורה.
והוא יכול להשתמש גם במערכות שהן 32-bit כמו שלנו או מערכות 64-bit כמו שזה המחשב.
יש לו כמה sections. אפשר לקרוא את ה-text, את ה-data ואת ה-rodata שלו כדי לקבל עוד מידע על הקובץ, ואפשר גם לטעון כל מיני סגמנטים ל-debugging and what not.
הוא תומך ב-dynamic linking, זאת אומרת שאם יש לי איזושהי ספרייה שאני משתמש בה בזמן ריצה אז הוא תומך בזה, כל מיני ספריות שנגמרות עם `.so` בסוף.
אז אילו פורמטים של קבצים? יש לנו קבצי executable, לדוגמה `ls`. אם נעשה `ls -lai /bin`, ואז `ls`, אז אנחנו נראה ש-`ls` הוא בעצם קובץ executable. הפקודה היא בעצם בינארי שאנחנו רואים אותו כאן.
אם אנחנו מדברים על קבצי object שאנחנו משתמשים בהם לקמפל, אם אנחנו נכנס לתוך `build`, אנחנו נראה שיש לנו כאן קבצי object. בעצם אנחנו עושים איתו linking בצורה סטטית.
קבצי shared libraries הם בעצם קבצים שצריכים להיות במערכת בזמן ריצה, לעומת הקבצים הסטטיים. זה בעצם חלק מהפורמט של ELF, וכדי להשתמש בהם אני צריך שהם יהיו על המערכת.
Core dump זה איזשהו קובץ שנוצר בזמן נפילה של process. אם מקנפגים את ה-build בתצורה מסוימת אפשר לפתוח core dump עם GDB ולראות מאיפה הוא נפל ואולי גם לעשות קצת אנליזה על המערכת עצמה.
## SoC (System on Chip)
אני ארוץ קצת כי אנחנו גם קצת דיברנו, וגם אני רוצה שנגיע לחלק שלכולם יהיה מותקן ושנספיק להעלות את ה-BeagleBone.
SoC, System on Chip. בעצם SoC זאת אומרת לא רק ה-CPU עצמו, זה עוד כל מיני חיבורים פריפריאליים של המערכת.
אם פעם הייתה לנו יחידת עיבוד ספציפית שנקראת CPU ועוד כל מיני קונטרולרים או יחידות ופריפריאלים שאנחנו היינו משתמשים כדי להתחבר למערכת, אז SoC בעצם מאחדת את הכל תחת ברזל אחד, אפשר להגיד.
בעצם הבלוק עצמו של המעבד, שלצידו יש כל מיני דברים כמו Wi-Fi, Bluetooth, פריפריאלים, memory controllers, ויחידות storage שאנחנו משתמשים.
בעצם מה שזה עושה זה מקטין לנו את הגודל של ה-board, ומקטין לנו את ה-power consumption כי אני לא צריך להתחיל להעביר data על חוטים יותר גדולים או על כל מיני מערכות יותר גדולות, ומקטין את כל הקומפוננטות של המערכת. החיסרון הוא שאם משהו נהרס צריך להחליף את הכל.
הרבה פעמים משתמשים בו בסמארטפונים, טאבלטים, מערכות embedded ו-IoT devices. בעצם אם אני רוצה שיהיה לי גם stack Bluetooth בתוך ה-CPU אז אני אחפש איזשהו SoC שתומך בזה.
אז מה ההבדל בין SoC ל-CPU או MCU?
MCU זה Microcontroller Unit. בעצם MCU קטן יותר, עם הרבה פחות יכולות והרבה פחות ריסורסים.
הוא בעצם מכיל איזושהי מערכת עיבוד פשוטה ואיזשהו flash על המערכת שמשם תרוץ התוכנית עצמה, RAM עליו, וכמה פריפריאלים בסיסיים מאוד כמו GPIO או טיימרים או איזשהם on-board communication בסיסיים, SPI, UART, I2C.
SoC לעומת זאת הרבה יותר חזק מ-MCU, לרוב יותר מהירים, ומכיל CPU עם איזשהן יחידות עיבוד הרבה יותר חזקות, לדוגמה ARM Cortex-A8 שאנחנו הולכים להשתמש בו. בעצם יש לו איזשהו interface ל-Dynamic RAM, ו-multimedia capabilities, שזה ה-eMMC שהוא built-in.
כשנבין ש-SoC זה לא אומר שהכל נמצא עליו, במקרה שלנו ל-BeagleBone Black יש גם כרטיס SD וגם כרטיס SD שהוא מוטמע במערכת. הכרטיס שמטמע במערכת נקרא eMMC, אז בעצם הוא על ה-board, אבל לא בתוך ה-CPU.
הרבה פעמים SoC יריצו מערכות הפעלה טיפה יותר מסובכות כמו Linux.
## SoC vs. MCU vs. CPU
CPU זה Central Processing Unit, זה בעצם הפרוססור עצמו. יש לו מערכות של cache, עם כמה שכבות של cache, תלוי בכמות ה-CPUs שיהיו ל-SoC. יש לו איזשהו קונטרולר של RAM ו-I/O controller בסיסי.
לעומת זאת ל-SoC, אם אנחנו משווים אותו ל-CPU, יכול להיות גם GPU, איזשהו DSP (שזה Digital Signal Processing Unit), ואיזושהי יחידה של security על ה-chip עצמו, לדוגמה CRM אם אנחנו מדברים על bootloaders. ואני פחות אצטרך לדאוג לכמה יחידות שונות של עיבוד שאני אציג אותן בצורה חיצונית.
אז איך נראה ההשוואה אחד בין השני? בעצם CPU זה רק ה-processing unit. לעומת זאת MCU זה CPU עם קונטרולרים ו-peripherals. ו-SoC יש לו גם CPU, יכול להיות לו גם GPU, גם peripherals, וגם connectivity, נגיד במקרה שלנו איזשהו NIC קטן שנמצא שם.
שימושים: CPU זה לפעולות גנריות, MCU זה לפעולות ספציפיות, ו-SoC זה לדברים שהם מולטי-פונקציונליים.
Power consumption: לעומת CPU ו-SoC, ל-MCU יש power consumption שהוא נמוך. SoC מצליחים לעבוד על בטריה או על משהו ממוצע.
דוגמה: פלאפון, כאילו, סמארטפון. זה בעצם, אם זה SoC, אז הצריכה היא נמוכה עד גבוהה. וכשאתה פותח מודם ואתה מרחיב תקשורת, אז אתה באמת בצריכה גבוהה, אבל בשאר הזמן אתה יכול ממש לישון.
עלויות: תלוי בהכול. CPU לרוב יהיה הרבה יותר יקר מ-MCU, ו-SoC זה מה שנמצא באמצע. כל מיני דוגמאות ל-CPU-ים: Intel Core i7, AMD Ryzen.
MCU-ים: האהוב על כולנו, STM32. בעצם אני חושב שכל אחד בתעשייה כמעט יגיע למצב שהוא נוגע בדבר הזה. יש AVR ו-ESP32. לדוגמה, ב-SoC בעצם Qualcomm Snapdragon שכולנו משתמשים בפלאפון, וה-Apple M1 לדוגמה.
## Toolchain
בואו נדבר על toolchain, כי אנחנו עוד רגע הולכים להתקין. אז מה נותן לנו toolchain? בעצם toolchain זה איזשהו סט של כלים שאנחנו יכולים להשתמש בו כדי להריץ קוד, ויכולים להשתמש בספריות שלו כדי לבנות, במקרה שלנו, לבנות U-Boot ואת ה-Linux עצמו.
בעצם קומפוננטות שיש, ויש עוד הרבה, אני לא עברתי על כולן כי זה גם משתנה. אז יש compiler, אז אנחנו ראינו GCC. יש גם compiler ספציפי ל-toolchain ספציפי שמיועד למערכת ספציפית, ואנחנו נדבר על ARM x86 או ARM בכללי. לדוגמה, GCC זה ה-GNU compiler.
יש לנו גם איזשהו assembler, שזה בעצם ההמרה שתתבצע מה-C או ה-C++ ל-machine code עצמו. זה בעצם איזשהו, אם אנחנו רוצים להשוות שנייה, איזשהו assembly, יש את ה-assembly של x86, ולעומת זאת יש את ה-assembler, סליחה, ה-assembler של x86, ולעומת זאת יש את ה-assembler של ARM. אז אם אני משתמש באיזשהו toolchain שהוא cross-compiler שיודע לקחת קוד מ-x86 ולהפוך אותו לקוד שמיועד בארכיטקטורה של ARM, אז זה ה-assembler שאנחנו משתמשים.
Linker, שזה הרכיב שעושה את ה-linking של הספריות בזמן הקומפילציה, ויוצר מזה קובץ executable אחד. לדוגמה ld, שזה ה-linker של GNU.
Debugger, דיברנו על GDB, שזה גם מגיע. כל מיני ספריות שהן precompiled, כמו לדוגמה גרסה מסוימת של libc או glibc אם אנחנו משתמשים בזה.
ו-build tools שמגיעים שאנחנו משתמשים, אז זה Make, CMake, Ninja אם יצא למישהו לעבוד עם זה, ויש עוד כמה מוכרים. שכחתי כמה.
אז למה אנחנו בעצם צריכים toolchain למערכות embedded? בעצם הסט כלים הזה עוזר לנו לקחת את הקוד שלנו מ-source code ולהפוך אותו ל-executable שמתאים לנו ספציפית למערכת שלנו. לדוגמה, cross-compilation...
סליחה, אני ארוץ קצת כדי שנספיק את הכול היום.
Cross-compilation במערכות embedded עם כמה ארכיטקטורות שונות, לדוגמה ARM או איזושהי ארכיטקטורה שצוברת פופולריות בשנים האחרונות וגדלה, RISC-V או RISC-5, תלוי איך רוצים לקרוא לזה, בעצם יוצרים לנו גישה יותר נוחה לעבוד בכמה מערכות פיתוח.
למה צריך cross-compiler? צריך אותו כדי לג'נרט קוד ספציפי לארכיטקטורה ספציפית. זאת אומרת, אם אני משתמש ב-cross-compiler GCC ל-ARM, אז אני אדבר על `arm-none-eabi-gcc`. ספציפית בזה אנחנו לא משתמשים, אנחנו נשתמש באחד שנותן לנו Yocto ואחד שנותן לנו ה-toolchain שבחרתי לקורס.
מה עוד נותן לנו? נותן לנו איזושהי רמה של אופטימיזציות, בעצם רמה שאנחנו יכולים לשפר בה את ה-performance שלנו. איזושהי אופטימיזציה שנותן לנו לא רק ה-GCC, אלא ה-toolchain עצמו. זאת אומרת, `-Os` לעשות אופטימיזציה של הגודל של המערכת, `-O1`, `-O2`, `-O3`. אני לא נוגע בזה בקורס הזה, אבל אם זה מישהו מעניין, שווה לקרוא.
נמשיך הלאה. בעצם את האפשרות לעשות debugging אוטומטי, בעצם אם אנחנו משתמשים ב-GDB עם חומרה שתומכת בזה ישירות, כמו JTAG או SWD.
Linking ו-memory management במערכות embedded נותן את האפשרות להשתמש אוטומטית ב-controllers של זיכרון, וסטנדרטיזציה.
## Linaro
ה-toolchain שאנחנו הולכים להשתמש בו הוא של Linaro, אז תפסתי את עצמי שנייה לכתוב על Linaro עצמם.
אז Linaro זה בעצם איזשהו קולקטיב של מפתחים שמבצעים open-source software למערכות ARM. כותבים בעצם קוד שהוא optimized ספציפית ל-ARM, לארכיטקטורה, בעצם לאסמבלר של ARM, כולל toolchain ו-kernels שמותאמים ל-ARM, וספריות גם כן.
המטרה שלהם בעצם זה לפתח את כל העולם הזה של מערכות שהן על ARM, ולהוסיף עוד פיתוח ולשפר את ה-innovation בעולם.
קומפוננטות ספציפיות שאנחנו יכולים להשתמש בהן של Linaro, אז אנחנו הולכים להשתמש בקומפיילר שנקרא `arm-linux-gnueabihf`, שזה בעצם ל-hard float system, ואני חושב שמה שזה אומר זה ה-FPU. FPU? Floating Point Unit, לא? כן, כן, FPU. בעצם זה בדיוק, כמו שרציתי להגיד, איזושהי חומרה ספציפית שהיא עושה לנו את כל הפעולות של ה-floating point. בעצם פעולות של floating point, כמו חיבור, חילוק וכאלה, הן פעולות שהן מאוד כבדות. ואם אנחנו נעשה אותן בחומרה ספציפית, זה יהיה הרבה יותר מהיר. ויש לנו בעצם מערכות שלמות שתומכות בזה.
אותו דבר יש לנו גם ל-soft float system שהם בלי FPU.
כל מיני debuggers ספציפיים שמתאימים למערכת שאנחנו עובדים, ספריות ו-build tools שמשתמשים בהם, נגיע בכולם.
## Practical Section
והגענו לחלק הפרקטי של השיעור.
אז בואו נתחיל מלהתקין Minicom. אני יודע שאין לכם חומרות, אז ברגע ש... סליחה.
ברגע שיהיה לכם חומרה, אז תצטרכו לחזור לחלק הזה של השיעור. בגלל זה אני שואל כאילו על החומרה, כדי שאני אדע כמה מזה לחכות וכמה מזה צריך להתחיל לאסוף.
אז מה זה Minicom? Minicom זה איזשהו כלי לקריאה של מידע סריאלי. לפני הכול, אני רק אוודא שאני עברתי על כל ה... על כל הדברים שהתקנתי. את זה אני יכול להעיף משם. זה כל ה-class commands שהרצתי, שיהיה לכם גם, אז אני שם לכם פה. אה, אוקיי, זה ההתקנה עצמה. בסדר.
אז Minicom. Minicom בעצם זה איזושהי תוכנית מאוד קטנה שאנחנו יכולים להשתמש בה לקריאת מידע סריאלי. בעצם התוכנה עצמה בעצם עושה אינטראקציה עם כל מיני devices על ממשק סריאלי. למה אנחנו משתמשים ב-Minicom? כי זה מאוד נוח ומאוד קטן, ואפשר להשתמש בזה מתוך ה-virtual machine כדי לעבוד על זה. בעצם נותן לנו איזשהו טרמינל, ונראה איך להתקין. אז אם אין לכם מותקן את ה-Minicom, אז אנחנו נעבור עכשיו ל-VM שלי, ואני ארצה להראות לכם שזה כבר עובד.
אתם רואים את ה-VM, נכון? אם אני רוצה לפתוח טרמינל, אז אני עושה Ctrl+Alt+T, ו-`sudo apt install minicom`. במקרה שלי זה כבר מותקן. אכן כן, כבר יש לי אותו.
אם אני רוצה להגדיר את ה... רגע שנייה, שנייה לפני זה. ל-BeagleBone...
- שאלות?
- אפשר להריץ את זה גם מה-VS Code?
- זהו, שלא. אפשר להריץ את זה מה-VS Code, צריך להשתמש באיזשהו טרמינל ספציפי, אני לא עשיתי את זה. אני חושב שזה נקרא TRM או איזושהי תמיכה של טרמינל.
- לא, אם התקנת את ה-Minicom...
- לא, בגדול אתה לא מתקין את זה מתוך ה-VS Code. כשאתה מתקין משהו ב-VS Code זה מותקן במכונה הווירטואלית.
- לא, בטרמינל שאנחנו בתוך המכונה הווירטואלית, לא?
- אני אראה, אני אראה בדיוק מה קורה כשמנסים להשתמש בזה, אז הוא אומר שהחלון קטן מדי. בוא ננסה לעשות `sudo minicom`.
- לא, להתקין.
- להתקין אפשר מכל מקום. ההתקנה עצמה אתה יכול לעשות אותה מתוך הטרמינל, אבל השימוש אתה צריך לצאת מתוך ה-VM עצמו או מהמכונה אם אתה עובד על מכונה עצמה.
אני חושב, יודע מה, בוא נסתכל אם נעשה מספיק גדולה. `sudo minicom`. אה, אוקיי. אם מקטינים מספיק את המסך, זה כן עובד. אני לא יודע למה, אבל אני שנייה אצא. Ctrl+A, Ctrl+... Ctrl+A Q כדי לצאת. אבל בוא נגדיל את זה חזרה.
לפני זה, אני רוצה רק לדבר על ה... על ה-UART. אז אני לא אמרתי לכם לקנות כזה דבר, לא אמרתי לכם להשתמש. אני משתמש באיזשהו מתאם של FTDI. הוא סתם יקר, לדעתי. אני, off the record, אגיד שדפקתי אותו מהעבודה שלי כשהעזבתי. אז לי יש כזה, אבל... טוב שזה מוקלט, זה בסדר. אז לי יש כזה. פשוט אם אתם רוצים, תחפשו איזשהו מתאם TTL ל-232, 3.3V. אם אתם ממש רוצים שזה... אני יכול לחפש לכם... לחפש לכם אחד. אגב, ב-Mouser הוא עולה כמעט כמו BeagleBone Black. כאילו, זה פשוט... פשוט שוד.
- מה זה עושה? לא הבנתי מה זה עושה.
- FTDI יש כאלה בסנטים באליאקספרס.
- בערך, בעליאקספרס עובדים מעולה.
- מה זה עושה? זה בעצם ממיר UART, שזה תקשורת סריאלית, ל-USB. ואז אני יכול לחבר את זה ל-USB במחשב, בעצם יפתח לי COM port.
- הבנתי.
- יש כאלה באליאקספרס בגרושים.
- באליאקספרס הוא ממש זול. לא ממליץ לקנות של FTDI, הם עובדים מעולה, אבל הם יקרים. אז פשוט מישהו מגיע לתעשייה ומצליח לשים את ידו על FTDI, לא לשחרר.
אוקיי, אז אתם יודעים, אפילו פחות משמינית מהמחיר. אז אם אנחנו כבר כאן, בוא ננסה כבר מהחלון הזה כי הצלחנו, אז `sudo minicom -s`.
אה, רגע, לפני הכול. ברגע שאני מחבר את ה-USB serial converter שציינו, אז אני בעצם צריך להעביר אותו לשתף אותו עם המכונה הווירטואלית. אז איך אני עושה את זה? אני בעצם הולך לכאן, Devices -> USB, ואני מסמן אותו ב-V, כפי שזה כבר מסומן. אז איך אני קורא אם הוא נמצא? אז אני יכול לעשות פשוט `ls`... רגע, לפני הכול. `ls`...
- רגע, יש לך FTDI שמחובר ל-BeagleBone כרגע?
- יש לי FTDI שמחובר ל-UART מצד אחד, מצד שני למחשב.
- ל-UART של ה-BeagleBone, כן?
- כן. יש ל-BeagleBone... אולי אני אראה לכם את זה... BeagleBone debug port. בוא נראה קצת כאן. יש ממש פורט שנקרא UART0, שאליו מחברים בדיוק כזה.
- מחובר ישירות ל... USB?
- למה בדיוק?
- ל-BeagleBone.
- ל-BeagleBone יש גם יציאת USB, אבל אתה לא תוכל לעשות... לראות את ה... את ה-logs שיוצאים מהמכשיר עצמו. בעצם זה ישירות מקושר ל-bootloader. אז עליה מה-power on, משלב מסוים ב-power on...
- כאילו זה לא עניין של לחבר בדאטה ל-device tree שה-logs יצאו ל-USB? או שזה בשלב מאוחר יותר?
- אני... זה כאילו hardware. ממש חומרתי יוצא משם. ה-logs... ואני אגע בזה שבוע הבא, אבל ב-bootloader הראשון שנמצא על ה-MCU... על ה-SoC עצמו, מאתחלים את ה-UART הזה, וזה אפילו נעשה לפני ה-U-Boot, אוקיי?
- מבין.
- אז בעצם אם אנחנו מדברים ברמת ה-device tree, ואתה כבר מקדים את המאוחר, אז אם אנחנו ברמת ה-device tree, אז זה עדיין device tree לפני U-Boot. ה-device tree בעצם הוא מה-bootloader השני.
- כאילו ה-bootloader הראשון אתה מדבר עליו, אתה לא יכול לשנות אותו?
- לא, אני לא יכול לשנות אותו. האמת היא שאני כן בונה אותו, אז אולי אפשר לחפש שם איזה משהו וזה, אבל...
אני לא יודע. אני גם לא חושב שיש לי את ה-CPU... אני לא חושב שגם יש לי את ה-resources של ה-CPU אקטיביים בשלב הזה. נכון, אני מעלה בכל שלב ב-U-Boot, אני מעלה עוד יכולות של ה-CPU, ואז יש לי עוד פונקציונליות. ה-USB controller הוא כנראה לא נמצא ברמה הזאתי, בסדר?
אני יכול להסתכל על זה עד השיעור הבא.
פחות מעניין. אני מבין שזה כאילו דברים של השיעור הבא, אני אחכה עם זה. [לא ברור] ההבדל בין הדברים האלה, אז אני אקרא כבר.
אתם מוזמנים גם לשתף אותנו.
בגדול, רואים מכאן את הפורט. יש בעצם כמה פינים, שנראה לי שכאן רואים את זה הכי טוב, באיזשהו serial header שעליו אנחנו מתחברים. אני יכול לנתק אחר כך ולהראות לכם איך זה נראה.
יש גם איזשהו פין של power, של 5V. אני לא יודע אם זה אמור להיות בעיה, אני ניתקתי אותו, אבל פשוט אם מישהו קונה כבר עם ה-header הזה של ה-6 פינים, אז רק שימו לב ש-to be on the safe side אני מנתק את ה-power, כי שרפתי כמה FTDI בחיים שלי.
## הגדרת Minicom עבור BeagleBone Black
בואו נמשיך. אז אני רוצה לראות לאיזה USB TTY הוא ממופה. אז אני אעשה `ls` לתוך ה-device path ונראה מה יש. אז אני רואה שבעצם נוצר `ttyUSB0`, אוקיי?
אז איך אני מוסיף את ה-device הזה? אני צריך לקנפג את ה-Minicom. אז `sudo minicom -s`, והוא פותח לי את החלון הזה. אני אגדיל אותו קצת כדי שיהיה לכולנו יותר נוח לראות.
ובתוך החלון הזה אני בעצם רואה את ההגדרות של ה-serial port ומכאן אני יכול לשנות. ה-serial device עצמו, ברגע שאני עושה A אז אני יכול לכתוב את ה-path. במקרה שלנו זה `/dev/ttyUSB0`. אין השלמה אוטומטית.
אחר כך אנחנו יכולים לדבר על כל מיני הגדרות serial-יות. אנחנו לא נכנסים להגדרות ה-serial-יות האלה, זה בשיעורי חומרה, או שיש איזשהו שיעור במאגר, לא השבוע אלא שבוע הבא שכן ניגע בהגדרות serial-יות, אבל אפשר תמיד לקרוא על זה.
ה-baud rate שמשתמשים ב-BeagleBone זה 115200, שזה baud rate יחסית גבוה ל-UART, אבל יחסית נמוך באופן כללי לאיזשהו bandwidth. משתמשים ב-8 data bits, בלי parity, ועם 1 stop bit. אנחנו לא משתמשים ב-hardware flow control, למרות שהוא מחווט מבחינתנו.
אם אני רוצה לשנות את זה אז אני לוחץ על E, אוקיי? ולפי זה, נגיד אם אני אנסה לשנות את זה לגודל של 7 data bits עם even parity ו-1 stop bit, אז אני עושה R, ואני רואה בדיוק כאן שזה השתנה. אני אחזיר ל-Q כי אני רוצה שזה יעבוד, וכשאני רוצה לצאת אני לוחץ על Enter. אם אנחנו משתמשים רק ב-setup הזה, אז אני שומר אותו כ-default: Save setup as dfl, ויוצא.
כדי להריץ, נקדים קצת את המאוחר ונראה כאן כמה דברים, קצת recap של הקורס על ה-BeagleBone עצמו. אז `sudo minicom`, והוא ייתן לי את המסך עצמו. אני ארסט פה, אצלי הוא מחובר. יש לי פה איזושהי בעיה של הזחת הדמות, בואו נתעלם מזה. hard reset, אני חושב שזה קרה לי כבר היום.
והנה, אנחנו יכולים ממש לראות את תהליך העלייה. האמת היא שאני רואה שזה פה מה-U-Boot, אבל אם אני זוכר נכון, או שאנחנו גם נראה את זה בשיעור הבא, נעצר עוד מלפני ה-U-Boot. בעצם מפה הוא עולה, ואנחנו מגיעים לעלייה של המערכת עצמה.
## התקנת Toolchain
בזמן שזה עולה, בואו נעשה את ההתקנה של ה-toolchain שלנו. בתוך התיקייה של השיעור עצמו שמתי לכם את התיקייה שנקראת `toolchain`. לכם זה לא נראה יפה כמו אצלי, כי אתם עוד לא עשיתם את מה שצריך לעשות.
כדי לפתוח טרמינל חדש, Ctrl+Shift+~ פותח לי טרמינל חדש. אני אזיז את זה ימינה קצת. ניכנס לתוך התיקייה של ה-toolchain:
`cd lesson_1/toolchain`
`ls`, לכם יופיע רק `get_toolchain.sh`.
ברגע שאני מריץ `get_toolchain.sh`, אנחנו עושים את הפעולות הבאות. זה מה שנמצא בתוך הסקריפט, זה סקריפט די פשוט. כל מה שהוא עושה זה הוא הולך לתוך ה-downloads של Linaro (Linaro זה ה-toolchain שאנחנו משתמשים בו ספציפית), והוא נותן לי גישה ל-toolchain עצמו. אני הורדתי את גרסה 14, לכם בשיעורי הבית יש להוריד גרסה אחרת.
אנחנו משתמשים ב-`arm-linux-gnueabihf`, שזה ה-hardware floating point unit. תלוי במחשב שהוא ה-native שלכם, אתם צריכים לבחור: אם אתם מקמפלים מתוך ARM ואתם עובדים מתוך מחשב שהוא ARM, אתם צריכים להשתמש ב-`gcc-linaro-14...aarch64`, שזה בעצם ARM 64, שזאת הארכיטקטורה שעליה אנחנו בונים. אם אנחנו משתמשים במחשבים של x86, אז אנחנו צריכים להוריד את התחתון יותר, את `gcc-linaro-14...x86_64`.
אנחנו לא צריכים לעשות את זה בשום פקודה מיוחדת כי עשיתי את זה כאן כבר. אז אני יוצר איזושהי תיקייה שנקראת בשם של ההפצה (תכלס תצטרכו לקרוא לזה בשם אחר, אני פשוט רציתי שיהיה רשום השם היפה של כל ה-string היפה הזה שפה). אני לוקח את הגרסה עצמה ואני מוריד אותה עם `wget`. אני אחר כך עושה `extract`, נכנס לתוך התיקייה וממש יוצר symbolic link מה-GCC עצמו של המערכת ל-GCC של ה-ARM. אז בואו נעשה את זה ביחד: `./get_toolchain.sh`.
ארז, אני לא רואה את `toolchain` בתוך `lesson_1`.
גם לי אין.
אוקיי, אוקיי. באיזה branch אתה? באיזה tag אתה?
אני ב-main, לדעתי.
יכול להיות שאישית לא הוספתי את `toolchain`, בוא נראה. הכנסתי אותו ל-`.gitignore` ובגלל זה. שנייה, אני אסדר את זה.
אוקיי, אז אם תעשו `git pull` מה-main, אז יהיה לכם את הקוד. טעות שלי ב-`.gitignore`. אני אטפל ב-`.gitignore` אחר כך.
אז בואו נחזור. `get_toolchain` בעצם מביא את הכל ועושה symbolic link. אני אמחק שנייה את ה-symbolic link שלי. אם אתם עובדים ישירות על המכונה (לא דרך VM), תיזהרו עם זה, כי מה שאנחנו נעשה עכשיו טיפה מסוכן.
נריץ את זה מחדש. אסיר את ה-symbolic link אצלי במערכת: `rm -rf` לזה. עכשיו נעשה `./get_toolchain.sh`. אני מוריד אותו שוב, לוקח את הכל לתוך התיקייה עצמה. אחר כך אני עושה `extract` לזה במערכת, נכנס לתוך התיקייה עצמה ל-`bin`, ומ-`bin` אני עושה symbolic link מהגרסה הספציפית ל-compiler עצמו. ברגע שזה יסתיים, אז זה כבר יעבוד.
אבל מה שיהיה חסר לנו זה להוסיף את זה ל-PATH של ה-shell של המערכת. אצלכם זה פשוט יעבוד.
עכשיו בואו נעשה את הפקודה הבאה: אנחנו רוצים להוסיף את ה-PATH לתוך ה-PATH של המערכת, אז נעשה `nano ~/.bashrc`. נירד לסוף עם Ctrl+End, נעתיק את הפקודה הזאתי עצמה, ונעשה Shift+Insert. אם זה כבר קיים, לא להוסיף את זה, כי זה יכול להיות הרסני.
אנחנו מסיימים להעתיק את הכל, עושים Ctrl+S, Ctrl+X וסייימנו. אבל גם עכשיו זה עדיין לא יעבוד, בשביל זה אנחנו צריכים לרסט את המערכת. אחרי ריסט זה כבר יעבוד.
אני יכול לעשות `whereis` בשביל לבחון את ה-symbolic link. ובשביל ה-version עצמו, כדי לראות שהצלחתי להעביר, אז אני רואה שאני נמצא בגרסה 14, אוקיי?
דבר אחד אחרון שרציתי להראות: נחזור ל-BeagleBone הבסיסי, להראות משהו ששמתי כאן. כדי להתחבר, ה-default זה debian, והסיסמה היא `temppwd`.
רציתי שנריץ כל מיני דברים מהשיעור עצמו, ספציפית על זה, אז תראו את ה-file system. ה-file system שלנו, `pwd`, מורכב מ-`boot`, `dev`, `etc`, `home`, `media`, `mnt`, `opt`, `proc` (של ה-processes), `run` והכל.
אם אנחנו רוצים להסתכל על ה-devices, אז ל-BeagleBone עצמו יש שלושה interface-ים של $I^2C$. symbolic linking אנחנו יכולים לעשות אותו דבר.
## סיכום ונושאים לשיעור הבא
זה הכל להיום. בשיעור הבא אנחנו נדבר על processes שרצים, לא ברמה המתקדמת של המשך הקורס.
אבל רק באוברוויו, כי רציתי לסיים להגיע גם לחלק המעשי הפרקטי.
נדבר על סיגנלים של Linux, נדבר על IPC בעצם בקטנה, לא ניכנס לזה יותר מדי, את זה אנחנו נעשה בהמשך. קצת Error handling בתוך Linux. ניכנס ל-File I/O Basics, בעצם לכל מיני כתיבה לקבצים, קריאה וכתיבה. נדבר על ה-BeagleBone Black boot process, bootloaders בכללי, וספציפית על ה-U-Boot שלנו.
וזה הכל להיום. שאלות וכאלה... אני לא אעצור את ההקלטה, אתן לשחר לערוך את זה אחר כך, אבל שאלות, כן.
- אתה עכשיו בתוך ה-BeagleBone?
- כן.
- אנחנו גם נהיה בתוך ה-BeagleBone הווירטואלי שלנו?
- כן, יהיה לכם QEMU. בעצם אמיולטור שיעשה את אותו דבר, מינוס החומרה. לחומרה יהיה סימולטור.
- אז זה כאילו, זה לא השיעורי בית שלנו? להגיע למצב שאתה עכשיו?
- לא, אין לכם את החומרה. השיעורי בית שלכם, אתם רוצים שאני אעבור על השיעורי בית? השיעורי בית...
- כי אני לא התקנתי, לא התקנתי את ה-toolchain הזה.
- אז תנסה לחזור על זה, כי חלק מהשיעורי בית זה להתקין toolchain נוסף, להתקין גרסה אחרת של toolchain. בעצם המטלות של היום זה בעצם לכתוב איזשהי תוכנית שמשתמשת ב-API וב-ABI כלשהו, לעשות איזשהו hard link לתוכנית עצמה, ולתת לתוכנית כאילו others write permissions, ואם אתם מצליחים להגיע למצב שאתם מורידים ומתקינים toolchain אחר. זה הכל, לא יותר מדי, אבל כל מיני דברים שאמרנו עליהם וכדאי לנסות.
- את ה-toolchain שעשית עכשיו, מי שלא הספיק עכשיו גם כאילו צריך לעשות את זה?
- כן, גם אני לא הספקתי.
- איפה אנחנו מתקינים את זה? אני רוצה להבין, על ה-Virtual Machine שלי?
- כן, ה-toolchain עצמו מותקן על ה-Virtual Machine. עם ה-toolchain אני אוכל לבנות את כל ה-Linux, את ה-U-Boot וכל מיני תוכניות אם אני רוצה ספציפית. וגם device drivers אני בונה עם ה-toolchain. זה לא בסקופ של השיעור, אבל גם כן בשביל זה. לא של הקורס הזה.
- אז אתה יכול להוסיף אולי איזשהו צעד שם לגבי ההתקנה שעכשיו עשית שלא הספקנו?
- כל ה-class commands נמצאים כאן.
- אוקיי, כי אמרת שם איזה משהו להיזהר, לא לעשות את זה, זה הרסני...
- אה, את ה-bashrc. הכתיבה ככה ל-bashrc. כל פעם שהמערכת עולה, אז היא מריצה את זה.
- אה, בעריכה של... אוקיי, אוקיי.
- כי ב-get_toolchain.sh, כשאתה מוריד את ה-toolchain אתה מוריד פה by default ראיתי למעלה את ה-ARM, ואם אני רץ על המחשב שלי שהוא Intel, אז כדאי שאני אשנה שם, לא?
- לא, אם אתה רץ על המחשב שלך, אז אתה צריך לחצות ארכיטקטורה. ה-ARM עצמו זה אם המחשב שלך, אם ה-VM או המחשב שלך הוא לא ARM, אוקיי? ה-instruction set של המחשבים שכולנו משתמשים כנראה הוא x86.
- אה, סליחה, כן.
- אז כל הרצף צעדים של להתקין את זה, זה בעצם בקובץ הזה. אוקיי.
- נכון, חוץ מזה, ואני אשים פה הערה.
- אז אני מריץ את ה-get_toolchain, את הסקריפט הזה, ואז עושה ידנית את הפקודות שבתוך class_comments?
- לא את כולן, אבל את כל מה ש... כן.
- כאילו אני מוסיף ל-PATH את הדבר הזה, ב-bashrc או במה שאני משתמש את ה-PATH, ואז עושה reset ובודק עם ה-version שהתקנתי בעצם.
- כן, אפשר גם להריץ ישירות את זה אם אתה לא רוצה שזה יהיה חלק מה-PATH ספציפית. אוקיי, זה דרך לבדוק את זה, ואחרי זה נמצא פה.
## סיכום ושאלות
מעולה. אז ניפגש בשבוע הבא, נכון? כן, אין שום שינוי, שום דבר. אם אתם נתקלים במשהו, או אם יש משהו שמסקרן, או אם יש משהו שאתם רוצים אקסטרה, תדברו איתי במהלך השבוע.
מעולה. אני אעצור את ההקלטה עצמה. רגע, זה pause ל-share... יכול להיות שלא הקלטתי? בסדר גמור. טוב, אם לא הקלטתי... אה לא, כן הקלטתי. בסדר.