330 likes | 538 Views
מערכות הפעלה. תרגול 11 – מבוא למנהלי התקנים ב- Linux. Linux Device Drivers, 2 nd Edition Alessandro Rubini & Jonathan Corbet O’Reilly http://www.xml.com/ldd/chapter/book/. תוכן התרגול. מבוא למנהלי התקנים ומודולים ב- Linux כתיבה והרצה של מודולים התקני תווים. מה זה מנהל התקן?.
E N D
מערכות הפעלה תרגול 11 –מבוא למנהלי התקנים ב-Linux Linux Device Drivers, 2nd Edition Alessandro Rubini & Jonathan Corbet O’Reilly http://www.xml.com/ldd/chapter/book/
תוכן התרגול • מבוא למנהלי התקנים ומודולים ב-Linux • כתיבה והרצה של מודולים • התקני תווים מערכות הפעלה - תרגול 11
מה זה מנהל התקן? • שכבת תוכנה החוצצת בין החומרה לבין האפליקציה • מנהל ההתקן מחביא את הפרטים הנוגעים לפעולת החומרה • המשתמש מבצע פעולות באמצעות אוסף פונקציות סטנדרטי שאינו תלוי בהתקן מסוים • תפקיד מנהל ההתקן למפות את הפונקציות הסטנדרטיות לפעולות המבוצעות על התקן החומרה • גישה מודולרית - ניתן לבנות מנהל התקן בנפרד משאר הגרעין, ואז "לחבר" אותו בזמן ריצה, בשעת הצורך מערכות הפעלה - תרגול 11
מודולים ב-Linux • ל-Linux יש יכולת להרחיב בזמן ריצה את אוסף הפעולות שמספק הגרעין • קטע קוד שניתן להוסיף לגרעין בזמן ריצה נקרא מודול • ספריה משותפת הנטענת (מקושרת בזמן ריצה) לגרעין • רק משתמשים מורשים יכולים לטעון מודולים • ישנה חלוקה של מודולים לפי התפקודיות שהם מספקים: • התקני תווים • התקני בלוקים • ממשקי רשת • ועוד (USB, SCSI,...) מערכות הפעלה - תרגול 11
התקני תווים ובלוקים • התקן תווים - התקן שניגשים אליו כאל רצף של בתים (כמו קובץ) • מסוף, פורט סדרתי • בדרך כלל ניתן לגשת להתקן תווים רק באופן סדרתי • התקני בלוקים - התקן שניתן לתקשר איתו רק בכפולות של בלוק (למשל KByte) • בפועל Linux מאפשרת לקרוא מהתקני בלוקים גם בתים בודדים • משמשים, בין השאר, לייצג מערכות קבצים • ניתן לגשת להתקנים אלו דרך מערכת הקבצים (/dev) מערכות הפעלה - תרגול 11
ממשקי רשת • התקנים המסוגלים להחליף מידע עם מחשבים אחרים • באחריותם לשלוח ולקבל חבילות מידע • אינם מיוצגים במערכת הקבצים • ישנן קריאות מערכת ייחודיות לטיפול בממשקי רשת מערכות הפעלה - תרגול 11
ענייני אבטחה • מודול רץ ב-kernel mode • גישה למבני הנתונים בגרעין • חשוב להימנע מחורי אבטחה במודול • למשל buffer overrun בתכנות ב-c • הקפדה על אתחול משתנים • יש להתייחס לקלט משתמש בחשדנות • במידת הצורך, הגבלת השימוש במודול למשתמשים מורשים מערכות הפעלה - תרגול 11
מודול ראשון #include <linux/module.h> #include <linux/kernel.h> /* for using printk */ MODULE_LICENSE(“GPL”); int init_module(void) { printk(“Hello, World\n”); return 0; } void cleanup_module(void) { printk(“Goodbye cruel world\n”); } מערכות הפעלה - תרגול 11
הפעלת המודול • קובץ makefile לדוגמה: KERNELDIR = /usr/src/linux-2.4.18-14custom include $(KERNELDIR)/.config CFLAGS = -D__KERNEL__ -DMODULE –I$(KERNELDIR)/include –O -Wall all: hello.o • הרצה: > make > insmod ./hello.o Hello, world > rmmod hello GoodBye cruel world > מערכות הפעלה - תרגול 11
העברת פרמטרים למודול • בעת טעינת המודול, ניתן להעביר אליו פרמטרים הנוגעים לקונפיגורציה של המודול • סוגי פרמטרים נתמכים: • b – byte, h – short, i – integer, l – long, s – string • ניתן להעביר גם מערך של פרמטרים • בקוד המודול מגדירים את המשתנים שיקבלו את הפרמטרים באמצעות המאקרו MODULE_PARM • המאקרו צריך להופיע מחוץ לפונקציה. בד"כ ממוקם בתחילת המודול • פרמטר ראשון – משתנה הפרמטר, פרמטר שני – סוג הפרמטר • יש להגדיר לכל פרמטר ערך ברירת מחדל • ניתן להשתמש במאקרו MODULE_PARM_DESC כדי להוסיף תיאור לפרמטר • כלי ניהול אוטומטיים יכולים לקרוא את התיאור (אפשר גם עם modinfo) מערכות הפעלה - תרגול 11
העברת פרמטרים למודול (2) • הגדרת הפרמטרים במודול (params.c, למשל): int iValue=0; char *strValue; int iArray[4]; MODULE_PARM(iValue,”i”); MODULE_PARM(strValue,”s”); MODULE_PARM(iArray,”4i”); • העברת פרמטרים בטעינת המודול: > insmod ./params.o iValue=3 sValue=“hello” iArray=1,2,3,4 מערכות הפעלה - תרגול 11
גישה לנתוני גרעין • למודול יש גישה למבני הנתונים של הגרעין • ישנם חלקים בגרעין הזמינים רק אחרי הוספת #define __KERNEL__ • מופיע ב-makefile • צריך להוסיף #include לקבצים המתאימים … #include <linux/sched.h> int init_module(void) { printk(“The process is \”%s\” (pid %i)\n”, current->comm, current->pid); return 0; } … מערכות הפעלה - תרגול 11
קריאה לפונקציה פעולת השמה מצביע לפונקציה מצביע לנתונים התקני תווים - איך זה עובד? המודול גרעין Linux insmod register_chrdev() init_module() chrdevs[] fops אוסף פונקציות פונקציות אחרות בגרעין rmmod cleanup_module() unregister_chrdev() מערכות הפעלה - תרגול 11
התקני תווים • להתקני תווים ניגשים דרך שמות המופיעים במערכת הקבצים • בדרך כלל ממוקמים במדריך /dev • כאשר מבצעים ls –l, התקני תווים מאופיינים בתו c בעמודה הראשונה • התקן תווים מאופיין ע"י שני מספרים: • מספר ראשי (major number) - מזהה את מנהל ההתקן המקושר להתקן • מספר משני (minor number) – מספר שמשמש את מנהל ההתקן כדי להבחין בין התקנים שונים המחוברים אליו crw-rw-rw- 1 root root 1, 3 Aug 31 2002 null crw------- 1 root root 10, 1 May 12 2003 psaux crw------- 1 root tty 4, 1 May 12 10:33 tty1 crw-rw-rw- 1 root root 1, 5 Aug 31 2002 zero מערכות הפעלה - תרגול 11
הוספת מנהל התקן חדש • כדי ליצור מנהל התקן חדש, יש להקצות לו מספר ראשי חדש • מבוצע ע"י הפונקציה register_chrdev(), המוגדרת ב-<linux/fs.h> • ההקצאה מבוצעת במסגרת אתחול מנהל ההתקן (init_module()) • תחביר: int register_chrdev(unsigned int major, const char *name, struct file_operations *fops); • פרמטרים: • major – המספר הראשי אותו רוצים להקצות למנהל ההתקן • name – שם ההתקן, כפי שיופיע ב-/proc/devices • fops – מערך של מצביעי פונקציות, המממשים את פעולת ההתקן. נרחיב עליו בהמשך • ערך מוחזר: במקרה של הצלחה יוחזר ערך 0 או חיובי, אחרת -1. מערכות הפעלה - תרגול 11
בחירת מספר ראשי • גרסה 2.4 של Linux תומכת ב-256 מספרים ראשיים • המספרים 0 ו-255 שמורים לשימוש עתידי • חלק מהמספרים הראשיים מוקצים באופן סטטי להתקנים נפוצים • הקצאה סטטית של מספרים ראשיים עבור כל התקן יכולה להיות בעייתית • התנגשות בין מספרים ראשיים של התקנים שונים • ניתן לבצע הקצאה דינמית של מספר ראשי ע"י העברת ערך 0 עבור הפרמטר major • מערכת ההפעלה בוחרת מספר פנוי ומחזירה אותו כתוצאה של register_chrdev() • במקרה של הקצאה סטטית ערך החזרה יהיה 0 • ניתן לראות את המספר שהוקצה גם ב-/proc/devices מערכות הפעלה - תרגול 11
הסרת מנהל התקן מהמערכת • כאשר מסירים מנהל התקן, יש לשחרר את המספר הראשי שהוקצה לו, באמצעות הפונקציה unregister_chrdev(), המוגדרת ב-<linux/fs.h> • תחביר: int unregister_chrdev(unsigned int major, const char *name); • פרמטרים: • major – המספר הראשי של מנהל ההתקן אותו רוצים להסיר • name – שם ההתקן, כפי שמופיע ב-/proc/devices • ערך מוחזר: במקרה של הצלחה יוחזר ערך 0 או חיובי, אחרת -1. מערכות הפעלה - תרגול 11
יצירת התקן חדש • הקריאה ל-register_chrdev רק מקשרת בין מנהל התקן לבין מספר ראשי • כדי לעבוד עם ההתקן, יש ליצור קובץ חדש שייצג אותו, באמצעות הפקודה mknod(דורש הרשאות superuser) • תחביר: mknod NAME TYPE MAJOR MINOR • פרמטרים: • NAME – שם הקובץ החדש (שייצג את ההתקן) • TYPE– סוג ההתקן (c – התקן תווים, b – התקן בלוקים) • MAJOR– המספר הראשי של ההתקן (למעשה קובע את מנהל ההתקן) • MINOR– המספר המשני של ההתקן (מספר בין 0 ל-255) • ניתן להסיר התקן באופן דומה למחיקת קובץ (rm) מערכות הפעלה - תרגול 11
זיהוי התקן ע"י מנהל ההתקן • קבצים מיוצגים ע"י מבנה נתונים בשם inode (פרטים נוספים בתרגול על מערכות קבצים). • בפרט, גם להתקן תווים יש inode המייצג אותו. • בכל פעם שמבצעים פעולה על התקן, מנהל ההתקן מקבל כפרמטר את ה-inode המתאים. • השדה i_rdev ב-inode מכיל את המספר הראשי והמשני של ההתקן. • המאקרו MAJOR(inode->i_rdev) מחזיר את המספר הראשי • המאקרו MINOR(inode->i_rdev) מחזיר את המספר המשני מערכות הפעלה - תרגול 11
פעולות על התקן • מערכת ההפעלה מגדירה אוסף פעולות שניתן לבצע על קבצים • בפרט זהו גם אוסף הפעולות שניתן לבצע על התקן תווים • ניתן להפעיל את הפעולות באמצעות קריאות מערכת • מבנה הנתונים file_operations הוא מערך של מצביעי הפונקציות המממשות את אותן הפעולות • מוגדר ב-<linux/fs.h> • משתנה המצביע למבנה הנ"ל מכונה בד"כ fops או f_op • מצביע NULL מייצג פונקציה לא ממומשת, או מימוש ברירת מחדל • גישה מונחית אובייקטים • הקובץ הוא האובייקט • המתודות של האובייקט מוגדרות ע"י אוסף הפונקציות ב-fops מערכות הפעלה - תרגול 11
שדות חשובים ב-file operations int (*open) (struct inode *, struct file *); int (*release) (struct inode *, struct file *); int (*flush) (struct file *); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); loff_t (*llseek) (struct file *, loff_t, int); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); struct module *owner; מערכות הפעלה - תרגול 11
אתחול file operations • לצורך תאימות גדולה יותר בין גרסאות שונות, הוגדר תחביר מיוחד לאתחול: אתחול עם תוויות ("tagged” initialization) • הרחבה יחודית למהדר של GNU struct file_operations my_fops = { .llseek=my_llseek, .read=my_read, .write=my_write, … }; • אתחול שדה owner נעשה ע"י המאקרו הבא ב-init_module: SET_MODULE_OWNER(&fops); מערכות הפעלה - תרגול 11
איפה אנחנו עומדים בינתיים major number minor number קבצי התקן: crw-r--r-- 1 root root 254, 0 May 17 2006 /dev/mycdev0 crw-r--r-- 1 root root 254, 1 May 17 2006 /dev/mycdev1 מנהל התקן: fops mycdev open release read write … owner מערכות הפעלה - תרגול 11
פתיחת התקן • כאשר פותחים התקן (קריאת המערכת open), בין השאר מערכת ההפעלה מבצעת את הפעולות הבאות: • מאתחלת מבנה נתונים מסוג struct file • מבנה נתונים זה מייצג קובץ/התקן פתוח • משתנה המצביע למבנה הנ"ל מכונה בד"כ file או filp • כל הקריאות שהמשתמש מבצע על file descriptor מסויים, יועברו למנהל ההתקן עם אותו struct file • במידה והפונקציה open אותחלה עבור מנהל ההתקן, מערכת ההפעלה קוראת לה ומעבירה את מבנה הנתונים כפרמטר מערכות הפעלה - תרגול 11
שדות חשובים ב-struct file mode_t f_mode; /* (FMODE_READ, FMODE_WRITE) */ loff_t f_pos; unsigned int f_flags; /* (O_RDONLY, O_NONBLOCK,…, see <linux/fcntl.h>) */ struct file_operations *f_op; void *private_data; מערכות הפעלה - תרגול 11
פתיחת התקן (2) • הפונקציה open של מנהל ההתקן אחראית ל: • הגדלת מונה השימוש של מנהל ההתקן (מתבצע אוטומטית בגרסאות 2.4 ומעלה) • לבדוק שההתקן תקין (אין בעיות חומרה) • לאתחל את ההתקן, אם זו הפעם הראשונה שפותחים אותו • לזהות את המספר המשני, לעדכן את f_op אם צריך • לאתחל מבני נתונים ב-filp->private_data מערכות הפעלה - תרגול 11
סגירת התקן • הפונקציה release של מנהל ההתקן אחראית ל: • הקטנת מונה השימוש של מנהל ההתקן (מתבצע אוטומטית בגרסאות 2.4 ומעלה) • שחרור מבני נתונים ב-filp->private_data, אם צריך • פעולת סגירה מתבצעת גם אם האפליקציה לא סגרה קבצים פתוחים באופן מפורש • כאשר תהליך מבצע exit מערכת ההפעלה תפעיל את קריאת המערכת close עבור כל קובץ פתוח. מערכות הפעלה - תרגול 11
הפונקציות read ו-write • תחביר: ssize_t read(struct file *filp, char *buff, size_t count, loff_t *offp); ssize_t write(struct file *filp, const char *buff, size_t count, loff_t *offp); • פרמטרים: • filp – מבנה הנתונים המייצג את הקובץ (ההתקן) הפתוח • buff – חוצץ באזור הזכרון של המשתמש (מקור לקריאה/יעד לכתיבה) • count – מספר הבתים שיש לקרוא/לכתוב • offp – מצביע המייצג את המיקום שאליו המשתמש ניגש בקובץ • ערך מוחזר: במקרה של הצלחה יוחזר מספר הבתים שהועתקו, אחרת ערך שלילי. הפרמטר offp מעודכן כדי לבטא את המיקום החדש בקובץ מערכות הפעלה - תרגול 11
הפונקציות read ו-write (2) • ברוב המקרים נדרש להעתיק מידע בין אזור הזכרון של המשתמש ואזור הזכרון של הגרעין. לרוב מתבצע באמצעות: unsigned long copy_to_user(void *to, const void *from, unsigned long count); unsigned long copy_from_user(void *to, const void *from, unsigned long count); • מוגדרות ב-<asm/uaccess.h> • הפונקציות מוודאות כי הגישה מתבצעת לאזורים חוקיים בזכרון • במידה ולא, לא מתבצעת כל העתקה • בכל מקרה, מוחזר מספר הבתים שנותרו להעתקה (0 אם ההעתקה הצליחה) • ניתן להקצות ולשחרר זכרון בגרעין באמצעות kmalloc ו-kfree • זהות ל-malloc ו-free, למעט פרמטר נוסף של kmalloc (GFP_USER, GFP_KERNEL) מערכות הפעלה - תרגול 11
הפונקציה ioctl • מטרתה לאפשר פקודות בקרה ייחודיות להתקן • יש להשתמש באפשרות זאת רק אם לא ניתן לספק מענה הולם במסגרת הפונקציות הקיימות • תחביר: int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); • פרמטרים: • inode – אובייקט ה-inode המייצג את הקובץ • filp – מבנה הנתונים המייצג את הקובץ הפתוח • cmd – מספר סידורי של פקודה (על מנת לתמוך במספר פקודות בקרה) • בדרך כלל משתמשים בקובץ header כדי להגדיר שמות לפקודות • arg – פרמטר כללי אופציונלי (אם המשתמש לא מעביר אותו, יהיה ערך זבל) • ערך מוחזר: תלוי בכותב מנהל ההתקן (שגיאה – ערך שלילי) • בדרך כלל לפונקציה יהיה מבנה switch, בהתאם לפרמטר cmd מערכות הפעלה - תרגול 11
הפונקציה ioctl (2) • בצד המשתמש, לקריאת המערכת ioctl יש מבנה ייחודי • תחביר: int ioctl(int fd,int cmd,…); • פרמטרים: • fd – מתאר קובץ (שהתקבל כתוצאה של open) • cmd – מספר סידורי של פקודה (יועבר כמו שהוא לפונקציה המממשת) • … – נועד למנוע בדיקה של המהדר על הפרמטר האופציונלי • בדרך כלל מתייחסים לפרמטר האופציונלי כ-char *argp • ערך מוחזר: תלוי בכותב מנהל ההתקן (שגיאה – ערך -1) מערכות הפעלה - תרגול 11
הפונקציה ioctl (3) • עדיף שהתקנים שונים יגדירו קבועים שונים עבור הפרמטר cmd • התקנים שונים עושים שימוש באותם מספרים - פתח לתקלות • מוסכמה לקביעת המספרים על סמך ארבעה פרמטרים • type – מספר "קסם", רצוי ייחודי להתקן (8 ביטים) • הנחיות לבחירת המספר: Documentation/ioctl-number.txt • number – מספר סידורי (8 ביטים) • direction – כיוון העברת המידע (מנקודת המבט של המשתמש) _IOC_NONE , _IOC_READ , _IOC_WRITE, _IOC_READ | _IOC_WRITE • size – גודל מידע המשתמש המעורב בהעברה (8-14 ביטים) • הקובץ <asm/ioctl.h> מגדיר פונקציות מאקרו לקביעת המספרים • ישנה פקודת מאקרו לכל כיוון (direction) של העברה: _IO(type,nr) , _IOR(type,nr,dataitem) _IOW(type,nr,dataitem) , _IOWR(type,nr,dataitem) מערכות הפעלה - תרגול 11
הפונקציה ioctl (4) • דוגמה: #define MYDEV_IOC_MAGIC ‘o’ #DEFINE MYDEV_IORESET _IO(MYDEV_IOC_MAGIC,0) #DEFINE MYDEV_SETMODE _IOW(MYDEV_IOC_MAGIC,1,char) #DEFINE MYDEV_GETMODE _IOR(MYDEV_IOC_MAGIC,2,char) מערכות הפעלה - תרגול 11