מערכות הפעלה

Download Report

Transcript מערכות הפעלה

‫מערכות הפעלה‬
‫תרגול ‪ – 4‬ניהול תהליכים‪ ,‬מבני נתונים למימוש‬
‫תהליכים‬
‫מה בתכנית?‬
‫‪ ‬ניהול תהליכים‬
‫‪ ‬מימוש תהליכים ב‪XINU-‬‬
‫‪ ‬טבלת תהליכים‬
‫‪ ‬מימוש תורי תהליכים‬
‫‪ ‬תרגילים‬
‫‪2‬‬
‫ניהול תהליכים‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪3‬‬
‫המעבד מריץ את כל התהליכים אך בכל רגע נתון הוא‬
‫מריץ תהליך בודד‬
‫התהליך המורץ במעבד נקרא תהליך נוכחי ( ‪current‬‬
‫‪)process‬‬
‫תהליכים אחרים מחכים לביצוע במעבד‪ ,‬או מחכים‬
‫לקבלת משאב אחר‬
‫ניהול תהליכים‬
‫‪ ‬מערכת ההפעלה אחראית על כך שבכל רגע ורגע‬
‫יהיה תהליך אחד מוגדר כתהליך הנוכחי‬
‫‪ ‬במידה והתהליך מוותר על מעמדו כתהליך הנוכחי‬
‫או שמערכת ההפעלה מחליטה להחליפו‪ ,‬עליה‬
‫לבחור תהליך אחר שיחליף אותו‬
‫‪4‬‬
‫ניהול תהליכים‬
‫‪‬‬
‫האלגוריתם המחליט האם להחליף את התהליך הנוכחי‬
‫ומי התהליך שיחליפו נקרא זמן (‪)Scheduler‬‬
‫‪‬‬
‫בזמן החלפת התהליך הנוכחי יש לשמור את ההקשר‬
‫שלו (‪( )context‬הכולל את ערכי הרגיסטרים שונים) כדי‬
‫שניתן יהיה לשחזר אותו בזמן החזרתו לביצוע (החלפת‬
‫הקשר‪)Context Switching ,‬‬
‫‪5‬‬
‫מדיניות הזימון‬
‫‪‬‬
‫‪‬‬
‫התהליך הנוכחי נבחר בין תהליכים המחכים למעבד‬
‫קיימות כמה שיטות לבחירת התהליך הנוכחי‪:‬‬
‫‪ ‬מדיניות ‪FIFO‬‬
‫‪ ‬בחירה סדרתית (‪)Round-Robin‬‬
‫‪ ‬בחירה לפי עדיפות‬
‫‪ ‬בחירה לפי זמן ריצה‬
‫‪6‬‬
‫)‪First Come First Serve (or FIFO‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫במקור‪ ,‬תהליך אחד משתמש במעבד עד לסיומו‬
‫כיום‪ ,‬תהליך משתמש במעבד עד שנחסם בהמתנה‬
‫למאורע כלשהו‬
‫מדיניות ללא הפקעה (‪)non-preemptive‬‬
‫תהליכים קצרים "נתקעים" אחרי תהליכים ארוכים =>‬
‫תהליכים קצרים נמצאים הרבה זמן בתור המוכנים‪ ,‬יחסית‬
‫לזמן החישוב שהם צורכים‬
‫‪7‬‬
‫‪Round-Robin‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪8‬‬
‫כל תהליך מקבל את המעבד לפרק זמן קצוב‪ ,‬ולאחריו‬
‫הוא מוחלף באחר אף אם לא סיים‪ .‬ההחלפה נעשית לפי‬
‫סדר קבוע (‪)Round-Robin‬‬
‫חלוקת זמן בין התהליכים השונים הוגנת‬
‫נוכחי‬
‫דוגמה‪:‬‬
‫מדיניות הזימון‬
‫‪‬‬
‫‪‬‬
‫לפי עדיפות‪ :‬לכל תהליך משוייך מספר עדיפות‪ .‬התהליך‬
‫בעל עדיפות גבוהה ביותר המחכה למעבד הופך לתהליך‬
‫הנוכחי‬
‫סדר העדיפויות‪:‬‬
‫‪13‬‬
‫‪9‬‬
‫‪10‬‬
‫‪9‬‬
‫‪8‬‬
‫‪6‬‬
‫‪1‬‬
‫מדיניות הזימון לפי עדיפות‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪10‬‬
‫התהליך הנוכחי יאבד את המעבד רק אם תהליך בעל‬
‫עדיפות גבוהה יותר ידרוש אותו‪ ,‬או עם יוותר עליו בעצמו‬
‫אם תהליך בעל עדיפות גבוהה יותר לא ישחרר את‬
‫המעבד‪ ,‬תהליכים בעלי עדיפות נמוכה יותר לא יתבצעו‬
‫קיימות מ"ה בהן תהליך שמתבצע מאבד את עדיפותו עם‬
‫הזמן‪ ,‬ותהליך שמחכה משפר את עדיפותו כדי לאפשר‬
‫"צדק" גדול יותר בחלוקת משאב המעבד‬
‫מדיניות הזימון – היישום ב‪XINU-‬‬
‫‪‬‬
‫‪ XINU‬משתמשת בשיטת העדיפויות כאשר בין‬
‫התהליכים באותה עדיפות מתבצע ‪Round-Robin‬‬
‫‪‬‬
‫העדיפות של תהליך נקבעת בזמן יצירתו‪ ,‬אך ניתנת‬
‫לשינוי בכל עת‪.‬‬
‫‪11‬‬
‫ דיאגרמת מצבים‬- ‫תהליך‬
send
receive
RECEIVING
wait
signal
WAITING
sleep
wake up
SLEEPING
resched
resched
CURRENT
READY
suspend
SUSPENDED
suspend
resume
Create
12
‫טבלת תהליכים‬
‫‪ ‬קיימת טבלת תהליכים‪ ,‬המיוצגת כוקטור של‬
‫רשומות‪ ,‬בה כל רשומה מייצגת הקשר של תהליך‪,‬‬
‫ולכל תהליך מוקצת רשומה‬
‫‪ ‬רשומה מכילה את המידע על מצב התהליך ואת כל‬
‫המידע על הקשר התהליך הדרוש להפעלתו‬
‫‪ ‬קיימים מספר משתנים המייצגים את מצב התהליך‬
‫או שמציינים שתהליך מחכה למשאב כלשהו‬
‫‪13‬‬
‫טבלת תהליכים ‪ -‬המשך‬
‫‪ ‬האינדקס בטבלת התהליכים משמש כמזהה של‬
‫התהליך (‪)pid – process identifier‬‬
‫‪ ‬המזהה של התהליך הנוכחי שמור במשתנה נפרד‬
‫המאפשר התיחסות לתהליך הנוכחי מקוד מ"ה‬
‫‪ ‬התהליכים המחכים למעבד שמורים בתור דו‪-‬כווני‬
‫על פי עדיפותם‪ .‬תור זה נקרא ‪ready queue‬‬
‫ומבוצע על ידי ‪rdyhead, rdytail‬‬
‫‪14‬‬
proc.h - ‫טבלת תהליכים‬
#define NULLPROC
extern
extern
extern
extern
struct
int
int
int
0
/* id of the null process; it */
/* is always eligible to run */
pentry proctab[];
numproc;
/* currently active processes
nextproc;
/* search point for free slot
currpid;
/* currently executing process
*/
*/
*/
15
proc.h - ‫טבלת תהליכים‬
/* process table entry */
struct
pentry
char
int
int
int
int
char
char
word
char
int
int
{
pstate;
pprio;
psem;
pmsg;
phasmsg;
*pregs;
*pbase;
plen;
pname[PNMLEN+1];
pargs;
(*paddr)();
/* process state: PRCURR, etc.
/* process priority
/* semaphore if process waiting
/* message sent to this process
/* nonzero iff pmsg is valid
/* saved environment
/* base of run time stack
/* stack length
in bytes
/* process name
/* initial number of arguments
/* initial code address
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
};
16
proc.h - ‫טבלת תהליכים‬
#define NPROC
30
/* process state constants */
#define
#define
#define
#define
#define
#define
#define
PRCURR
PRFREE
PRREADY
PRRECV
PRSLEEP
PRSUSP
PRWAIT
'01\'
'02\'
'03\'
'04\'
'05\'
'06\'
'07\'
// process is currently running
// process slot is free
// process is on ready queue
// process waiting for message
// process is sleeping
// process is suspended
// process is on semaphore queue
17
‫תהליך ה‪NULL-‬‬
‫מה עושה מ"ה כאשר אין תהליך הדורש מעבד?‬
‫‪ ‬אחת הדרכים האפשריות היא לתת לזמן לבצע‬
‫לולאה ולחכות עד שתהליך כלשהו יהיה מוכן לחזור‬
‫לביצוע אך לשם כך שקוד הזמן יהיה מורכב יותר‬
‫‪ ‬דרך מועדפת הינה ליצור תהליך שתמיד יהיה מוכן‬
‫לחישוב‬
‫‪18‬‬
‫תהליך ה‪ - NULL-‬המשך‬
‫‪‬‬
‫תהליך זה נקרא ‪ NULL process‬ובנוי כלולאה אינסופית‬
‫מהצורה‬
‫;‪While (TRUE) /* Do nothing */‬‬
‫‪‬‬
‫תהליך ה‪ NULL-‬יקבל את העדיפות המזערית האפשרית‬
‫ולכן יהפך לנוכחי רק כאשר אין אף תהליך אחר שדורש‬
‫את המעבד‬
‫המזהה של תהליך זה הוא ‪0‬‬
‫‪‬‬
‫‪19‬‬
‫רשימות תהליכים ב‪XINU-‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪20‬‬
‫במהלך הריצה‪ ,‬בכל רגע‪ ,‬חלק ניכר מתהליכים נמצאים‬
‫במצב המתנה למשאבים (‪ ,CPU‬קלט‪ ,‬הודעה וכו')‬
‫מכיוון שמספר תהליכים עלולים להמתין לאותו משאב יש‬
‫לנהל תור המתנה‬
‫כלל‪ :‬התהליך יכול להופיע בכל זמן נתון בתור אחד‬
‫מספר התהליכים והתורים השונים הוא סופי‬
‫מימוש הרשימות‬
‫‪ ‬בעיה‪ :‬הרשימות הנחוצים עבור המשאבים השונים הם‬
‫מגוונים (‪, FIFO‬לפי עדיפות‪ ,‬תור הפרשים)‬
‫עובדה זו מקשה על ניהול מערכת הרשימות‬
‫‪‬‬
‫‪21‬‬
‫פתרון‪ :‬מבנה אחיד לכל הרשימות שמספק את כל מגוון‬
‫הדרישות ומאפשר אחידות בטיפול‬
‫מבנה רשומה‬
‫ מצביע האיבר הבא‬- qprev
‫ מצביע האיבר הקודם‬- qnext
‫ עדיפות של התהליך‬,‫ מפתח‬- qkey
head
^
MININT



tail
next
KEY
prev
next
MAXINT
prev
^
22
q.h – ‫הגדרת תור‬
:‫מבנה רשומה‬
struct qent {
int qkey;
int qnext;
int qprev;
};
23
‫תורים ב‪XINU-‬‬
‫‪‬‬
‫תור התהליכים המחכים למעבד‬
‫‪ ‬תור עדיפויות‪ ,‬הכנסה לפי מפתח – עדיפות‬
‫‪ ‬בתור עדיפויות התהליכים מסודרים בסדר עולה בכיוון ראש => זנב‬
‫‪ ‬הוצאה – איבר אחרון‬
‫‪‬‬
‫תור התהליכים המחכים לסמפור – ‪FIFO‬‬
‫‪ ‬הוצאה – מראש התור‬
‫‪‬‬
‫תור הרדומים – תור ההפרשים‬
‫‪24‬‬
‫מימוש רשימה בעזרת מערך‬
0
Key
Next
Prev
25
33
3
14
0
1
2
NPROC
3
32
...
‫איבר קודם‬
NPROC-1
‫איבר הבא‬
NPROC
...
2NSEM+4
Head=32
MININT
3
-1
Tail=33
MAXINT
-1
1
25
q.h – ‫הגדרת תור‬
Inline list
manipulation
procedures
extern struct qent q[];
#define isempty(list) (q[(list)].qnext >= NPROC)
#define nonempty(list) (q[(list)].qnext < NPROC)
#define firstkey(list) (q[q[(list)].qnext].qkey)
#define lastkey(tail) (q[q[(tail)].qprev].qkey)
#define firstid(list) (q[(list)].qnext)
26
‫פונקציות לטיפול ברשימות‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪27‬‬
‫‪ enqueue‬הכנסת איבר לסוף הרשימה‬
‫‪ dequeue‬ניתוק איבר מהרשימה והחזרת האינדקס שלו‬
‫‪ insert‬הכנסת תהליך לרשימה לפי ערכו של ‪key‬‬
‫‪ getfirst‬הסרת התהליך הראשון ברשימה והחזרת‬
‫האינדקס‬
‫‪ getlast‬הסרת התהליך האחרון ברשימה והחזרת‬
‫האינדקס‬
‫‪ newqueue‬איתחול רשימה חדשה במבנה ה‪q-‬‬
insert.c – ‫הכנסה לפי מפתח‬
#include <conf.h>
#include <kernel.h>
#include <q.h>
int insert(proc, head, key)
int proc; /* process to insert
*/
int head; /* q index of head of list
*/
int key; /* key to use for this process */
{
int
next;
/* runs through list */
int
prev;
next = q[head].qnext;
while (q[next].qkey < key) /* tail key is MAXINT */
next = q[next].qnext;
q[proc].qnext = next;
q[proc].qprev = prev = q[next].qprev;
q[proc].qkey = key;
q[prev].qnext = proc;
q[next].qprev = proc;
return(OK);
}
28
queue.c – ‫הוספה לסוף התור‬
#include <conf.h>
#include <kernel.h>
#include <q.h>
int enqueue(item, tail)
int item;
int tail;
{
struct
qent
struct
qent
*tptr; /* tail entry */
*mptr; /* item entry */
tptr = &q[tail];
mptr = &q[item];
mptr->qnext = tail;
mptr->qprev = tptr->qprev;
q[tptr->qprev].qnext = item;
tptr->qprev = item;
return(item);
}
29
queue.c – ‫הוצאה מתור‬
#include <conf.h>
#include <kernel.h>
#include <q.h>
int dequeue(item)
int item;
{
struct qent
*mptr; /* q entry for item */
mptr = &q[item];
q[mptr->qprev].qnext = mptr->qnext;
q[mptr->qnext].qprev = mptr->qprev;
return(item);
}
30
– ‫הסרת התהליך הראשון ברשימה‬
getitem.c
#include <conf.h>
#include <kernel.h>
#include <q.h>
int
int
{
getfirst(head)
head; /* q index of head of list */
int
proc; /* first process on the list
if ((proc=q[head].qnext) < NPROC)
return( dequeue(proc) );
else
return(EMPTY);
*/
}
31
– ‫הסרת התהליך האחרון ברשימה‬
getitem.c
#include <conf.h>
#include <kernel.h>
#include <q.h>
int
int
{
getlast(tail)
tail; /* q index of tail of list */
int proc;
/* last process on the list */
if ((proc=q[tail].qprev) < NPROC)
return( dequeue(proc) );
else
return(EMPTY);
}
32
– ‫איתחול רשימה חדשה‬
newqueue.c
int
{
newqueue()
struct
struct
int
qent
*hptr;
qent
*tptr;
hindex, tindex;
/* address of new list head */
/* address of new list tail */
/* head and tail indexes
*/
/* nextqueue is global variable
hptr = &q[ hindex=nextqueue++ ];
/* giving next used q pos. */
tptr = &q[ tindex=nextqueue++ ];
hptr->qnext = tindex;
hptr->qprev = EMPTY;
hptr->qkey = MININT;
tptr->qnext = EMPTY;
tptr->qprev = hindex;
tptr->qkey = MAXINT;
return(hindex);
}
*/
33
‫טבלת תהליכים ‪ /‬תורים‬
‫יישום התורים של התהליכים מתבצע על ידי פונקציות‬
‫ניהול תורים לכן‪:‬‬
‫‪ ‬אין בטבלת תהליכים מצביעים על סדר התורים‬
‫‪ ‬ניהול התורים נעשה בטבלת התורים (‪)q‬‬
‫‪ ‬לכל תהליך משוייכת רשומה בטבלת תהליכים וכן‬
‫בטבלת התורים‬
‫‪34‬‬
‫טבלת תהליכים ‪ /‬תורים‬
‫‪ ‬בטבלת התורים יש יותר רשומות מטבלת‬
‫התהליכים‪ NPROC.‬רשומות ראשונות בטבלת‬
‫התורים משויכות לתהליכים המקבילים בטבלת‬
‫התהליכים‪.‬‬
‫‪ ‬שאר הרשומות בטבלת התורים משמשות‬
‫כראשי‪/‬זנבות תורים שונים (‪, Ready‬סמפורים‪,‬‬
‫וכו')‬
‫‪35‬‬
‫תרגיל ‪1‬‬
‫‪‬‬
‫נתון‪:‬‬
‫‪ ‬קיימים שני תהליכים‬
‫‪ ‬תהליך ראשון מעלה את המונה ב‪1-‬‬
‫‪‬‬
‫מטרה‪:‬‬
‫‪ ‬על התהליך השני להדפיס את ערך המונה בכל פעם‬
‫שמתבצעת החלפת התהליכים במעבד‬
‫‪36‬‬
‫פתרון‬
unsigned long int who_is_current = 0, count = 0;
xmain)(
{
int Inc(), Pr)(;
resume( create(Inc, INITSTK, INITPRIO, "proc 1", 0));
resume( create(Pr, INITSTK, INITPRIO, "proc 2", 0));
while(1)
;
// Do nothing but serve as alternative to Inc
// Pr may not be ready (printing)
}
37
1 ‫פתרון – המשך‬
Inc()
{
int mypid;
mypid = getpid(); /* Store my pid */
while (1)
{
count = 0;
who_is_current = mypid; /* Signal I am current */
/* As long as I am current, increment count */
while(who_is_current == mypid)
count++;
/* I have been replaced and become current again */
} /* while */
} /* Inc */
38
2 ‫פתרון – המשך‬
Pr()
{
char str[80];
int count_value;
int mypid;
mypid = getpid();
while(1)
{
count_value = count; /* Store count value aside, locally */
who_is_current = mypid; /* Signal I am current */
sprintf(str, "count_value = %lu\n",
count_value);
printf(str);
while (who_is_current == mypid) /* Do not continue until */
;
/* I am replaced and current again */
} /* while */
} /* Pr */
39
2 ‫תרגיל‬
‫ כפי שהוא נראה ברגע‬Q ‫יש למלא את המבנה של‬
/**/ -‫שהתכנית הגיע אל הנקודה המסומנת ב‬
#include <kernel.h>
extern SYSCALL sleep(), wait();
int nop();
xmain() {
int sem = screate(1);
resume(create(sleep,
INITSTK, 20, “pr1”, 1, 25));
resume(create(wait,
INITSTK, 22, “pr2”, 1, sem));
resume(create(wait,
INITSTK, 24, “pr3”, 1, sem));
resume(create(sleep,
INITSTK, 21, “pr4”, 1, 10));
resume(create(nop,
INITSTK, 20, “pr5”, 0));
resume(create(sleep,
INITSTK, 20, “pr6”, 1, 30));
resume(create(wait,
INITSTK, 20, “pr7”, 1, sem));
resume(create(nop,
INITSTK, 20, “pr8”, 0));
create(sleep, INITSTK, 33, “pr9”, 1, 44);
/**/
nop();
}
nop()
{
while(1){};
}
40
3 ‫תרגיל‬
pid
key
next
prev
proc/list
name
…
…
…
…
…
SEM
pr3
pr7
SLEEP
pr4 (10)
pr1 (15)
1
READY
pr5 (20)
pr6 (5)
2
pr8 (20)
41