#4 שבוע 3,# שבוע תהליכים : `א קורס מערכות הפעלה תכנות מערכת ומבוא
Transcription
#4 שבוע 3,# שבוע תהליכים : `א קורס מערכות הפעלה תכנות מערכת ומבוא
שבוע ,#3שבוע 4# תהליכים קורס מערכות הפעלה א': תכנות מערכת ומבוא לתכנות מקבילי מכללת הדסה -מכללה חרדית צבי מלמד [email protected] הרצאות הקורס מבוססות במידה רבה ביותר על ההרצאות של ד"ר יורם ביברמן © כל הזכויות שמורות לד"ר יורם ביברמן ולצבי מלמד ©צבי מלמד 1 מערכות הפעלה – תכנות מערכת המשך הרצאה בנושא תהליכים יום א' 27.11.11 ©צבי מלמד 60 שכפול חוצצים:fork היבט של (fork_buffer.c – part 1) // // // // // // // // // file: fork_buffer.c A small program that fork()s. Before the fork(), the father prints "I am alone" as the output is buffered, it is not sent to the screen. When the child is born, and gets a copy of the address space of the father, it also gets this string. Therefore, when both send output (and break a line) this string occurs twice in the output (see bellow) #include #include #include #include 61 <stdio.h> <stdlib.h> // for exit() <sys/types.h> <unistd.h> // for fork() ©צבי מלמד שכפול חוצצים:fork היבט של (fork_buffer.c – part 2) int main() { pid_t status; printf("I am alone ") ; status = fork() ; if (status < 0) { perror("cannot fork") ; exit(EXIT_FAILURE) ; } if (status > 0) puts("hello from father\n") ; else puts("hello from son\n") ; return EXIT_SUCCESS ; } 62 ©צבי מלמד הפלט של האב נשמר ועדיין אינו נשלח,בחוצץ std-out ל fork()-הקריאה ל משכפלת גם את החוצץ כאן החוצץ נפלט גם כאן החוצץ נפלט שכפול חוצצים:fork היבט של תוצאות הריצה /* a run: ====== <212|0>yoramb@inferno-05:~/os$ !a a.out I am alone hello from son I am alone hello from father <213|0>yoramb@inferno-05:~/os$ */ 63 ©צבי מלמד שכפול חוצצים:fork היבט של תיקונים אפשריים int main() { int main() { pid_t status ; pid_t status ; printf("I am alone \n") ; printf("I am alone ") ; fflush(stdout); fflush(stdout); status = fork() ; if (status > 0) puts("hello from father\n") ; else puts("hello from son\n") ; return EXIT_SUCCESS ; } 64 ©צבי מלמד היבט של fork יצירת סדרה פסאודו-אקראית על ידי שימוש בsrand() + rand()- ©צבי מלמד 65 (fork_n_wait_a.c) A הקובץ גירסה int main() { pid_t status ; int i ; srand(unsigned) time(NULL)) מודגשות רק השורות for (i = 0; i< 3; i++) { שהשתנו ביחס לקובץ status = fork() ; הקודם if (status < 0) { perror("Cannot fork()") ; (fork_n_wait.c) exit(EXIT_FAILURE) ; } if (status == 0) { do_child() ; exit(getpid()%3) ; . . . . . . . . . . . . . . . . . . . . . . . . . void do_child() { int r; sleep( r=(rand() % 10)) ; // seconds printf("%d: I've just slept %d seconds\n", getpid(), r); } 66 ©צבי מלמד הרצת הקובץ גירסה (fork_n_wait_a.c) A נראה ששלושת התהליכים-בנים שנוצרו הולכים לישון )(sleep אותו משך זמן "אקראי" הסיבה :שלושתם "ירשו" אותו "גרעין" seedשל הסדרה האקראית – בגלל שכפול מרחב הזיכרון! ©צבי מלמד 67 (fork_n_wait_b.c) B הקובץ גירסה int main() { pid_t status ; int i ; //srand(unsigned) time(NULL)) for (i = 0; i< 3; i++) { status = fork() ; if (status < 0) { perror("Cannot fork()") ; exit(EXIT_FAILURE) ; } if (status == 0) { do_child() ; exit(getpid()%3) ; . . . . . . . . . . . . . . . . . . . . . . . . . void do_child() { int r; srand(unsigned) time(NULL)); sleep( r=(rand() % 10)) ; // seconds printf("%d: I've just slept %d seconds\n", getpid(), r); } 68 ©צבי מלמד מודגשות רק השורות שהשתנו ביחס לקובץ הקודם (fork_n_wait.c) הרצת הקובץ גירסה (fork_n_wait_b.c) B נראה שהבעיה לא באמת נפתרה ...שלושת הילדים עדיין ישנים ) (sleepאותו משך זמן "אקראי" , למרות שseed(time())- מתבצע בנפרד בכל תהליך הסיבה :הכל קורה כך כך מהר – באותה שניה .ברמה של )() timeשניות( כולם מקבלים אותו ערך. ©צבי מלמד 69 הרצת הקובץ גירסה (fork_n_wait_c.c) C הפתרון :כל אחד מהבנים מייצר Seedשונה באמת ,על ידי שימוש בgetpid() - ©צבי מלמד 70 קריאת מערכת )(Exec • בדוגמות שראינו עד כה הילד הריץ קוד שנכלל במרחב הכתובות )בתכנית( של ההורה • כל הפונקציות שהילד מבצע חשופות גם להורה – לדוגמא ההורה יכול לקרוא לפונקציה )(do_child • מבחינת הביצוע ,ובפרט ערכי המשתנים ,אין קשר בין הרצת הפונקציה על-ידי ההורה וע"י הילד — הם שני תהליכים נפרדים • לעתים נרצה שהילד 'יעבור מוטציה' – כלומר ,יריץ תכנית שונה לגמרי מזו שמריץ ההורה. ©צבי מלמד 71 ( )המשךExec() קריאת מערכת ממירה,• נניח כי כתבנו את התכנית הבאה )המקבלת שתי מחרוזות : my_cmp וקובץ ההרצה הוא,( ומשווה ביניהם,אותן למספרים int main(int argc, char **argv) { if (argc != 3) { fputs("usage: my_cmp <arg1> <arg2>\n", stderr) ; exit(EXIT_FAILURE) ; } if (atoi(argv[1]) == atoi(argv[2])) puts("equals as ints\n") ; else puts("different as ints\n") ; return EXIT_SUCCESS ; } 72 ©צבי מלמד קריאת מערכת )() Execהמשך( • נניח שתהליך 123מריץ תוכנית שבה הגדרנו: ; }char *args[] = {"my_cmp", "017", "17", NULL .......... ; )execvp("my_cmp", args • מה יקרה כתוצאה מהקריאה ? execvp • התהליך ) 123הוא זה שביצע את הקריאה( 'יעבור מוטציה' ,כלומר לתוך מרחב הכתובות שלו נטענת התוכנית השוכנת בקובץ .my_cmpלתכנית זאת מועברים כארגומנטים המחרוזות השוכנות במערך .args • הדבר הזה קורה בלי תלות ובלי קשר ל ַאופן שבו נוצר התהליך 123 ©צבי מלמד 73 (1) – תוכנית דוגמאexec() :• נתבונן בתוכנית הבאה #include <stdio.h> #include <stdlib.h> // for exit() #include <string.h> #include <sys/types.h> #include <unistd.h> // for sleep(), execvp() #include <sys/wait.h> // for wait() void do_child(int) ; 74 ©צבי מלמד (2) – תוכנית דוגמאexec() int main() { pid_t status ; int i ; for (i= 0; i < 2; i++) { status = fork() ; if (status < 0) { fputs("error in fork", stderr) ; exit(EXIT_FAILURE) ; } else if (status == 0) { do_child(i) ; fputs("we are not supposed to b here", stderr); } } return EXIT_SUCCESS ; } 75 ©צבי מלמד – תוכניתexec() (3) דוגמא void do_child(int n) { #define MAX_STR_LEN 20 char s1[MAX_STR_LEN], s2[MAX_STR_LEN] ; if (n == 0) { char *args[] = {"my_cmp", "017", "17", NULL} ; if (execvp("my_cmp", args) != 0) { perror("execvp() failed") ; exit(EXIT_FAILURE) ; } (וריאנט שני )מתוך שישה } :exec() של strcpy(s1, "13") ; strcpy(s2, "12") ; if (execlp("my_cmp", "my_cmp", s1, s2, NULL) != 0) { perror("execlp() failed") ; exit(EXIT_FAILURE) ; } } 76 ©צבי מלמד :Execהרצת התוכנית a.out my_cmp ©צבי מלמד 77