שבוע #13 פרקFile System Implementation : מימוש מערכת הקבצים קורס מערכות הפעלה ב' מכללת הדסה /מכללה חרדית צבי מלמד Tzvi.Melamed@gmail.com הרצאות הקורס מבוססות במידה רבה ביותר על ההרצאות של ד"ר יורם ביברמן © כל הזכויות שמורות לד"ר יורם ביברמן ולצבי מלמד ©צבי מלמד 1 שימוש בקבצים ביוניקס ©צבי מלמד 113 חזרה – הבהרה מערכת הקבצים הלוגית -מבט על ©צבי מלמד 114 חזרה – הבהרה mount ©צבי מלמד 115 חזרה – הבהרה מבנה מערכת הקבצים ביוניקס ©צבי מלמד 116 שימוש בקבצים ביוניקס • הפילוסופיה של יוניקס :לבנות מערכת הפעלה קומפקטית. • בהתאמה :יוניקס מספקת רק פעולות בסיסיות על קבצים: – פתיחה ,קריאה ,כתיבה ,תנועה ) (seekוסגירה • כל פעולות הקו"פ מחייבות קריאות מערכת ולכן הן מכונות ) unbuffered i/oקו"פ נטול חציצה( – השם מטעה ,כי מערכת ההפעלה מחזיקה חוצצים עבור פעולות הקלט/פלט. • מבחינת ה kernel-כל פעולה על קובץ מתבצעת באמצעות הfile-- = descriptorמספר הכניסה שהוקצתה לקובץ במערך הקבצים הפתוחים של התהליך. ©צבי מלמד 117 מתארי הקבצים הסטנדרטיים • מתאר קובץ :#0קלט סטנדרטי • מתאר קובץ :#1פלט סטנדרטי • מתאר קובץ :#2קובץ השגיאה הסטנדרטי • בקובץ > <unistd.hמוגדרים הקבועים: – STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO ©צבי מלמד 118 פעולות על קבצים – פתיחת קובץ SYNOPSIS #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); int creat(const char *pathname, mode_t mode); DESCRIPTION The open() system call is used to convert a pathname into a file descriptor (a small, non-negative integer for in When the subsequent I/O as with read, write, etc.). call is successful, the file descriptor returned will use be the lowest file descriptor not currently open for the process. 119 ©צבי מלמד פעולות על קבצים – פתיחת קובץ ;)int open(const char *pathname, int flags ;)int open(const char *pathname, int flags, mode_t mode • הפרמטר הראשון הוא מחרוזת שכוללת את ה path-לקובץ • הפרמטר השני מתאר כיצד יש לפתוח את הקובץ • הפרמטר השלישי מתאר את ההרשאות לקובץ במקרה שמשתמשים בפקודה ליצור קובץ חדש. – מתי יוצרים קובץ חדש? את זה מתארים ה"דגלים" של הפרמטר השני )בשקף הבא( • ערך מוחזר :מתאר הקובץ הפנוי הראשון במערך מתארי הקבצים של התהליך ©צבי מלמד 120 פעולות על קבצים – פתיחת קובץ ;)int open(const char *pathname, int flags ;)int open(const char *pathname, int flags, mode_t mode • הפרמטר השני – דגלי הסטטוס של הקובץ :האפשרויות לפתיחת הקובץ – O_RDONLY, O_WRONLY, O_RDWR – ניתן לבצע bit-wise ORלדגלים נוספים – – O_APPENDהוסף בסוף – – O_CREATצור את הקובץ אם אינו קיים – – O_EXCLהכשל אם התבקשת ליצור והקובץ כבר קיים – – O_TRUNCאם הקובץ קיים ונפתח לכתיבה – רוקן אותו – – O_SYNCפעולת כתיבה תחזור רק אחרי שהנתונים נכתבו לדיסק )להבדיל :החזרה מהקריאה לאחר שהנתונים נכתבו לחוצץ( ©צבי מלמד 121 סגירת קובץ/ פעולות על קבצים – יצירת :• יצירת קובץ int open(const char *pathname, int flags, mode_t mode); int creat(const char *pathname, mode_t mode); creat()- עם הדגלים המתאימים או בopen()-– ניתן להשתמש ב int fd = open(“~/workspace/os/ex1.txt”, W_ONLY | O_CREAT | O_TRUNC, 0600); :• סגירת קובץ int close(int fd); .close() – קריאה לפונקציה בכשלון-1 ,– מחזירה אפס בהצלחה 122 ©צבי מלמד (file pointer) שינוי מקומו של מכוון הקובץ NAME lseek - reposition read/write file offset SYNOPSIS #include <sys/types.h> #include <unistd.h> off_t lseek(int fildes, off_t offset, int whence); DESCRIPTION The lseek function repositions the offset of the file descriptor fildes to the argument offset according to the directive whence as follows: ....... 123 ©צבי מלמד שינוי מקומו של מכוון הקובץ )(file pointer • • • • • נשתמש בפקודת )( [ l = long ] lseekבכדי לשנות את מיקומו של המצביע לקובץ הפעולה קובעת את ההסטה offsetשל המכוון מתחילת הקובץ )אפס – הבית הראשון בקובץ( כל פעולת קריאה או כתיבה משנה את המצביע בהתאם למספר הבתים שנקראו/נכתבו בפועל מחזירה את מקומו החדש של מכוון הקובץ או -1במקרה של כישלון הארגומנט השלישי – =) whenceמאיזה מקום( – – SEEK_SETקבע את המצביע להיות offsetבתים מתחילת הקובץ – – SEEK_CURהתקדם offsetבתים יחסית למיקום הנוכחי ) offsetעשוי להיות שלילי( – – SEEK_ENDהתקדם offsetבתים יחסית לסוף הקובץ ©צבי מלמד 124 שינוי מקומו של מכוון הקובץ )(file pointer • בכדי לדעת את מיקומו הנוכחי של המכוון נקרא ל lseek() -עם offset=0 • הפונקציה )( lseekאינה מבצעת פעולת קו"פ • בקובץ שפתוח לכתיבה – ניתן להזיז את המכוון מעבר לסוף הקובץ • התוצאה: – הכתיבה הבאה תהיה במקום שאליו מצביע המכוון – יצרנו "חור" בקובץ – חור שמכיל אפסים • דוגמאfile_creat_hole.c: ©צבי מלמד 125 file_creat_hole.c:דוגמא Now, offset is: 0 126 ©צבי מלמד file_creat_hole.c:דוגמא now, offset is: 3 now, offset is: 15 now, offset is: 18 127 ©צבי מלמד הרצה file_creat_hole.c: גודל הקובץ הוא 18 הפקודה (octal dump) odמציגה את תוכן הקובץ. האופציה –cהצג ב-ערכי תוים ascii ©צבי מלמד הכתובת בקובץ מוצגת בבסיס hexa ) 00020שקול ל 16-עשרוני( 128 read() – קריאה מקובץ read - read from a file descriptor SYNOPSIS ssize_t read(int fd, void *buf, size_t count); DESCRIPTION read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf. • הפונקציה מחזירה או,– את מספר הבתים שנקראו בפועל eof – אפס אם במקרה של כישלון-1 – 129 ©צבי מלמד קריאה מקובץ – )(read ;)ssize_t read(int fd, void *buf, size_t count • מספר הבתים שקראנו )ערך ההחזרה( קטן מ count -כאשר: א -נותרו פחות מ count-בתים עד סוף הקובץ )ה read-הבא יחזיר אפס( ב -כשקוראים מטרמינל – נקראת לכל היותר שורה אחת ג -בקריאה מהרשת – בגלל חציצה ואופני העברת נתונים ייתכן שייקראו פחות בתים מכפי שביקשנו ©צבי מלמד 130 דוגמאfile_read.c : ©צבי מלמד 131 דוגמאfile_read.c : ©צבי מלמד 132 דוגמא: file_read.c ©צבי מלמד 133 הרצהfile_read.c : ©צבי מלמד 134 כתיבה על קובץ size_t write(int fd, const void *buf, ;)size_t count • הפונקציה מחזירה: – מספר הבתים שנכתבו או -1בכישלון • סיבות אפשריות לכישלון: – דיסק מלא ,גודל הקובץ עבר את המקסימום שהוגדר לו • השפעה על מהירות הביצוע: – תכנית שמשתמשת בפעולות read/writeתרוץ במהירות גבוהה ביותר אם החוצץ יהיה לפחות בגודל בלוק עבור אותו ציוד – הגדלת החוצץ מעבר לגודל בלוק לא משפרת את הביצועים – פקודת ) statלדוגמא(stat /dev/tty : ©צבי מלמד 135 כתיבה על קובץ • במקום לכתוב/לקרוא על קבצים באמצעות פעולות read/write ניתן למפות את הקובץ לזכרון )( mmapולפנות אליו כמו אל מערך )באמצעות מצביע( – המערך בזיכרון הראשי – מכיל את נתוני הקובץ שמקורו בדיסק – קריאת כתובת של הקובץ שעדיין לא נטענה לזיכרון page + faultטעינת הדפים לתוכנית )שקוף לתכנית( – כתיבה למערך – מערכת ההפעלה תעדכן את הקובץ בדיסק – באופן שקוף ,בתזמון על פי שיקולי המערכת. ©צבי מלמד 136 שיתוף קבצים – הכנה …teaser דילמה :1# • להלן תסריט בעייתי: תהליך א' פתח קובץ שגודלו 1000בתים לכתיבה )לא במוד (appendומבצע: )if (lseek(fd, 0, SEEK_END) <0 ........ if (write(fd, buff, 100)!=100 ........ • תהליך ב' עושה במקביל את אותן הפעולות על אותו הקובץ ©צבי מלמד 137 teaser… שיתוף קבצים – הכנה :fork() אחרי/ פתיחת קבצים לפני:2# דילמה :'• מקרה א if (fd=open(“xyz”) <0)... fork() ... write(fd, “abcde”); :'• מקרה ב fork() if (fd=open(“xyz”) <0)... write(fd, “abcde); 138 ©צבי מלמד שיתוף קבצים • שלושה מבני נתונים שה kernel-מחזיק עבור ניהול הקו"פ: א -במערך התהליכים ) PCBאו – (process descriptorכניסה לכל תהליך שמורץ במערכת. – ה PCBמכיל את טבלת מתארי הקבצים הפתוחים של התהליך – .file descriptor table – לכל מתאר: • דגלי הסטטוס – האם המתאר נשאר פתוח לאחר ביצוע )(exec • מצביע לטבלת קבצים פתוחים גלובלית של המערכת ©צבי מלמד 139 שיתוף קבצים ב -ה kernel-מחזיק מערך של קבצים פתוחים. – קובץ עשוי להיות מיוצג יותר מפעם אחת אם הוא נפתח מספר פעמים – כל כניסה מכילה: • דגלי סטטוס של הקובץ (read, write, sync, append, )…trunc • ה offset -הנוכחי של הקובץ • מצביע לכניסה המתאימה בטבלת ה VNODE-של המערכת ©צבי מלמד 140 שיתוף קבצים ג -לכל קובץ פתוח או רכיב ציוד ,קיים מבנה בשם .vnodeמכיל: – מידע אודות סוג הקובץ – מצביעים לפונקציות שפועלות על הקובץ – בדרך כלל ה vnode-מכיל בתוכו את ה(v=virtual, inode- )i=information – ה inode-מכיל את כל ה – meta-data-המידע "אודות הקובץ" – הבעלים ,הרשאות ,גודל ,זמנים ,מצביעים לבלוקים בדיסק(... – ה inode-נשמר על הדיסק בטבלת ה .inodes-נטען כאשר הקובץ נפתח בפעם הראשונה ©צבי מלמד 141 שיתוף קבצים – מבני הנתונים העיקריים ©צבי מלמד 142 ©צבי מלמד 143 שיתוף קבצים – vnode structure ©צבי מלמד 144 file-descriptor & open-file tables 145 ©צבי מלמד פעולות אטומיות להלן תסריט בעייתי: • תהליך א' פתח קובץ שגודלו 1000בתים לכתיבה )לא במוד (appendומבצע את הפעולות: )if (lseek(fd, 0, SEEK_END) <0 ........ if (write(fd, buff, 100)!=100 ........ תהליך ב' עושה במקביל את אותן הפעולות על אותו הקובץ ©צבי מלמד 146 הבהרה – שני תהליכים שפותחים אותו קובץ • תזכורת :להלן תיאור של מבנה הנתונים שה kernel-מחזיק לטיפול בקבצים • מה קורה כשתהליך מבצע ?fork • מה קורה כששני תהליכים נפרדים פותחים את אותו הקובץ? ©צבי מלמד 147 הבהרה – שני תהליכים שפותחים אותו קובץ • מה קורה כאשר שני תהליכים פותחים – באופן עצמאי – את אותו הקובץ? • לכל פתיחה של קובץ נוצרת כניסה נפרדת בטבלת הקבצים הפתוחה – מאפשר ,לדוגמא ,מכוון קובץ ) (offsetנפרד לכל אחד מהתהליכים • אבל ..קיים רק vnodeאחד )וכמובן גם רק inodeאחד ,עבור הקובץ הזה(. • המחשה בשקף הבא. ©צבי מלמד 148 הבהרה – שני תהליכים שפותחים אותו קובץ ©צבי מלמד 149 הפונקציות )(dup() dup2 • פגשנו אותן בסמסטר א' ;)int dup(int fildes ;)int dup2(int fildes, int fildes2 • • • • • )( dupמשכפלת את מתאר הקובץ ,ומחזירה את מתאר הקובץ החדש בפונקציה )( dup2אנו קובעים את הערך הרצוי ל .filedes2-אם מקום זה "תפוש" על ידי מתאר של קובץ פתוח – אזי ראשית נסגר הקובץ הקיים נסגר הפעולות שמבצעות פקודות אלו הן אטומיות! דרך אלטרנטיבית היא להשתמש בפקודה fcntlכפי שמיד נראה. השקף הבא – המחשה של פעולת )(dup ©צבי מלמד 150 kernel structure after dup() 151 ©צבי מלמד שיתוף קבצים כיצד משולבות הפעולות שמבצעים התהליכים השונים על הקובץ? א -כל כתיבה מזיזה את המצביע לקובץ בטבלת הקבצים הפתוחים. – אם הקובץ גדל כתוצאה מכך – אזי ה inode-מתעדכן ב -אם הקובץ נפתח במוד של O_APPENDאזי הדבר מצוין בסטטוס בטבלת הקבצים הפתוחים – בכל פעולת כתיבה מתבצע באופן אטומי (1) :מצביע הכתיבה מקבל את גודל הקובץ מה (2) inode-מתבצעת כתיבה ) (3ערכו מתעדכן ג lseek -מעדכן רק את טבלת הקבצים הפתוחים )אך לא את ה(inode- ד fork() -ו dup() -גורמות לכך ששתי כניסות בטבלת המתארים תתבצענה על כניסה אחת בטבלת הקבצים הפתוחים ©צבי מלמד 152 פעולות אטומיות תסריט אפשרי: • שני תהליכים כותבים לאותו קובץ ,והכוונה שכל כתיבה תתבצע בסוף הקובץ ).(append .1בטבלת הקבצים הפתוחים ,ברשומה של תהליך א' מסומן כי מכוון הקובץ מצביע לכתובת 1000 .2בטבלת הקבצים הפתוחים ,ברשומה של תהליך ב' מסומן כי מכוון הקובץ מצביע לכתובת 1000 .3תהליך א' כותב את מאה הבתים שלו והקובץ גדל ל1100- בתים. .4תהליך ב' כותב את הבתים שלו על גבי אילו של תהליך א' • מכיוון שזה לא מה שרצינו קיבלנו תוצאה שגויה ©צבי מלמד 153 פעולות אטומיות הסיבה לבעיה: • שתי הפעולות הללו: קידום המכוון לסוף הקובץ if (lseek(fd, 0, SEEK_END) <0); // כתיבה לקובץ if (write(fd, buff, 100)!=100; // • אינן אטומיות .ולכן ,לא מובטח לנו שלכשיתבצע ה write-הוא אכן יכתוב לסוף הקובץ ©צבי מלמד 154 :דוגמא file_share_bad.c 155 ©צבי מלמד file_share_bad.c :דוגמא 156 ©צבי מלמד :הרצה file_share_ bad.c 157 ©צבי מלמד פעולות אטומיות הפתרון: • כל תהליך יפתח את הקובץ עם O_APPENDואז ה kernel-ידאג לכך שלפני כל פעולת כתיבה ,מכוון-הקובץ ) (offsetיוזז לסוף הקובץ. ובנוסף ...שתי הפקודות תבוצענה באופן אטומי • באופן דומה פעולת יצירת הקובץ: )open(“...”, O_WRONLY | O_CREAT, 0600 מבצעת שתי פעולות: – בדיקה האם הקובץ קיים – אם אינו קיים – אז יצירתו • לכאורה – בין שתי הפעולות עלול היה תהליך אחר ליצור את הקובץ, לכתוב עליו ,ואז התהליך שלנו היה דורס את התוכן שזה עתה נכתב. • למעשה – מערכת ההפעלה מבטיחה לנו אטומיות של הבדיקה +יצירת הקובץ ©צבי מלמד 158 file_share_good.c :דוגמא 159 ©צבי מלמד file_share_good.c :הרצה 160 ©צבי מלמד הפונקציה )(fcntl • הפונקציה )( fcntlמשמשת לצורך קביעת ושליפת התכונות של קובץ פתוח: ©צבי מלמד 161
© Copyright 2024