#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‬‬