600 likes | 922 Views
מערכות הפעלה. תרגול 5 – מימוש זמן תהליכים. מה בתכנית?. כמה מילים על ניהול זיכרון ב- XINU ארכיטקטורת 86 x - תזכורת מימוש זמן תהליכים ב- XINU איך לשנות XINU. ניהול זיכרון. הקצאה ושחרור זכרון נעשים באמצעות getmem/freemem בהתאמה (בדומה ל- malloc/free בשפת ) .
E N D
מערכות הפעלה תרגול 5 – מימוש זמן תהליכים
מה בתכנית? • כמה מילים על ניהול זיכרון ב-XINU • ארכיטקטורת 86x - תזכורת • מימוש זמן תהליכים ב-XINU • איך לשנות XINU מערכות הפעלה - תרגול 2
ניהול זיכרון הקצאה ושחרור זכרון נעשים באמצעות getmem/freemem בהתאמה (בדומה ל-malloc/free בשפת ). <מצביע לבלוק> = getmem( <גודל בבתים> ); getmemמחזירה מצביע למקום הזיכרון שהוקצה. אם אין מקום מתאים יוחזר NULL. מערכות הפעלה - תרגול 2
ניהול זיכרון freemem(<מצביע לבלוק>, <גודל הבלוק> ); • freememמשחררת את מספר הבתים שצויין מהמקום בזכרון שסופק כפרמטר. • המצביע חייב להיות זה שהתקבל בקריאה קודמת כלשהי ל-getmem. מערכות הפעלה - תרגול 2
מרחב הכתובות בזכרון • המעבד מסוגל לגשת למרחב הכתבות של 1MB • כל כתובת ניתנת לייצוג על ידי 20 ביטים, אבל מילת המכונה היא בגודל 16 ביט • נעזרים ב-2 מילים: <Segment>:<Offset> • הנוסחה לכתובת אבסולוטית: <Address> = <Segment> * 16 + <Offset> • דוגמה: 1300:2A03 = 1300 * 0x10 + 2A03 = 13000 + 2A03 = 15A03 מערכות הפעלה - תרגול 2
מרחב הכתובות בזכרון • דוגמה: 1300:2A03 = 1300 * 0x10 + 2A03 = 13000 + 2A03 = 15A03 • נחשב גם 15A0:0003 15A0:0003 = 15A03 מכאן שלאותה כתובת אבסולוטית יכולים להיות מספר ייצוגים במבנה הזה מערכות הפעלה - תרגול 2
רגיסטרים מערכות הפעלה - תרגול 2
הבהרות • AX, BX, CX, DX הם רגיסטרים בגודל מילה אחת, אך ניתן להשתמש בכל אחד מהם כשני רגיסטרים בגודל בית אחד (AH, AL) • SS מחזיק את סגמנט המחסנית כדי לגשת לבסיס המחסנית נשתמש ב-SS:0 כדי לגשת לראש המחסנית נשתמש ב-SS:SP • IP מצביע לפקודה הבאה לביצוע בסגמנט הקוד. לכן, CS:IP מכיל את הכתובת האבסולוטית של הפקודה הבאה לביצוע • BP מצביע על הפרמטרים שנשלחו על המחסנית לפונקציה המתבצעת ("הפונקציה הנקראת"). על ידי שימוש במרחק יחסי מה-BP ניתן להגיע לפרמטרים שונים, לכתובת החזרה מהפונקציה ולמשתנים הלוקליים לדוגמה, הפרמטר הראשון נמצא ב-[BP+4]. כדי להגיע לכתובת אבסולוטית יש להשתמש בסגמנט השמור ב-SS. מערכות הפעלה - תרגול 2
רגיסטר FLAGS • כולל את ביט אישור הפסיקות וכן ביטי Condition code • הביטים שבשימוש על ידי XINU הם: • ZF: (Zero Flag) מציין האם הערך האחרון שחושב הינו 0 • CF (Carry Flag) : מציין האם בעת חישוב הערך האחרון נוצר Сarry אל מעבר לביט העליון של המילה (כאשר יש גלישה) • IF (Interrupt Flag) : • כשכבוי: פסיקות (חומרה בלבד) לא מאופשרת • כשדלוק: פסיקות מאופשרות 9 6 0 IF ZF CF מערכות הפעלה - תרגול 2
רגיסטר FLAGS • IF (Interrupt Flag) : • כשכבוי: פסיקות (חומרה בלבד) לא מאופשרת • כשדלוק: פסיקות מאופשרות • פסיקות חמרה (דיסק, מקלדת, מסך, שעון וכו') קורות כתוצאהמשינוי מצבשל חמרה (תו הוקש במקלדת, תזוזת ראש הדיסקהסתיימה וכדומה). • דגל IF מורה האם לשרת פסיקות מסוג זה. ניתן לכבות או להדליק אתהדגל דרך פקודות המכונה: cli, sti מערכות הפעלה - תרגול 2
וקטור הפסיקות • טיפול בפסיקה מתבצע באמצעות הרצת שגרה מיוחדת האחראית לטפל באותה פסיקה. לשגרה כזאת קוראים ISR – Interrupt Service Routine • למבנה נתונים המקשר בין כל מספר פסיקה לשגרת הטיפול בה קוראים וקטור פסיקות • כתובת שגרת ISR: 4 בתים (2 סגמנט, 2 אופסט) • K1 בזכרון לשמירת כל הכתובות (=1024 בתים) • הפסיקות המזוהות ע"י מס' FF..0. כשמגיעה פסיקה מס' v , מריצים את שגרת הטיפול הנמצאת בזכרון בכתובת 4v. מערכות הפעלה - תרגול 2
חריגות • חריגות (למשל חלוק באפס, גישה לזכרון לא קיים, נפילת מתח) מטופלות כפסיקות. • דוגמאות: • כניסה 0 בוקטור הפסיקות שמורה לחריגה – חילוק באפס • כניסה 4 שמורה עבור גלישה (overflow) מערכות הפעלה - תרגול 2
פסיקות תכנה • ניתן "לייצר" פסיקות באמצעות פקודת האסמבלר int. פסיקות אלו נקראות פסיקות תכנה • תחביר: int <מספר פסיקה>; • דוגמה: פסיקת breakpoint int 3; מערכות הפעלה - תרגול 2
סוגי פסיקות ב-XINU מערכות הפעלה - תרגול 2
סביבת ריצה • התוכנה ב-XINU נחלקת לשני סגמנטים: • סגמנט קוד המוצבע ע"י CS • סגמנט נתונים ומחסנית המוצבע ע"י DS, SS • סגמנט הקוד (CS) כולל את הקוד של כל האפליקציה, וכל התהליכים • סגמנט הנתונים (DS) כולל את כל הנתונים הסטטיים בזכרון, את המחסניות של כל התהליכים ואת שטח הזכרון המיועד להקצאות זכרון דינמיות מערכות הפעלה - תרגול 2
סביבת ריצה • נשים לב – ב-XINU כל התהליכים משתפים את: • אותו קוד • אותם נתונים סטטיים (משתנים גלובליים) • אותו אזור להקצאה דינמית (כך ששימוש לא נכון יכול לדרוס זכרון שהוקצה ע"י תהליכים אחרים) • אולם, לכל תהליך יש מחסנית נפרדת השייכת לו בלבד מערכות הפעלה - תרגול 2
קריאה לפונקציה קריאה לפונקציה מתבצעת בכמה שלבים: • באחריות הפונקציה הקוראת לשמור את ערכי הרגיסטרים AX, BX, CX, DX במידה שידרשו לאחר החזרה • הפונקציה הקוראת דוחפת את הפרמטרים על המחסנית (בסדר הפוך) • ביצוע פקודת המכונה לקריאה לפונקציה (שומרת כתובת החזרה על המחסנית) מערכות הפעלה - תרגול 2
קריאה לפונקציה בהמשך: • באחריות הפונקציה הנקראת לשמור את ערכי הרגיסטרים BP, SI, DI (ואת Flags אם מבצעים STI, CLI) ולהקצות על המחסנית מקום למשתניה הלוקליים • ערך הפונקציה מוחזר ברגיסטר AX • אחרי החזרה, הפונקציה הקוראת אחראית להוציא את הפרמטרים מהמחסנית מערכות הפעלה - תרגול 2
קריאה לפונקציה – מצב המחסנית . . . sp לפני הקריאה פרמטר 3 פרמטר 2 פרמטר 1 באחריות הפונקציה הקוראת לפני ביצוע call כתובת חזרה לאחר ביצוע call (תחילת ביצוע הנקראת) bp BP משתנים מקומיים באחריות הפונקציה הנקראת SI DI sp בזמן ביצוע הפונקציה הנקראת . . . מערכות הפעלה - תרגול 2
קריאה לפונקציה – הערות • אין צורך לשמור רגיסטרים בהם הפונקציה הקוראת לא משתמשת • סדר דחיפת הפרמטרים הוא הפוך: הפרמטר הראשון הוא הקרוב ביותר לכתובת החזרה (4 + BP) • כתובת חזרה נלקחת מ-IP:CS. בעת הקריאה IP כבר מצביע על הפקודה שאחרי הקריאה לפונקציה, ולכן זוהי כתובת החזרה ממנה מערכות הפעלה - תרגול 2
קריאה לפונקציה – הערות • BP נשמר על המחסנית לפני עדכונו. בכך מבטיחים שכל ערכי BP מהווים מעין רשימה מקושרת כך שכשיוצאים מפונקציה ניתן לשחזר את הערך הקודם • משתנים לוקליים "מוקצים" מתוך המחסנית (פשוט על ידי קידום SP). בכך מבטיחים שכאשר יוצאים מהפונקציה כל המשתנים הלוקליים משתחררים באופן אוטומטי • בדרך כלל AX מספיק כדי להחזיר את ערך החזרה מהפונקציה. אם יש צורך להחזיר מצביע מפורש (Seg:Off) יוחזר הערך ב-AX:DX מערכות הפעלה - תרגול 2
קריאה לפונקציה – דוגמה פונקציה my_add מחברת שני מספרים ומחזירה את סכומם: sum = my_add(4, 6); באסמבלר: _my_add proc near push bp mov bp, sp push si push di mov bx, [bp+4] mov cx, [bp+6] mov ax, bx mov bx, cx add ax, bx pop di pop si pop bp ret _my_add endp מערכות הפעלה - תרגול 2
מנגנון החלפת התהליכים • העובדה שהמעבד משרת תהליך מסויים נובעת בעיקר מכך שאוגרי המעבד מכילים את הערכים "שלו" • ראינו שזה קובע את הפקודות הבאות לביצוע, המחסנית הנוכחית, ערכי האוגרים – ההקשר • החלפת התהליכים היא פעולת החלפת הקשר (context switch)– שמירת ההקשר של תהליך יוצא וטעינת ההקשר של תהליך נכנס מערכות הפעלה - תרגול 2
מנגנון החלפת התהליכים בנוסף להחלפת ההקשר להחלפת התהליכים שתי מחויבויות נוספות: • מימוש מדיניות החלפת התהליכים • עדכון מבנה נתונים גלובלי של XINU מערכות הפעלה - תרגול 2
החלפת הקשר • בזמן החלפת הקשר של התהליך הנוכחי יש לשמור את ההקשר שלו ולטעון למעבד את ההקשר של התהליך המחליף • ההקשר כולל: • ערכי אוגרים • ערך Flags • הכתובת הבאה לביצוע (CP:IP) • המחסנית של התהליך • ההקשר נשמר על גבי המחסנית של התהליך • מצביע המחסנית נשמר בטבלת התהליכים מערכות הפעלה - תרגול 2
החלפת הקשר • בהחלפת ההקשר יש גם לעדכן משתנים נוספים המציינים את מצבי התהליכים: • משתנה currpid – המציין את התהליך הנוכחי • מצבי התהליכים בטבלת התהליכים • עדכון תור READY (getlast/insert) • ב-XINU הזימון והחלפת ההקשר נעשות ע"י פונקציות resched ו-ctxsw(במידה ויש החלפה בפועל) מערכות הפעלה - תרגול 2
החלפת הקשר - resched • שגרת resched מרכזת את כל הפעולות הדרושות להחלפת הקשר • קריאה לשגרה היא רק בדיקה האם צריך לבצע החלפת תהליך • יוזמי הקריאה ל-resched: • תהליך עצמו • מערכת הפעלה מערכות הפעלה - תרגול 2
קריאה ל-resched ביוזמת התהליך • ויתור של תהליך על המעבד • המתנה להודעה/סמפור, השעייה, שינה, סוף התהליך • תהליך דואג לשנות את מצבו לפני קריאה ל-resched • כך גםresched מזהה שמדובר על ויתור • במצב של ויתור התהליך תמיד יוצא מהמעבד ונשאר במצב שבחר לעצמו מערכות הפעלה - תרגול 2
קריאה ל-resched ביוזמת מ"ה • הקריאה מתבצעת למרות שהתהליך הנוכחי מעוניין להמשיך לרוץ • זה קורה כי: • הקצאת הזמן של התהליך נגמרה • קיים תהליך עם עדיפות יותר גבוהה שמוכן להכנם למעבד • התהליך מגיע לשגרה במצב PRCURR • כאן לא בהכרח תתבצע החלפת התהליכים (לפי מדיניות ההחלפה) • אם בכל זאת ההחלפה מתבצעת התהליך עובר למצב של PRREADY ונכנס לתור ה-READY מערכות הפעלה - תרגול 2
טבלת תהליכיםproc.h - #defineNPROC 30 /* process state constants */ #definePRCURR '\01' // process is currently running #definePRFREE '\02' // process slot is free #definePRREADY '\03' // process is on ready queue #definePRRECV '\04' // process waiting for message #definePRSLEEP '\05' // process is sleeping #definePRSUSP '\06' // process is suspended #definePRWAIT '\07' // process is on semaphore queue מערכות הפעלה - תרגול 2
טבלת תהליכיםproc.h - /* process table entry */ struct pentry { char pstate; /* process state: PRCURR, etc. */ int pprio; /* process priority */ int psem; /* semaphore if process waiting */ int pmsg; /* message sent to this process */ int phasmsg; /* nonzero iff pmsg is valid */ char *pregs; /* saved environment */ char *pbase; /* base of run time stack */ word plen; /* stack length in bytes */ char pname[PNMLEN+1]; /* process name */ int pargs; /* initial number of arguments */ int (*paddr)(); /* initial code address */ }; מערכות הפעלה - תרגול 2
טבלת תהליכיםproc.h - #define NULLPROC 0 /* id of the null process; it */ /* is always eligible to run */ extern struct pentry proctab[]; extern int numproc; /* currently active processes */ extern int nextproc; /* search point for free slot */ extern int currpid; /* currently executing process */ מערכות הפעלה - תרגול 2
מימוש של resched- resched.c int resched() { register struct pentry *optr; /* pointer to old process entry */ register struct pentry *nptr; /* pointer to new process entry */ optr = &proctab[currpid]; if ( optr->pstate == PRCURR ) { /* no switch needed if current prio. higher than next */ /* or if rescheduling is disabled ( pcxflag == 0 ) */ if ( sys_pcxget() == 0 || lastkey(rdytail) < optr->pprio || ( (lastkey(rdytail) == optr->pprio) && (preempt > 0) ) ) return; /* force context switch */ optr->pstate = PRREADY; insert(currpid,rdyhead,optr->pprio); } /* if */ else if ( sys_pcxget() == 0 ) { מערכות הפעלה - תרגול 2
מימוש של resched- resched.c } /* if */ else if ( sys_pcxget() == 0 ) { kprintf("pid=%d state=%d name=%s", currpid,optr->pstate,optr->pname); panic("Reschedule impossible in this state"); } /* else if */ /* remove highest priority process at end of ready list */ nptr = &proctab[ (currpid = getlast(rdytail)) ]; nptr->pstate = PRCURR; /* mark it currently running */ preempt = QUANTUM; /* reset preemption counter */ ctxsw(&optr->pregs,&nptr->pregs); /* The OLD process returns here when resumed. */ return; } מערכות הפעלה - תרגול 2
מימוש של ctxsw • ההנחה היא שכל תהליך שאינו רץ כרגע נמצא באמצע הקריאה ל-ctxsw • פעולות של ctxsw • החלפת ערך האוגר SP של התהליך היוצא בזה של התהליך הנכנס – הפעלת מחסנית של התהליך הנכנס => מעבר לתהליך הנכנס • ערך הSP נשמר בטבלת התהליכים • שמירת אוגרים נוספים (DI, SI) במחסנית • התהליך מגיע לשגרה במצב PRCURR • כאן לא בהכרח תתבצע החלפת התהליכים (לפי מדיניות ההחלפה) • אם בכל זאת ההחלפה מתבצעת התהליך עובר למצב של PRREADY ונכנס לתור ה-READY מערכות הפעלה - תרגול 2
ctxsw – ctxsw.asm ; void ctxsw(opp,npp) ; char *opp, *npp; ; Stack contents upon entry to ctxsw: ; SP+4 => address of new context stack save area ; SP+2 => address of old context stack save area ; SP => return address _ctxsw proc near push bp mov bp,sp ; frame pointer pushf ; flags save interrupt condition cli ; disable interrupts just to be sure push si push di ; preserve registers mov bx,[bp+4] ; old stack save address mov [bx],sp mov bx,[bp+6] ; new stack save address mov sp,[bx] pop di pop si popf ; restore interrupt state pop bp ret _ctxsw endp הקשר א' הקשר ב' מערכות הפעלה - תרגול 2
שימו לב! • כאשר תהליך א' קורא ל-resched, ההקשר של התהליכים מתחלף לפני ש-resched נגמרת, אבל resched ממשיכה להתבצע ובסופה היא מחזירה את החישוב לתהליך ב'. • סוף החישוב של resched מתבצע בהקשר של תהליך ב' • בשלב מאור יותר, כשתהליך א' יחזור לביצוע, הוא ימשיך את הביצוע resched (ו-ctxsw) בדיוק מהנקודה בה הוחלף ההקשר מקודם, כך שבסה"כ הוא מבצע את כל פקודות המכונה ב-resched וב-ctxsw מערכות הפעלה - תרגול 2
המחסנית בזמן קריאה ל-ctxsw • תסריט: תהליך B התחלף בתהליך C, ..., תהליך D התחלף בתהליך A, תהליך A מתחלף בתהליך B לפני קריאה תהליך נכנס B תהליך יוצא A … (resched) … (resched) &C -> pregs &B -> pregs &B -> pregs &A -> pregs כתובת חזרה bp flags si di B -> pregs מערכות הפעלה - תרגול 2
המחסנית בזמן קריאה ל-ctxsw • תסריט: תהליך B התחלף בתהליך C, ..., תהליך D התחלף בתהליך A, תהליך A מתחלף בתהליך B אחרי קריאה תהליך נכנס B תהליך יוצא A … (resched) … (resched) &C -> pregs &B -> pregs &B -> pregs &A -> pregs כתובת חזרה bp flags si di A -> pregs מערכות הפעלה - תרגול 2
resched & ctxsw • השדה היחיד בטבלת התהליכים המשמש להחלפת תהליך הוא pregs(השומר את ערך SP מצביע על מחסנית) • איך נשמר כל נתון הקשר: • אוגרים כלליים (AX,BX,CX,DX) נשמרים על ידי הקורא ל-resched אם הערכים שלהם חשובים לו • SI, DI, BP, Flags נשמרים על ידי ctxswעל גבי המחסנית • CS, DS, ES, SS – לא משתנים ולא נשמרים (בפסיקה:ברמת הפסיקה) • IP נשמר ע"י פקודת call ל .resched- הערך המעודכן ברגע ההחלפה לא צריך להישמר! • SP ערכו לאחר הכנסת הקשר למחסנית נשמר בטבלת התהליכים ומשתחזר אוטומטית לערכו הקודם בהוצאה מהמחסנית • משתני התהליך הנוכחי, שדות טבלת התהליכים ותור ready מעודכנים על ידי resched מערכות הפעלה - תרגול 2
eidi.asm – disable(ps) מערכות הפעלה - תרגול 2
disable / restore • פונקציות משתמש ב-XINU מגנות על קטעי קוד קריטים באמצעות סמפורים ואמצעי סנכרון נוספים • גם מערכת ההפעלה צריכה להגן על קטעי קוד קריטים שהיא מריצה • מערכת הפעלה מכילה קטעי קוד הרגישים מאוד לפסיקות (חומרה או תוכנה) • מערכת הפעלה מבצעת מניעת פסיקות באמצעות המקרו disable(ps) מערכות הפעלה - תרגול 2
restore(ps) - eidi.asm • restore(ps) מחזיר את הדגלים שנשמרו ב-ps #define restore(x) sys_restor (x) ; void sys_restor(ps) ; int ps; _sys_restor proc near push bp mov bp,sp ; C calling convenion push [bp+4] popf ; restore flag word pop bp ret _sys_restor endp מערכות הפעלה - תרגול 2
restore(ps) • הדגלים השמורים במשתנה psנדחפים על המחסנית ומיד מוצאים לתוך אוגר הדגלים • restoreמחזיר את המצב הנתון ב-ps. הוא אינו מאפשר בכל מקרה את הפסיקות!!! • מערכת הפעלה מכילה קטעי קוד הרגישים מאוד לפסיקות (חומרה או תוכנה) • צריך לשים לב ש-disableמעדכן את המשתנה אותו הוא מקבל כפרמטר מערכות הפעלה - תרגול 2
שימוש ב-disable/restore • ניתן לראת שקריאות מ"ה ב-XINU "עטופות" בצירוף disable(ps)/restore(ps) ו-ps הוא המשתנה הלוקלי מסוג int • המקרו enable(): מדליק את דגל הפסיקות (קורא ל-sti). במקרים קיצוניים בלבד נרצה להדליק את דגל הפסיקות ללא קשר לערכו האחרון בעת כיבויו. על פי רוב נשתמש ב-restore • למשתמש אסור לקרא ל-disable/enable/restore ; void sys_enabl() _sys_enablprocnear sti;enable interrupts ret _sys_enablendp מערכות הפעלה - תרגול 2
disable(ps)/restore(ps) • מה קורה כשהתהליך מבצע קריאה מפורשת ל-resched בקוד ה"עטוף" ב-disable/restore? • האם תתאפשר החלפת תהליכים? • האם התהליך החדש ירוץ עם פסיקות מכובות? • האם זה מותר? • האם זה קורה בפועל? מערכות הפעלה - תרגול 2
disable(ps)/restore(ps) • קריאות מפורשות ל-resched מותרות ואף נמצאות בקוד של מערכת ההפעלה • disableאוסר טיפול בפסיקות ולא אוסר החלפת התהליכים. לכן, בעת ביצוע קוד מוגן ע"י disable/restore לא יתבצעו החלפות תהליכים "לא רצוניות" (ולא צפויות) אלא רק כאלו שיזומות על ידי קריאה מפורשת ל-resched • מצב הפסיקות (דגלים) נשמר במשתנה לוקלי על מחסנית התהליך הקורה ל-.resched בעת החלפת תהליכים נטען, בין השאר, רגיסטר הדגלים של התהליך החדש הנכנס לריצה • בעת שהתהליך זה יבצע restore הוא ישחזר את מצבו מאז הקריאה האחרונה שלו ל- disable(מצב זה שמור על המחסנית הלוקלית שלו) • שימו לב: ההשפעה של disable לוקלית על התהליך עצמו, וכשתהליך זה יחזור לריצה, ישוחזר מצבו בצורה מלאה מערכות הפעלה - תרגול 2
שגרות שרות – getprio(), getpid() • מטרת שגרות שרות הינה לאפשר לאפליקציה גישה לנתונים שנמצאים בטבלאות ומשתני מ"ה ללא גישה ישירה • טבלאות ומשתני מ"ה אינם אמורים להיות נגישים לאפליקציה ובפרט אסורים לשינוי על ידה • getprioמחזירה את עדיפות התהליך הרצוי. נתון זה נלקח מהשדא pprio של טבלת התהליכים • getpidמחזירה את המזהה התהליך הנוכחי. נתון זה נלקח מהשדה сurrpid • במערכות הפעלה מתקדמות משתנים וטבלאות מ"ה נמצאים בזיכרון שאינו נגיש כלל לתהליכים רגילים. במערכות אלה הגישה אפשרית רק דרך קריאות מ"ה מערכות הפעלה - תרגול 2
getpid() – getpid.c,getprio.c – getprio() SYSCALL getpid() { return(currpid); } SYSCALL getprio(pid) intpid; { structpentry *pptr; intps; disable(ps); if (isbad(pid) || (pptr = &proctab[pid]) -> pstate == PRFREE) { restore(ps); return(SYSERR); } restore(ps); return (pptr->pprio); } מערכות הפעלה - תרגול 2
chprio() – chprio.c מערכות הפעלה - תרגול 2