שבוע 8# תהליכים : פרק משותף זיכרון : נושא Shared Memory – ` קורס

‫שבוע ‪#8‬‬
‫פרק‪ :‬תהליכים‬
‫נושא‪ :‬זיכרון משותף – ‪Shared Memory‬‬
‫קורס מערכות הפעלה א'‬
‫מכללת הדסה ‪ /‬מכללה חרדית‬
‫צבי מלמד‬
‫‪Tzvi.Melamed@gmail.com‬‬
‫הרצאות הקורס מבוססות במידה רבה ביותר על ההרצאות של ד"ר יורם ביברמן‬
‫© כל הזכויות שמורות לד"ר יורם ביברמן ולצבי מלמד‬
‫©צבי מלמד‬
‫‪1‬‬
‫זכרון משותף ‪ -‬מבוא‬
‫•‬
‫עד כה ‪ -‬כלי ה‪ IPC -‬שראינו היו סדרתיים )תור = ‪(FIFO‬‬
‫– ‪ -‬כולל ה ‪message-queue‬‬
‫•‬
‫זיכרון משותף‪:‬‬
‫– מאפשר לתהליכים שחולקים אותו לפנות לאותו קטע‪-‬זיכרון )= מערך(‬
‫בגישה ישירה‪ :‬הם יכולים לקרוא ‪ /‬לכתוב על כל תא רצוי‪.‬‬
‫– מהירות‪ :‬המידע אינו עובר דרך תווך ביניים כלשהו בין הכותב לקורא‬
‫•‬
‫מאידך‪:‬‬
‫– אתגר‪ :‬כיצד לגרום לתהליכים לסנכרן\לתאם את הפניה לזכרון המשותף‪,‬‬
‫כך שתהליך ב' לא יקרא נתונים לפני שתהליך א' סיים להכינם‬
‫– תזכורת‪ :‬במחשבים מודרניים אפילו פעולת השמה על הזיכרון אינה‬
‫אטומית!‬
‫– בדרך כלל הסינכרון יעשה באמצעות סמפורים )מנעולים( ]בסמסטר הבא[‬
‫©צבי מלמד‬
‫‪2‬‬
‫זכרון משותף ‪ -‬מבוא‬
‫• כזכור‪ :‬מערכת ההפעלה מגינה עלינו‪ ,‬בכך שהיא מפרידה את‬
‫מרחב הכתובות של כל תהליך מכל האחרים‪.‬‬
‫• בזכות זאת‪ :‬תהליך ב' לא יכול לפגוע בנתונים של תהליך א'‬
‫• ‪ ‬כדי להשיג את שיתוף הזיכרון יש להשתמש באמצעים‬
‫מיוחדים‪.‬‬
‫• כפי שציינו‪ ,‬זיכרון משותף‪ ,‬תור הודעות וסמפור‪ ,‬נקראים‬
‫‪ ,XSI IPC‬מקורם משותף )‪ (System V‬ויש ביניהם קווי‬
‫דמיון‪ ,‬בפרט ברעיון של מפתח חיצוני לעומת מזהה פנימי‪,‬‬
‫באמצעותם פונים ל ַאובייקט‪.‬‬
‫©צבי מלמד‬
‫‪3‬‬
‫זכרון משותף – ייצור המפתח החיצוני‬
‫• גם בזיכרון משותף נתחיל ביצור מפתח‪ ,‬באמצעות )(‪:ftok‬‬
‫; ‪key_t key‬‬
‫{ )‪if ((key = ftok("~yoramb/os", 'b')) == -1‬‬
‫; )…(‪perror‬‬
‫; )…(‪exit‬‬
‫}‬
‫©צבי מלמד‬
‫‪4‬‬
‫זכרון משותף ‪ -‬יצירת האוביקט‬
‫•‬
‫•‬
‫•‬
‫•‬
‫נשתמש ב ַ מפתח כדי להקצות את האובייקט הדרוש‪ ,‬ויוחזר לנו מזהה האובייקט‪:‬‬
‫; ‪int shm_id‬‬
‫‪shm_id = shmget(key,‬‬
‫‪SHM_SIZE,‬‬
‫; )‪IPC_CREAT | IPC_EXCL | 0600‬‬
‫{ )‪if (shm_id == -1‬‬
‫…‬
‫}‬
‫הדבר דומה ליצירת‪/‬קבלת האוביקט בתור ההודעות‬
‫הארגומנט הראשון ‪ -‬והשלישי דומים לאלה בפונקציה )(‪,msgget‬‬
‫הארגומנט השני ‪ -‬גודל שטח הזיכרון הרצוי בבתים‬
‫– שטח הזיכרון המוקצה מאופס‬
‫– בפועל‪ ,‬מוקצים דפים שלמים‪ ,‬חלק מהדף האחרון המוקצה לא יעמוד לרשותנו‬
‫©צבי מלמד‬
‫‪5‬‬
‫עוד על )(‪shmget‬‬
‫• סוגי שגיאות‪:‬‬
‫– ביקשנו גודל שקטן מהמינימום ‪ SHMMIN‬או גדול‬
‫מהמקסימום ‪ SHMMAX‬המותרים‬
‫– האוביקט כבר קיים והעברנו ‪IPC_CREAT | IPC_EXCL‬‬
‫– עברנו את המכסות – כמות האוביקטים או כמות הזכרון‬
‫המוקצית למטרה זאת‬
‫– האוביקט לא קיים )וגם לא הדלקנו את ‪(IPC_CREAT‬‬
‫– אין לנו הרשאת גישה‬
‫– המערכת לא הצליחה להקצות זיכרון‬
‫©צבי מלמד‬
‫‪6‬‬
‫זכרון משותף ‪ -‬קבלת האוביקט‬
‫•‬
‫•‬
‫קטע זה לביצוע על ידי תהליכים‬
‫נוספים שירצו להשתמש בזכרון‬
‫המשותף שכבר הוקצה‬
‫הארגומנט השלישי‪:‬‬
‫– לא ליצור אוביקט‪/‬להקצות שטח‬
‫– לא להכשל אם הוא כבר מוקצה‬
‫– ההרשאות ‪ – 600‬קריאה ‪/‬כתיבה‬
‫למשתמש הנוכחי‪.‬‬
‫– ההרשאות נקבעות בזמן היצירה‪,‬‬
‫ונבדקות עבור התהליך שרק מקבל‬
‫את האוביקט – האם כוונותיו‬
‫תואמות את ההרשאות שנוצרו‬
‫לקטע‬
‫;)‪key = ftok(......‬‬
‫; ‪int shm_id‬‬
‫= ‪shm_id‬‬
‫‪shmget(key,‬‬
‫‪0, // OR: SHM_SIZE‬‬
‫; )‪0600‬‬
‫{ )‪if (shm_id == -1‬‬
‫…‬
‫}‬
‫©צבי מלמד‬
‫‪7‬‬
‫שקף גיבוי‪IPC_PRIVATE :‬‬
‫•‬
‫•‬
‫•‬
‫•‬
‫•‬
‫במקום להשתמש ב‪ KEY-‬חיצוני כלשהו‪ ,‬ניתן להשתמש בערך ‪.IPC_PRIVATE‬‬
‫נוצר אזור "זכרון‪-‬משותף" פרטי לאותו תהליך‪ .‬תהליך אחר שיבצע קריאה דומה‪,‬‬
‫לא יקבל את אותו האזור‪ ,‬אלא ייווצר עבורו אזור אחר )או תור אחר עבור ‪(msgget‬‬
‫לכאורה – למה זה טוב?‬
‫– לשיתוף בין אבא ובניו‬
‫– לדוגמא‪ :‬זאת תופעה מקובלת שתהליך שרת יוצר את תהליכים הקליינטים‬
‫כיצד מתבצע השיתוף?‬
‫– האבא יצר את ה‪) shared-memory-‬הקריאה ל )(‪ (shmget‬לפני שביצע את ה‪-‬‬
‫‪FORK‬‬
‫– האב קיבל את המפתח הפנימי‪ .‬המפתח עבר לבנים בתהליך שכפול הזכרון‬
‫עלות ‪ /‬תועלת לעומת השיטה של מפתח חיצוני‪:‬‬
‫– ‪ +‬חסכנו את הצורך במפגש ‪ rendezvous‬והסכנה של קונפליקט במפתחות‬
‫– ‪ -‬הגבלנו עצמנו לאב קדמון ולבניו‪ ,‬צאצאיו‬
‫©צבי מלמד‬
‫‪8‬‬
‫סיכום ביניים‬
‫‪ (1‬מערכת ההפעלה הקצתה בזיכרון המחשב שטח לזכרון משותף‬
‫‪ (2‬ניתנה הרשאת גישה אליו לתהליכים שבצעו )(‪shmget‬‬
‫‪ (3‬קיים מזהה פנימי בשביל להתייחס לאזור הזכרון המשותף‬
‫מה חסר?‬
‫•‬
‫לשלב את שטח הזיכרון במרחב הכתובות של תהליכים אלה;‬
‫•‬
‫כלומר‪ :‬כרגע )עדיין( אין כתובות )עליהן מצביע פוינטר כלשהו(‬
‫בתהליכים השונים באמצעותן הם יפנו לשטח הזיכרון המשותף‪.‬‬
‫©צבי מלמד‬
‫‪9‬‬
‫הקדמונת קצרצרה לזיכרון ווירטואלי‬
‫•‬
‫באופן ווירטואלי – התוכנית "רואה" ‪-‬‬
‫זכרון לינארי רציף‬
‫•‬
‫הזכרון מחולק לסגמנטים‬
‫•‬
‫לכל סגמנט יש כתובת בסיס‪ ,‬גודל‬
‫וקוד‪/‬אופן הגנה‬
‫•‬
‫למעשה חלוקה עדינה יותר – לדפים‬
‫בגודל ‪) 4K‬תלוי מכונה(‬
‫•‬
‫דף יכול להיות בזכרון הראשי )ירוק(‪ ,‬או‬
‫בזכרון המשני )צהוב(‪ ,‬או ‪) invalid‬אדום(‬
‫•‬
‫מנגנון חומרה דואג לתרגומים המסובכים‬
‫מכתובת ווירטואלית שקיימת בתוכנית‬
‫לכתובת פיזית ‪ +‬בדיקת הרשאות גישה‬
‫©צבי מלמד‬
‫‪10‬‬
‫חיבור )‪ (attach‬התהליך לזכרון המשותף‬
‫• לאחר שמקצים את הזכרון המשותף‪ ,‬צריך להוסיף אותו לטבלת‬
‫דפי‪-‬הזכרון של התהליך‪.‬‬
‫• מתבצע על ידי הקריאה ל‪) shmat -‬נשמע כמו שמטע ‪" -‬סמרטוט" ‪-‬‬
‫אבל בעצם‪(shared-memory-attach :‬‬
‫• הוספה כפולה ונשנית של אותו אזור – נקבל מספר כתובות‪ ,‬אבל‬
‫כולן מתייחסות לאותו מידע‬
‫• הוספה עם הרשאות כתיבה‪/‬קריאה‪.‬‬
‫• אם התוכנית צריכה רק לקרוא – ראוי להשתמש באופציה של‬
‫‪) READ-ONLY‬במקרה של ‪ BUG‬בתוכנית שלנו – לא נקלקל‬
‫את הזיכרון המשותף לתהליכים אחרים(‬
‫©צבי מלמד‬
‫‪11‬‬
‫חיבור )‪ (attach‬התהליך לזכרון המשותף‬
‫;)‪void *shmat(int shmid, const void *shmaddr, int shmflg‬‬
‫•‬
‫•‬
‫•‬
‫‪ NULL‬בארגומנט השני מורה שניתן‬
‫לשלב את קטע הזכרון המשותף בכל‬
‫מקום פנוי במרחב הכתובות ‪ .‬זה הערך‬
‫המקובל מאוד לארגומנט‪) .‬בד"כ הוא‬
‫ישולב בין המחסנית לערמה‪(.‬‬
‫אפס בארגומנט השלישי מורה‬
‫שברצוננו‪ ,‬בעזרת המצביע הנוכחי גם‬
‫לקרוא וגם לכתוב על הזכרון המשותף‪.‬‬
‫אם נרצה רק לקרוא – נעביר את הדגל‬
‫‪) SHM_RDONLY‬אין אפשרות‬
‫מקבילה ל"כתיבה בלבד"(‬
‫; ‪char *shm_ptr‬‬
‫)* ‪shm_ptr = (char‬‬
‫‪shmat(shm_id,‬‬
‫‪NULL,‬‬
‫; )‪0‬‬
‫{ )‪if (shm_ptr == (char *) -1‬‬
‫;)"‪perror("shmat failed‬‬
‫;)‪exit(EXIT_FAILURE‬‬
‫}‬
‫©צבי מלמד‬
‫‪12‬‬
‫עוד על ‪shmat ...‬‬
‫• הפעולה מגדילה מונה פנימי )בדסקריפטור( שסופר את מספר ה‪-‬‬
‫‪) attachments‬שימושי לפעולת ה‪(delete-‬‬
‫• ערך מוחזר‪ :‬המצביע לזכרון – במקרה הצלחה‪ ,‬או ‪ -1‬במקרה של‬
‫כשלון‬
‫• שגיאות אפשריות‪:‬‬
‫– אין הרשאות‬
‫– קונפליקט בכתובות‬
‫– בעיה בהקצאת הזכרון‬
‫©צבי מלמד‬
‫‪13‬‬
‫עבודה רגילה עם המצביע לזכרון‬
.‫ לטפל בשטח הזיכרון כבכל מערך‬shm_ptr ‫ נוכל באמצעות‬,‫• עתה‬
:‫לדוגמה‬
char input_str[MAX_STR_LEN] ;
fgets(input_str, MAX_STR_LEN, stdin) ;
strcpy(shm_ptr, input_str) ;
:‫• או‬
for (i=0; i< 10; i++)
shm_ptr[i] = '?' ;
14
‫©צבי מלמד‬
‫סיום עבודה – ניתוק המצביע‬
‫•‬
‫•‬
‫•‬
‫•‬
‫•‬
‫בסיום העבודה עם הזכרון המשותף‪ ,‬ננתק את המצביע על ידי‬
‫קריאת המערכת ‪shmdt(shm_ptr) :‬‬
‫לא משחררים את שטח הזיכרון ‪ -‬אלא רק מנתקים את המצביע‬
‫הזה ממנו‪.‬‬
‫הפעולה הזאת מעדכנת מונה )מקטינה באחד(‬
‫‪ –shm_nattch‬חבר ב ‪shared-memory descriptor‬‬
‫ה‪ shared-memory -‬ישוחרר‪ ,‬רק לאחר ש‪:-‬‬
‫– הודענו על רצוננו לשחררו באמצעות קריאה ל‪shmctl -‬‬
‫– מספר המצביעים אליו ירד והגיע לאפס – כלומר‪ ,‬אחרי שכל‬
‫התהליכים שעושים בו שימוש הסתיימו )או שחררו את‬
‫המצביע(‬
‫©צבי מלמד‬
‫‪15‬‬
‫שיחרור הזכרון המשותף‬
‫•‬
‫•‬
‫•‬
‫כדי לשחרר את שטח הזיכרון יכול כל תהליך שיש לו הרשאת קריאה ‪ +‬כתיבה‬
‫על השטח )ולא רק מי שהקצה אותו( להגדיר משתנה עזר‪:‬‬
‫; ‪struct shmid_ds shm_desc‬‬
‫ולבצע‪:‬‬
‫)‪if (shmctl(shm_id, IPC_RMID, &shm_desc) == -1‬‬
‫{‬
‫; )"‪perror("shmctl failed‬‬
‫; )‪exit(EXIT_FAILURE‬‬
‫}‬
‫קטע זכרון משותף שלא שוחרר מסיבה כלשהי )למשל‪ :‬התהליך עף‪ (...‬ממשיך‬
‫להתקיים )ולגזול משאבי מערכת(‪.‬‬
‫– בפרט‪ ,‬ניסיון הקצאה שני )הרצה חוזרת של תוכניתנו( – ההקצאה תכשל‬
‫©צבי מלמד‬
‫‪16‬‬
‫עבודה מה‪shell -‬‬
‫• ‪ : ipcs -m‬מציג את משאבי הזכרונות המשותפים‬
‫• >‪ : ipcrm –m <shm-id‬משחררת את הזכרון המשותף הזה‬
‫©צבי מלמד‬
‫‪17‬‬
Shared-memory descriptor
struct shmid_ds {
struct ipc_perm shm_perm;
size_t shm_segsz;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
unsigned short shm_cpid;
unsigned short shm_lpid;
short shm_nattch;
...
};
18
‫©צבי מלמד‬
/*
/*
/*
/*
/*
/*
/*
/*
operation perms */
size of segment (bytes) */
last attach time */
last detach time */
last change time */
pid of creator */
pid of last operator */
no. of current attaches */
‫זכרון משותף תקלה אפשרית‬
‫• ניזכ ֵר כי הפעולה ‪) v++‬המגדילה את תא הזיכרון ‪ v‬באחד( למעשה‪ ,‬בד"כ‪ ,‬הופכת‬
‫לשלוש פקודות מכונה‪:‬‬
‫‪1. reg  v‬‬
‫‪2. reg++‬‬
‫‪3. v  reg‬‬
‫•‬
‫עתה נניח כי אנו מריצים שני תהליכים‪ ,‬החולקים זיכרון משותף בן שני בתים‪,‬‬
‫אליו הם פונים כאל מערך ‪ .a‬כל אחד מהתהליכים סופר‪ ,‬ומוסיף לזיכרון‬
‫המשותף‪ ,‬כמה אפסים וכמה אחדים הוא קרא )לתאים ‪ #1 ,#0‬במערך(‪.‬‬
‫{ )‪while ((c = getchar()) != EOF‬‬
‫)'‪if (c == '0' || c == '1‬‬
‫; ‪a[c – '0']++‬‬
‫}‬
‫©צבי מלמד‬
‫‪19‬‬
‫זכרון משותף תקלה אפשרית‬
‫• נניח התהליך הבא‪.‬‬
‫תהליך ב'‬
‫• ערכו של ]‪ a[0‬הוא ‪.17‬‬
‫• כל אחד משני התהליכים קרא‬
‫אפס נוסף‪ ,‬וברצונו על‪-‬כן להגדיל‬
‫את התא ‪ #0‬במערך;‬
‫• מערכת ההפעלה מריצה אותם‪,‬‬
‫כולל החלפת הקשר‪ ,‬באופן‬
‫המתואר ממול‪.‬‬
‫תהליך א'‬
‫]‪reg1 = a[0‬‬
‫]‪reg2 = a[0‬‬
‫‪reg1++‬‬
‫‪reg2++‬‬
‫• מה תהיה התוצאה?‬
‫‪a[0] = reg1‬‬
‫‪a[0] = reg2‬‬
‫©צבי מלמד‬
‫‪20‬‬
‫זכרון משותף תקלה אפשרית‬
‫•‬
‫אם כאמור נניח שערכו של ]‪a[0‬‬
‫הוא ‪.17‬‬
‫•‬
‫התוצאה בסיום‪a[0]18 :‬‬
‫•‬
‫ולא מה שהתכוונו שיקרה‪ ,‬והוא ש‪:‬‬
‫‪a[0]19‬‬
‫תהליך ב'‬
‫תהליך א'‬
‫]‪reg1 = a[0‬‬
‫]‪reg2 = a[0‬‬
‫‪reg1++‬‬
‫‪reg2++‬‬
‫‪a[0] = reg1‬‬
‫‪a[0] = reg2‬‬
‫©צבי מלמד‬
‫‪21‬‬
‫פתרונות אפשריים למניעה הדדית‬
‫הפתרון‪:‬‬
‫שימוש בסמפור או במנעולים כלליים‪ ,‬כפי שנכיר בסמסטר הבא‬
‫‪.I‬‬
‫או‪:‬‬
‫‪.II‬‬
‫בנעילת קטע הזכרון המשותף באמצעות מנעול יעודי כפי‬
‫שלינוקס וסולאריס מאפשרות לנו לעשות )באופן שחורג‬
‫מהסטנדרט‪.‬‬
‫–‬
‫בלינוקס רק החל מגרסה ‪ 2.6.10‬כל משתמש יכול לבצע‬
‫פעולה זאת )בעבר רק משתמש מיוחס יכול היה לבצעה(‬
‫©צבי מלמד‬
‫‪22‬‬
shmctl() ‫מניעה הדדית באמצעות‬
while ((c = getchar()) != EOF) {
‫ עד‬.‫אנו מבקשים לנעול את הז"מ‬
if (c == '0' || c == '1')
‫שהמנעול לא יינתן לנו אנו‬
{
‫תקועים\מושהים‬
if (shmctl(shm_id,
SHM_LOCK,
&shm_desc) == -1) {
perror("shmctl(lock) failed");
‫ רק אנו מטפלים‬,‫כאן‬
exit(EXIT_FAILURE) ;
‫בז"מ באופן בלבדי‬
}
a[ c - '0']++ ;
if (shmctl(shm_id,
‫שחרור הז"מ שנעלנו‬
SHM_UNLOCK,
&shm_desc) == -1) {
perror("shmctl(unlock) failed") ;
exit(EXIT_FAILURE) ;
}
}
}
‫©צבי מלמד‬
23
‫דוגמה‪ :‬יצרן‪-‬צרכן של מחרוזות‬
‫•‬
‫תכנית א' קוראת מחרוזות מ‪ stdin-‬ו‪'-‬מייצרת' אותן – כלומר כותבת לז"מ‪.‬‬
‫–‬
‫•‬
‫תכנית ב' 'צורכת' את המחרוזות שייצרה תכנית א'‪.‬‬
‫–‬
‫•‬
‫•‬
‫ייתכן שמספר תהליכים המריצים תכנית זאת ירוצו במקביל‬
‫)כנ"ל(‬
‫כדי לסנכרן את הגישה שלהן לז"מ הן משתמשות בתא ‪ #0‬בקטע הזיכרון‪:‬‬
‫–‬
‫סימן מינוס‪ :‬אין מחרוזת בזיכרון‪.‬‬
‫–‬
‫סימן פלוס‪ :‬יש מחרוזת בזיכרון‪.‬‬
‫הערה‪:‬‬
‫–‬
‫בתכנית יש ליקויים מבחינת סנכרון הגישה לזיכרון המשותף‪ ,‬נושא עליו נדון‬
‫בפרק ‪ ,#7‬אך היא תקינה מהבחינה הטכנית ולכן תספק את צרכינו הנוכחיים‪.‬‬
‫©צבי מלמד‬
‫‪24‬‬
(‫צרכן של מחרוזות‬-‫ "היצרן" )יצרן‬:‫דוגמה‬
/* shm_create_n_produce.c
* A program that allocates a block of shared memory,
* then repeatedly 'produces' strings (it reads from stdin)
* to the shm.
* A second program: a 'consumer' consumes
* these strings from the shm.
* (shm_consumer.c)
*
* The programs do not utilize the random access
* property of a shm
*/
25
‫©צבי מלמד‬
(‫צרכן של מחרוזות‬-‫ "היצרן" )יצרן‬:‫דוגמה‬
#include
#include
#include
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<string.h>
<unistd.h>
<sys/types.h>
<sys/ipc.h>
<sys/shm.h>
// for exit()
// for strcpy(), strcmp()
// for sleep()
#define MAX_STR_LEN
100
#define SHM_SIZE
MAX_STR_LEN + 1
//shm_ptr[0] holds whether the shm
//is empty (-) on full (+)
#define END_STRING
"END"
26
‫©צבי מלמד‬
(‫צרכן של מחרוזות‬-‫ "היצרן" )יצרן‬:‫דוגמה‬
int main() {
key_t key ;
int shm_id ;
‫המפתח החיצוני והפנימי לז"מ‬
‫המצביע בעזרתו נפנה לז"מ‬
char *shm_ptr ;
char input_string[MAX_STR_LEN] ;
‫מבנה לצורך שחרור הז"מ‬
struct shmid_ds shm_desc ;
// create a key for the shm
key = ftok("/tmp", 'y') ;
if (key == -1) {
perror("ftok failed: ") ;
exit(EXIT_FAILURE) ;
}
27
‫©צבי מלמד‬
(‫צרכן של מחרוזות‬-‫ "היצרן" )יצרן‬:‫דוגמה‬
if ((shm_id = shmget
(key,
SHM_SIZE,
IPC_CREAT
| IPC_EXCL
| 0600))== -1)
{
‫נוריד את המרכיב‬
‫הזה אם כמה‬
‫תהליכים עשויים‬
‫לנסות להקצות את‬
‫הז"מ‬
perror("shmget failed: ") ;
exit(EXIT_FAILURE) ;
}
shm_ptr = (char *) shmat(shm_id, NULL, 0) ;
if (!shm_ptr) {
perror("shmat failed: ") ;
exit(EXIT_FAILURE) ;
}
28
‫©צבי מלמד‬
(‫צרכן של מחרוזות‬-‫ "היצרן" )יצרן‬:‫דוגמה‬
shm_ptr[0] = '-' ;
// signals that shm is empty
puts("Now, (and only now!) reader can start");
printf("Enter a series of strings to be written\
on the shm.\n\
Enter %s to finish\n", END_STRING) ;
‫המתנה עסוקה‬
for( ; ; ) {
scanf(" %s", input_string) ;
while (shm_ptr[0] == '+') // while shm
// is not 'empty'
sleep(1) ;
if (shmctl(shm_id, SHM_LOCK, &shm_desc) == -1) {
perror("shmctl LOCK failed: ") ;
exit(EXIT_FAILURE) ;
}
29
‫©צבי מלמד‬
(‫צרכן של מחרוזות‬-‫ "היצרן" )יצרן‬:‫דוגמה‬
strcpy(shm_ptr +1, input_string) ;
shm_ptr[0] = '+' ;
// signals that shm is not empty
if (shmctl(shm_id, SHM_UNLOCK, &shm_desc) == -1
{
perror("shmctl UNLOCK failed: ") ;
exit(EXIT_FAILURE) ;
}
if (strcmp(input_string, END_STRING) == 0) {
if (shmctl(shm_id, IPC_RMID, &shm_desc) == -1)
{
perror("shmctl IPC_RMID failed: ") ;
exit(EXIT_FAILURE) ;
}
return( EXIT_SUCCESS ) ;
}
} // for (; ; )
return( EXIT_SUCCESS ) ;
}
30
‫©צבי מלמד‬
(‫צרכן של מחרוזות‬-‫ "הצרכן" )יצרן‬:‫דוגמה‬
// file: shm_consumer.c
// See documentation in: shm_create_n_produce.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define MAX_STR_LEN
100
#define SHM_SIZE
MAX_STR_LEN +1
#define END_STRING
"END"
//----------------------------------------------
31
‫©צבי מלמד‬
(‫צרכן של מחרוזות‬-‫ "הצרכן" )יצרן‬:‫דוגמה‬
int main() {
key_t key ;
int shm_id ;
char *shm_ptr ;
char output_string[MAX_STR_LEN] ;
struct shmid_ds shm_desc ;
// create THE SAME key for the shm as the producer
key = ftok("/tmp", 'y') ;
if (key == -1) {
perror("ftok failed: ") ;
exit(EXIT_FAILURE) ;
}
32
‫©צבי מלמד‬
(‫צרכן של מחרוזות‬-‫ "הצרכן" )יצרן‬:‫דוגמה‬
// get the id of the block of memory
// that was, hopefully, already created by the producer
if ((shm_id = shmget(key,
SHM_SIZE,
// OR: 0
0600)) == -1) {
perror("shmget failed: ") ;
exit(EXIT_FAILURE) ;
}
shm_ptr = (char *) shmat(shm_id, NULL, 0) ;
if (!shm_ptr) {
perror("shmat failed: ") ;
exit(EXIT_FAILURE) ;
}
for( ; ; ) {
.............
33
‫©צבי מלמד‬
(‫צרכן של מחרוזות‬-‫ "הצרכן" )יצרן‬:‫דוגמה‬
for( ; ; ) {
while (shm_ptr[0] == '-')
sleep(1) ;
// lock the shm before you operate on it
if (shmctl(shm_id, SHM_LOCK, &shm_desc) == -1) {
perror("shmctl LOCK failed: ") ;
exit(EXIT_FAILURE) ;
}
strcpy(output_string, shm_ptr +1) ;
shm_ptr[0] = '-' ;
// the shm is empty
if (shmctl(shm_id, SHM_UNLOCK, &shm_desc) == -1)
{
perror("shmctl UNLOCK failed: ") ;
exit(EXIT_FAILURE) ;
}
printf("Got: %s from the shm\n", output_string) ;
34
‫©צבי מלמד‬
(‫צרכן של מחרוזות‬-‫ "הצרכן" )יצרן‬:‫דוגמה‬
printf("Got: %s from the shm\n", output_string) ;
if (strcmp(output_string, END_STRING) == 0) {
if (shmctl(shm_id, IPC_RMID, &shm_desc) == -1) {
perror("shmctl IPC_RMID failed: ") ;
exit(EXIT_FAILURE) ;
}
return( EXIT_SUCCESS ) ;
}
} // for( ; ; )
return( EXIT_SUCCESS ) ;
}
35
‫©צבי מלמד‬
‫זכרון משותף – הערות נוספות‬
‫•‬
‫גודלו של שטח זכרון משותף שהוקצה אינו ניתן לשינוי‬
‫–‬
‫•‬
‫שלא כמו זיכרון )פרטי( שהוקצה דינאמית ע"י )(‪malloc‬‬
‫וגודלו ניתן לשנותו ע"י )(‪. ,realloc‬‬
‫ניתן לטפל במערך של מבנים על ידי שמגדירים‪:‬‬
‫} … { ‪struct S‬‬
‫; ‪struct S *ptr‬‬
‫; )כרגיל(‪ptr = (struct S *) shmatt‬‬
‫‪........‬‬
‫= … ‪ptr[0].‬‬
‫©צבי מלמד‬
‫‪36‬‬
‫זכרון משותף – הערות נוספות‬
‫•‬
‫במידה ואנו מחזיקים מבנה נתונים כלשהו בז"מ יש להקפיד‬
‫להחזיק את כל המידע אודותיו בז"מ )לדוגמה‪ :‬כמה תאים כולל‬
‫המערך‪ ,‬וכמה נמצאים כרגע בשימוש(‪.‬‬
‫•‬
‫מספר שלם בן ארבעה בתים יש לשמור בכתובת שהינה כפולה של‬
‫ארבע‪ .‬בד"כ הקומפיילר דואג לכך עבורנו‪ .‬עת אנו מקצים ז"מ‪,‬‬
‫ורוצים לאחסן בו נתונים )בפרט מבנים הכוללים חברים‬
‫מטיפוסים שונים(‪ ,‬חובת הדאגה לכך עוברת אלינו )אם לא נקפיד‬
‫על כך ישלח לתהליך הסיגנל ‪.(SIGBUS‬‬
‫©צבי מלמד‬
‫‪37‬‬