לדלג לתוכן

דרייברים בלינוקס - מבוא לתוכנה שמדברת עם החומרה בשבילכם

כל חתיכת חומרה שמחוברת למחשב, כרטיס רשת, דיסק, עכבר, כרטיס מסך, מדברת בפרוטוקול חשמלי משלה, שונה לגמרי מכל התקן אחר. אם כל תוכנית הייתה צריכה לדעת בדיוק איך לדבר עם כל התקן פיזי, אף תוכנה לא הייתה נכתבת פעם אחת ועובדת על כל המחשבים. דרייבר הוא בדיוק המתרגם שפותר את הבעיה הזאת - קוד ייעודי שיודע לדבר עם חתיכת חומרה ספציפית, ולחשוף אותה לשאר המערכת בממשק אחיד ומוכר.

למה דרייבר בכלל צריך לרוץ בתוך הקרנל

חומרה מדברת דרך רגיסטרים ופסיקות חומרה ברמה נמוכה מאוד, וגישה ישירה אליה דורשת הרשאות שהקרנל בלבד מחזיק. בגלל זה, רוב הדרייברים בלינוקס רצים בתוך מרחב הקרנל עצמו, לא בתוך תוכנית רגילה במרחב המשתמש. זה נותן להם גישה מלאה לחומרה, אבל זה גם המחיר - באג בדרייבר, בניגוד לבאג בתוכנית רגילה שקורסת בשקט, יכול להפיל את כל המערכת, בדיוק כי הוא רץ באותה רמת הרשאה כמו הקרנל עצמו.

שני סוגים עיקריים - התקני תווים והתקני בלוקים

לינוקס מסווג התקנים לכמה קטגוריות עיקריות, כשהנפוצות ביותר הן:

  • התקני תווים - character devices. מעבירים נתונים כזרם רציף, בית אחר בית, בלי אפשרות לקפוץ קדימה ואחורה באופן שרירותי. מקלדת, עכבר, וגם התקנים וירטואליים כמו /dev/random, כולם דוגמאות להתקני תווים.
  • התקני בלוקים - block devices. מעבירים נתונים בבלוקים בגודל קבוע, ומאפשרים גישה אקראית - לקפוץ ישר לכל מיקום בהתקן, לא רק לקרוא ברצף. דיסקים קשיחים וכונני SSD הם הדוגמה הקלאסית, וזו הסיבה שמערכות קבצים כמו ext4 יכולות בכלל להתקיים - הן דורשות גישה אקראית לבלוקים.

בשני המקרים, הדרייבר חושף את ההתקן דרך אותה שכבת VFS שכבר הכרנו - התקן מופיע בתור קובץ תחת /dev, ואפשר לגשת אליו עם open, read ו-write, בדיוק כמו קובץ רגיל על הדיסק.

מספרי major ו-minor - איך המערכת יודעת לאיזה דרייבר לפנות

כל התקן תחת /dev מזוהה בפועל על ידי שני מספרים - מספר major, שמזהה איזה דרייבר אחראי על ההתקן, ומספר minor, שמזהה איזה התקן ספציפי מתוך כמה שאותו דרייבר מנהל. כשתוכנית פותחת קובץ תחת /dev, הקרנל משתמש במספרים האלה כדי לדעת בדיוק לאיזה קוד דרייבר להפנות את הבקשה, ולאיזו יחידת חומרה ספציפית מתוכם.

מודולי קרנל - דרייברים שנטענים בלי אתחול מחדש

אחד היתרונות הגדולים של לינוקס הוא שדרייברים לא חייבים להיות מקומפלים בתוך הקרנל מראש. הם יכולים להיות מודולים - קבצים נפרדים שנטענים ומוסרים בזמן ריצה, בלי אתחול מחדש בכלל. הפקודה insmod טוענת מודול לקרנל, rmmod מסירה אותו, ו-modprobe עושה את שתי הפעולות בצורה חכמה יותר, כולל טיפול אוטומטי בתלויות בין מודולים. זו בדיוק הסיבה שחיבור כונן USB חדש למחשב לינוקס, לרוב, פשוט "עובד" מיד - הקרנל מזהה את סוג ההתקן, וטוען את המודול המתאים אוטומטית.

מה דרייבר צריך לחשוף

בדומה למה שראינו כשדיברנו על VFS, גם דרייבר בונה את הממשק שלו סביב מבנה file_operations - הוא מממש את הפעולות הרלוונטיות, open, read, write, ioctl לפעולות ייחודיות שלא מתאימות לממשק הכללי, ורושם אותן אצל הקרנל. משם ואילך, כל תוכנית שפותחת את ההתקן שלו, מדברת עם הפעולות האלה בלי לדעת שום פרט טכני על החומרה מתחת.

למה כדאי להבין את השכבה הזאת

הבנת דרייברים היא הבסיס להבנה איך חומרה ותוכנה נפגשות בפועל, ולמה חלק ניכר מהחולשות הכי מסוכנות בעולם נמצאות דווקא בדרייברים - קוד שרץ בהרשאות הגבוהות ביותר, לפעמים נכתב על ידי צד שלישי, ולפעמים לא נבדק באותה קפדנות כמו הקרנל המרכזי.

זה בדיוק סוג ההבנה שאנחנו בונים בקורס ליבת המחשב - איך חומרה ותוכנה מתחברות בפועל, ולא רק איך משתמשים בהתקן מוגמר.

ואם רעיון של דרייבר עדיין נשמע רחוק, יש קהילה שתשמח לעזור לחבר את הנקודות.

הצטרפו לקהילה בדיסקורד

לסיכום

דרייבר הוא המתרגם בין חומרה ספציפית לממשק אחיד שכל שאר המערכת יודעת לדבר איתו, בדרך כלל דרך /dev וממשק קבצים רגיל. הוא רץ בדרך כלל בתוך מרחב הקרנל, מסווג כהתקן תווים או בלוקים, ומזוהה דרך מספרי major ו-minor. וביכולת לטעון ולהסיר אותו כמודול, בלי אתחול מחדש, בדיוק טמון חלק גדול מהגמישות שהופכת את לינוקס לתומך בכזה מגוון עצום של חומרה.