יפו – המכללה האקדמית תל אביב – מערכות הפעלה 2# תרגיל בית מספר ~1~

‫מערכות הפעלה – המכללה האקדמית תל אביב – יפו‬
‫סמסטר א‪ ,‬תשע"ה‬
‫מרצה‪ :‬כרמי מרימוביץ‪ .‬מתרגל‪ :‬צבי מלמד‬
‫תרגיל בית מספר ‪#2‬‬
‫הוראות כלליות‬
‫בתרגיל הזה שתי שאלות‪:‬‬
‫‪ .1‬תכנית משתמש ב ‪ LINUX‬שמשתמשת בסמפורים ובזיכרון משותף‬
‫‪ .2‬מימוש סמפורים ב‪ XV6 -‬וכתיבת תכנית בדיקה‬
‫הגשה עד יום שני ‪ .5/1/15‬לכל מי שמתכנן לבקש הארכות – מועד זה כבר כולל הארכות‪...‬‬
‫הוראות ההגשה המפורטות נמצאות באתר המעבדה‪.‬‬
‫הסביבה הקובעת להרצת התרגילים‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫עבור לינוקס‪ :‬סביבת ‪Linux-ubuntu/VMWARE of the lab‬‬
‫עבור ‪ :XV6‬סביבת ‪ BOCHS /CYGWIN‬במעבדה‪.‬‬
‫כלומר‪ ...‬אתם יכולים לפתח ולפתור את התרגילים בסביבה הנוחה לכם‪ .‬אבל‪ ,‬לפני ההגשה‪ ,‬חובתכם‬
‫לוודא שהתרגילים רצים בסביבה הפורמלית‪-‬סטנדרטית‪.‬‬
‫~‪~1‬‬
‫שאלה ‪ - #1‬תכנית ‪PS – Prime-Semiprime‬‬
‫הערה מקדימה‪ :‬ידוע מה זה מספר ראשוני ‪ .prime number‬אולי קצת פחות ידוע מה זה ‪– semiprime number‬‬
‫זהו מספר שמתקבל ממכפלה של שני מספרים ראשוניים‪.‬‬
‫עליכם לכתוב זוג תכניות בלינוקס – לתכנית אחת נקרא ‪( psp‬קיצור של ‪ ) prime-SemiPrime‬והתכנית השנייה‬
‫‪ factor‬שפועלות באופן הבא‪.‬‬
‫התכנית ‪psp‬‬
‫תחילה תהליך האב מייצר ‪ NO_SONS‬בנים‪ .‬לאחר מכן‪ ,‬תהליך האב קורא זוגות מספרים מתוך הקלט הסטנדרטי‬
‫עד ‪( EOF‬הניחו שהקלט מכיל מספר זוגי של מספרים)‪ .‬את המספרים שהאב קורא הוא שם במערך הציקלי‬
‫‪( num_array‬זוג מספרים בכל פעם)‪ .‬לאחר מכן (כלומר לאחר ‪ )EOF‬האב מוסיף למערך ‪ num_arr‬אפסים‬
‫במספר כפול ממספר הבנים (כלומר סה"כ ‪ NUM_SONS * 2‬אפסים‪ .‬האב ממתין לבנים ומסתיים‪.‬‬
‫מטרתם של תהליכי הבנים עובדים באופן הבא‪ .‬מטרתם "לשלוף" בכל פעם זוג מספרים מהמערך ‪. .num_array‬‬
‫לכל מספר שנשלף התהליך מחשב את המספר הראשוני הבא (גדול או שווה למספר שקרא)‪ .‬כלומר‪ ,‬הוא מחשב‬
‫זוג מספרים ראשוניים‪ .‬לאחר מכן הוא מכפיל את שני המספרים הראשוניים‪ ,‬ואת הסמי‪-‬פריים הזה הוא כותב‬
‫למערך ציקלי אחר‪ ,‬בשם ‪ .sp_array‬כאשר בן קורא שני אפסים‪ ,‬הוא מסתיים‪( .‬למעשה‪ ,‬מספיק לבדוק שהוא‬
‫נתקל באפס אחד בלבד‪ ,‬אבל מכיוון שההכנסה וההוצאה מהמערך הם בזוגות‪ ,‬משתמשים כאן בשני אפסים‪.‬‬
‫התכנית ‪factor‬‬
‫התכנית מורכבת מתהליך בודד‪ .‬התהליך קורא ("שולף"‪ ,‬מוציא) מספר מהמערך ‪ ,sp_array‬מחשב את שני‬
‫הגורמים של המספר ומדפיס אותם בפורמט זהה לפורמט של הפקודה ‪ factor‬בלינוקס (קיימת פקודה כזאת)‪.‬‬
‫הערות והנחיות נוספות‪:‬‬
‫א‪.‬‬
‫ב‪.‬‬
‫ג‪.‬‬
‫ד‪.‬‬
‫ה‪.‬‬
‫ו‪.‬‬
‫ז‪.‬‬
‫עליכם להשתמש בזיכרון משותף עבור המערכים הציקליים והמצביעים שקשורים בהם‪.‬‬
‫שמות קבצי המקור שלכם יהיו‪ . psp.c, factor.c :‬כמו כן‪ ,‬יהיו לכם שני קבצים נוספים ‪shared.c , shared.h‬‬
‫שבהם יש הגדרות של קבועים‪ ,‬מבנים‪ ,‬פרוטוטייפים (בקובץ ‪ )shared.h‬ומימוש של הפונקציות‪ .‬הכוונה היא‬
‫שבקבצים האלו יהיה כל מה שמשותף לשתי התכניות‪ .‬יש למנוע כפילויות של קוד ככל שניתן‪.‬‬
‫כל גישה למערך הציקלי (כתיבת נתון או שליפתו) מחייבת המתנה של ‪ 0.2‬שניות (השתמשו בפונקציה‬
‫‪ .)usleep‬כתיבה או שליפה של זוג מספרים (המערך ‪ )num_array‬מצריכה רק השהיה אחת‪ .‬כל גישה‬
‫למערך ‪( sp_array‬כתיבה או שליפה של מספר בודד) גם היא מצריכה השהיה אחת‪.‬‬
‫יש לסנכרן את הגישה למערכים באמצעות סמפורים‪ .‬מומלץ להשתמש ב ‪ named-semphore‬של ‪ POSIX‬כפי‬
‫שראינו במעבדה ‪.#9‬‬
‫קבועי התכנית ‪:‬‬
‫‪#define NO_SONS 3‬‬
‫‪// number of children process in psp program‬‬
‫‪#define NO_NUM_ARR 8‬‬
‫‪// number of entries in num_array.‬‬
‫‪// of course, number of pairs is half of that‬‬
‫‪#define NO_SP_ARR 3‬‬
‫‪// number of entries in sp_array.‬‬
‫‪#define U_SLEEP 200000‬‬
‫‪// micro-sec of delay.‬‬
‫בתרגיל זה‪ ,‬אין חשיבות ליעילות האלגוריתמים בחיפוש המספר הראשוני הבא או מציאת הגורמים של‬
‫הסמיפריים‪( .‬כמובן‪ ...‬במידה סבירה ‪ ...‬לולאות ‪ FOR‬זה סביר‪ .‬מי שיצליח לפתח אלגוריתם אקספוננציאלי זה‬
‫לא סביר‪.)...‬‬
‫הניחו שהקלט תקין‪ ,‬מכיל רק מספרים חיוביים‪ ,‬והמספרים שתקבלו לא יגרמו לחריגות כלשהן‪ .‬בקיצור‬
‫הטיפוס ‪( unsigned int‬או ‪ )int‬יספק את צרכינו‪.‬‬
‫~‪~2‬‬
‫המלצות‬
‫‪ o‬הפרידו בין חגיגות‪ .‬וודאו שהפונקציה (או פונקציות) שמטפלות במערך ציקלי עובדות היטב בנפרד מהזיכרון‬
‫המשותף‪.‬‬
‫‪ o‬מומלץ להשתמש ב ‪ struct‬עבור כל אחד מהמבנים הציקליים‪ .‬הוא יכיל הן את המערך והן את המשתנים עבור‬
‫גודלו והאינדקס‪.‬‬
‫‪ o‬בקריאות לפונקציות )(‪ shm_open‬ו‪ mmap -‬אפשר להשתמש בדגלים הבאים‪:‬‬
‫;)‪shm_open(<name>, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG‬‬
‫;)‪mmap(NULL, <size>, <protection flags for read & write>, MAP_SHARED, <fd>, 0‬‬
‫~‪~3‬‬
XV6 ‫ מימוש סמפורים ב‬- #2 ‫שאלה‬
‫ וכן לכתוב תכנית משתמש שבודקת‬,‫שמוגדר להלן‬API ‫עליכם לממש קריאות מערכת שממשות סמפורים על פי ה‬
.‫ עליכם לתעד את אופן מימוש הסמפורים ואת תכנית המשתמש שכתבתם‬.‫ומדגימה את השימוש בסמפורים‬
:‫פרוטוטייפ הפונקציות‬
int
int
int
int
int
sem_open(char *name, int create, int init);
sem_close(int sd);
sem_wait(int sd);
sem_trywait(int sd);
sem_signal(int sd);
:‫להלן תיאור ההתנהגות הדרושה מכל אחת מהפונקציות‬
int sem_open(char *name, int create, int init);
a) name - the name of the semaphore. This is a system wide name. The max length is 6.
b) create – indicates if a new semaphore should be created
o 1 means that a new semaphore should be created if a semaphore with this name does not
exist in the system. In this case, the newly created semaphore gets the value init as it’s initial
value. However, if such a semaphore exists in the system, then the ‘create’ and ‘init’ are
ignored.
o 0 means, don’t create a new semaphore.
c) If create==0, and there isn’t such a semaphore in the system – a failure return code of -1 is returned.
d) If create==0, and there is already such a semaphore the function returns sd (semaphore descriptor)
which is the lowest available index in the process semaphore array. This works similar to the way
that open file works.
e) if create==1, sd will be returned as described in the previous bullet, i.e. the lowest available number
in the semaphore array. Of course, this entry points to the semaphore that already existed in the
system prior to this call, or to the one which was just created.
f) init – the initial value in case a new semaphore is created. It should be non-negative.
g) A kernel constant NOSEM limits the number of open semaphores per process, similar to the NOFILE.
Of course, if the call might result in exceeding NOSEM (per this process), the call fails and returns -1.
h) A kernel constant NSEM limits the total number of active semaphores in the system. This is similar
to NFILE.
int sem_close(int sd);
a) This call closes an open semaphore. Error code of -1 is returned if sd is index of non-valid entry in
the process semaphore table.
b) If this is the last reference to the semaphore, then the semaphore is destroyed. (refer to the similar
behavior of file close().
~4~
int sem_wait(int sd);
This is standard wait operation of semaphores.
a) This call decrements the value of the semaphore, provided it does not become negative. If this is
not possible, the process is suspended.
b) The process is resumed when the decrement is possible.
int sem_trywait(int sd);
This call tries to decrement the value of the semaphore, provided it does not become negative. If this is
possible, the call returns 0 (success). If this is not possible, the call returns -1 (failure), but the process is
NOT suspended.
int sem_signal(int sd);
This is standard ‘signal’ (or ‘post’) operation of semaphores.
a) The value of the semaphore is incremented.
b) wakeup other process who might be waiting on this semaphore.
Suggestions
 Don’t invent the wheel, and don’t invent the semaphore. Refer to handling
of files (ofile and ftable) to get ideas on ways to implement things.
 Add a global variable stable to the system, defined as follows:
struct {
struct spinlock gslock; //global semaphore lock
struct {
struct spinlock sslock; // single semaphore lock
.....
.....
} sem[NSEM];
} stable;
 Use gslock for sem_open and sem_close.
 Use sslock for sem_wait, sem_trywait and sem_signal.
 Consider the fork() – child should inherit all opened semaphores from the
parent.
 Consider the implications of exit().
!!‫בהצלחה‬
~5~