Περιβάλλον, διαχείριση και ιεραρχίες διεργασιών

Slides:



Advertisements
Παρόμοιες παρουσιάσεις
Λειτουργικό Σύστημα (Operating System) 1o μέρος
Advertisements

Το αλφαριθμητικό (string)
7.3.8 Μεταφραστές Ελληνογαλλική Σχολή Καλαμαρί - Τίκβα Χριστίνα.
ΙΩΑΝΝΗΣ ΚΩΝΣΤΑΝΤΙΝΟΥ 2ο ΦΡΟΝΤΙΣΤΗΡΙΟ ΠΑΡΑΣΚΕΥΗ 26 ΟΚΤΩΒΡΙΟΥ 2012 ΑΙΘΟΥΣΑ Β4 11.
Στοιχειώδεις Δομές Δεδομένων TexPoint fonts used in EMF. Read the TexPoint manual before you delete this box.: AA A A A Τύποι δεδομένων στη Java • Ακέραιοι.
ΙΩΑΝΝΗΣ ΚΩΝΣΤΑΝΤΙΝΟΥ 1ο ΦΡΟΝΤΙΣΤΗΡΙΟ ΠΑΡΑΣΚΕΥΗ 5 ΟΚΤΩΒΡΙΟΥ 2012 ΑΙΘΟΥΣΑ Β4 11.
Λειτουργικό Σύστημα 2ο μέρος.
ΜΑΘΗΜΑ 7ο Κυκλικές και Διπλά Συνδεδεμένες Λίστες,
ΕΠΛ 231 – Δομές Δεδομένων και Αλγόριθμοι
Κεφάλαιο 6 Υλοποίηση Γλωσσών Προγραμματισμού
Αναδρομη και static Γραψετε την συναρτηση sequence_size που διαβαζει μια απροσδιοριστου μεγεθους σειρας και υπολογιζει και τυπωνει το μεγεθος της. int.
Message Passing Interface (MPI)
ΤΕΧΝΙΚΕΣ Αντικειμενοστραφουσ προγραμματισμου
ΕΙΣΑΓΩΓΗ ΣΤΟ ΔΙΑΔΙΚΑΣΤΙΚΟ ΠΡΟΓΡΑΜMΑΤΙΣΜΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ – ΠΟΛΥΤΕΧΝΙΚΗ ΣΧΟΛΗ ΤΜΗΜΑ ΜΗΧΑΝΙΚΩΝ Η/Υ ΚΑΙ ΠΛΗΡΟΦΟΡΙΚΗΣ.
ΜΑΘ-3122/106 Προγραμματισμός
ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ Πίνακες Κλάσεις και Αντικείμενα.
Φροντηστήριο Διαχείριση Περιεχομένου Παγκόσμιου Ιστού και Γλωσσικά Eργαλεία.
24/11/2003Message Passing Interface (MPI)1 Αθήνα, Νοέμβριος 2003 Συστήματα Παράλληλης Επεξεργασίας Εργαστήριο Υπολογιστικών Συστημάτων.
22/11/2004Message Passing Interface (MPI)1 Αθήνα, Νοέμβριος 2004 Συστήματα Παράλληλης Επεξεργασίας Εργαστήριο Υπολογιστικών Συστημάτων.
Databases & Qt Μανούσης Πέτρος ΑΜ: 862
ΙΩΑΝΝΗΣ ΚΩΝΣΤΑΝΤΙΝΟΥ 2ο ΦΡΟΝΤΙΣΤΗΡΙΟ ΠΑΡΑΣΚΕΥΗ 11 ΟΚΤΩΒΡΙΟΥ 2012 ΑΙΘΟΥΣΑ Β4 1.
ΙΩΑΝΝΗΣ ΚΩΝΣΤΑΝΤΙΝΟΥ 3ο ΦΡΟΝΤΙΣΤΗΡΙΟ ΠΑΡΑΣΚΕΥΗ 19 ΟΚΤΩΒΡΙΟΥ 2012 ΑΙΘΟΥΣΑ Β4 1.
Νήματα Οι διεργασίες έχουν τα παρακάτω συστατικά:
Εργασία Η υλοποίηση του αλγορίθμου συγχώνευσης θα πρέπει να χρησιμοποιεί την ιδέα των ροών (streams). Θα πρέπει να υπάρχουν δύο διαφορετικά είδη.
ΘΠ06 - Μεταγλωττιστές Πίνακας Συμβόλων. Πίνακας Συμβόλων (Symbol Table) (Ι)  Είναι μια δομή στην οποία αποθηκεύονται τα ονόματα ενός προγράμματος και.
Η ΓΛΩΣΣΑ C ΜΑΘΗΜΑ 2.
Υποθετικός τελεστής Ο υποθετικός τελεστής (?:) αποτελείται από δύο σύμβολα. Ανήκει στην κατηγορία των τελεστών που αποτελούνται από συνδυασμό συμβόλων.
Τι είναι διεργασία Ένα πρόγραμμα σε εκτέλεση Η διεργασία περιλαμβάνει:
Διαχείριση μνήμης Υπόβαθρο Εναλλαγή Συνεχής κατανομή Σελιδοποίηση
ΣΥΝΑΡΤΗΣΕΙΣ.
Message Passing Interface (MPI) Συστήματα Παράλληλης Επεξεργασίας Εργαστήριο Υπολογιστικών Συστημάτων Αθήνα, Δεκέμβριος 2002.
ΗΥ 150 – ΠρογραμματισμόςΞενοφών Ζαμ π ούλης ΗΥ-150 Προγραμματισμός Δυναμική Διαχείριση Μνήμης (1/2)
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Αρχεία.
ΕΠΛ 223 Θεωρία και Πρακτική Μεταγλωττιστών7-1 Πίνακας Συμβόλων Πίνακας συμβόλων: δομή δεδομένων που χρησιμοποιείται για την αποθήκευση διαφόρων πληροφοριών.
1 ΗΥ-340 Γλώσσες και Μεταφραστές Φροντιστήριο Πίνακας Συμβόλων Symbol Table.
Επικοινωνία Ανθρώπου Μηχανής HTML CGI JAVASCRIPT Κουμπούλης Χρήστος Α.Μ. 921 Χαλαβαζής Βασίλης Α.Μ. 988.
Κεφάλαιο 10 – Υποπρογράμματα
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Αναδρομή (1/2)
Διεργασίες Λειτουργικά Συστήματα. Λειτουργικά Συστήματα/ Slide 2 Η Έννοια της Διεργασίας * Διεργασία (Process) – ο μηχανισμός εκτέλεσης ενός προγράμματος.
ΛΟΓ102: Τεχνολογία Λογισμικού Ι Διδάσκων: Νίκος Παπασπύρου 1Νίκος ΠαπασπύρουΛΟΓ102:
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Συναρτήσεις.
Threads Στον παραδοσιακό προγραμματισμό όταν ένα πρόγραμμα εκτελείται ονομάζεται process (διεργασία) και οι εντολές του εκτελούνται σειριακά η μία μετά.
ΗΥ 150 – Προγραμματισμός Ξενοφών Ζαμπούλης ΗΥ -150 Προγραμματισμός Αρχεία.
ΗΥ150 – ΠρογραμματισμόςΞ. Ζαμπούλης ΗΥ-150 Προγραμματισμός Αρχεία.
ΗΥ-340 Γλώσσες και Μεταφραστές Φροντιστήριο Syntax Directed Translation and alpha Language.
ΛΟΓ201: Τεχνολογία Λογισμικού ΙΙ Διδάσκων: Νίκος Παπασπύρου 1Νίκος ΠαπασπύρουΛΟΓ201:
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Δυναμική Διαχείριση Μνήμης (1/2)
Διεργασίες.
ΗΥ150 – ΠρογραμματισμόςΞενοφών Ζαμπούλης ΗΥ-150 Προγραμματισμός Αναδρομή (1/2)
ΠΡΟΓΡΑΜΜΑΤΙΣΤΙΚΕΣ ΤΕΧΝΙΚΕΣ Διδάσκοντες:Γιάννης Μαΐστρος Στάθης Ζάχος Νίκος Παπασπύρου
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Συναρτήσεις (μέρος δεύτερο) και Μεταβλητές.
ΗΥ150 – ΠρογραμματισμόςΚώστας Παναγιωτάκης ΗΥ-150 Προγραμματισμός Τύποι Μεταβλητών Τελεστές Βασική Είσοδος/Έξοδος.
Νήματα με την χρήση των Posix Threads (pthreads)‏.
ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ Κλάσεις και Αντικείμενα.
ΠΑΡΑΔΕΙΓΜΑ: ΤΑ ΕΠΙΠΕΔΑ ΥΛΙΚΟΥ – ΛΟΓΙΣΜΙΚΟΥ ΣΕ ΕΝΑΝ ΥΠΟΛΟΓΙΣΤΗ.
Γλώσσες Προγραμματισμού Μεταγλωττιστές Πίνακας Συμβόλων Πανεπιστήμιο Μακεδονίας Τμήμα Εφαρμοσμένης Πληροφορικής Ηλίας Σακελλαρίου.
ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ Ι
Δυναμικός Κατακερματισμός
Κατανεμημένα Συστήματα
Δείκτες Προγραμματισμός Ι
ΣΥΝΑΡΤΗΣΕΙΣ (Functions)
Πίνακας Συμβόλων Διαλέξεις στο μάθημα: Μεταφραστές Γιώργος Μανής.
Εισαγωγή στον Προγ/μό Υπολογιστών
Κεφάλαιο 10 Streams.
Processes.
ΗΥ-150 Προγραμματισμός Αναδρομή (1/2).
Προγραμματισμός ΗΥ Ενότητα 12: Αρχεία Δομών. Διδάσκων: Ηλίας Κ Σάββας,
ΤΕΧΝΙΚΕΣ Αντικειμενοστραφουσ προγραμματισμου
Δυναμικός Κατακερματισμός
ΕΚΦΡΑΣΕΙΣ, ΑΝΑΜΟΝΕΣ (DELAYS), ΗΧΟΙ
Μεταγράφημα παρουσίασης:

Περιβάλλον, διαχείριση και ιεραρχίες διεργασιών Διεργασίες Περιβάλλον, διαχείριση και ιεραρχίες διεργασιών

Η συνάρτηση main Κάθε πρόγραμμα ξεκινά από τη συνάρτηση main int main(int argc, char *argv[]) Στην πραγματικότητα, αρχικά ο πυρήνας καλεί μία ειδική ρουτίνα εκκίνησης που καθορίζεται από τον συνδέτη (συνήθως περιέχεται στο c0.o cstart.o κ.λπ.) Η ρουτίνα εκκίνησης διευθετεί τις παραμέτρους που λαμβάνει από τον πυρήνα ώστε η main να καλείται ως ανωτέρω

Τερματισμός διεργασιών Μία διεργασία τερματίζεται με 5 τρόπους: Κανονικός τερματισμός εκτέλεση return από τη main εκτέλεση συνάρτησης exit() εκτέλεση κλήσης συστήματος _exit() Απότομος τερματισμός κλήση της συνάρτησης abort() τερματισμός λόγω παραλαβής σήματος Η τιμή που επιστρέφεται από τη main προωθείται στην γονική διεργασία (φλοιός, λειτουργικό σύστημα κ.λπ.) συνήθως μέσω της exit exit(main(argc, argv));

Οι συναρτήσεις exit και _exit Πρότυπα: #include <stdlib.h> void exit(int status); void _exit(int status); H exit «συμμαζεύει» το περιβάλλον της βιβλιοθήκης της C εγγραφή των ενδιάμεσων μνημών των αρχείων (FILE *) και κλείσιμό τους κλήση των συναρτήσεων εξόδου που έχει καταχωρήσει ο χρήστης με τη συνάρτηση atexit void atexit(void (*func)(void));

Οι συναρτήσεις exit και _exit [atexit.c] #include <stdio.h> #include <stdlib.h> void atexit1(void) { puts("Bye-Bye cruel world");} void atexit2(void) { puts("See you on the other side");} int main(int argc, char *argv[]) { FILE *testFile; if (atexit(atexit1) != 0) perror("Cannot register atexit1"); if (atexit(atexit2) != 0) perror("Cannot register atexit2"); if (atexit(atexit2) != 0) perror("Cannot re-register atexit2"); if ((testFile = fopen("testFile", "w")) == NULL) { perror("Can't open testfile"); exit(1); } fputs("A line in testFile, yeah, yeah", testFile); if ((argc > 1) && (argv[1][0] == '1')) _exit(0); else exit(0);

Τερματισμός διεργασιών Συναρτήσεις χρήστη χειριστής εξόδου (atexit1) exit κλήση κλήση επιστροφή επιστροφή συνάρτηση main exit κλήση exit χειριστής εξόδου (atexit2) _exit επιστροφή κλήση επιστροφή κλήση exit επιστροφή Διαδικασία εκκίνησης C _exit «συμμάζεμα» εισόδου-εξόδου _exit exec Πυρήνας

Μεταβλητές περιβάλλοντος Δύο τρόποι για πρόσβαση: extern char **environ; int main(int argc, char *argv[], char *envp[]); Η δεύτερη προσέγγιση, αν και γενικά λειτουργεί, παραβιάζει τις προδιαγραφές της ANSI C – προτιμήστε την 1η Για ορισμό νέας μεταβλητής (και αλλαγή): int putenv(char *definition); Για πρόσβαση σε μία μεταβλητή: char *getenv(char *name); Κάποια συστήματα υποστηρίζουν την unsetenv(char *name); environ Λίστα μεταβλητών NULL ΗΟΜΕ=/users/usr1 PATH=/usr/bin:/bin:/usr/local/bin SHELL=/bin/tcsh USER=usr1

Μεταβλητές περιβάλλοντος [environ.c] #include <stdio.h> #include <string.h> #include <stdlib.h> int main(void) { extern char **environ; int count; char *value; if (putenv("A_NEW_VAR=the chosen value") != 0) fprintf(stderr, "Cannot register variable\n"); for (count = 0; environ[count] != NULL; count++) puts(environ[count]); value = getenv("MYVAR"); if (value == NULL) printf("\nMYVAR undefined\n"); else printf("\nMYVAR value = %s\n", value); return 0; }

Η διευθέτηση της μνήμης στα προγράμματα C Τμήμα κώδικα (text segment) Οι εντολές που εκτελεί η ΚΜΕ. Μπορεί να διαμοιράζεται ανάμεσα σε διεργασίες. Συνήθως είναι ανάγνωσης μόνο Τμήμα αρχικοποιημένων δεδομένων (initialised data segment) Καθολικές μεταβλητές με αρχική τιμή π.χ. int maxentries = 8000; static char sccsid[] = "@(#) mod_ssl/" MOD_SSL_VERSION ">"; Τμήμα μη αρχικοποιημένων δεδομένων (uninitialised data segment) Καθολικές μεταβλητές χωρίς αρχική τιμή π.χ. int lastBlock; double measurements[1000][1000]; Ο πυρήνας γράφει μηδενικά στο τμήμα αυτό πριν ξεκινήσει η εκτέλεση του προγράμματος Στοίβα Τοπικές μεταβλητές, παράμετροι συναρτήσεων και διευθύνσεις επιστροφής Σωρός Για δυναμική διαχείριση μνήμης (brk, sbrk, malloc, calloc κ.λπ.)

Τυπική διευθέτηση μνήμης Παράμετροι γραμμής εντολών & μεταβλητές περιβάλλοντος στοίβα σωρός Μη αρχ. Δεδομένα (bss) Αρχ. δεδομένα Κώδικας Ο πυρήνας γράφει μηδενικά Η exec τα διαβάζει από το αρχείο του προγράμματος

Διαχείριση μνήμης Συναρτήσεις βιβλιοθήκης (stdlib.h): void *malloc(size_t size); Δεσμεύει size bytes από τον σωρό και επιστρέφει ένα δείκτη σ’ αυτά, ή NULL αν αποτύχει. Οι τιμές των bytes είναι τυχαίες void *calloc(size_t nobjs, size_t size); Δεσμεύει από τον σωρό χώρο για αποθήκευση nobjs αντικειμένων μεγέθους size το καθένα και επιστρέφει ένα δείκτη σ’ αυτά, ή NULL αν αποτύχει. Τα δεσμευμένα bytes τίθενται σε 0. void *realloc(void *ptr, size_t newSize); Αλλάζει το μέγεθος του ήδη δεσμευμένου χώρου που δείχνει ο ptr σε newSize και επιστρέφει έναν δείκτη στον χώρο με μέγεθος newSize ή NULL, αν αποτύχει. Η επιστρφόμενη τιμή μπορεί να είναι διαφορετική από το ptr, αν ζητάμε επέκταση και δεν υπάρχει χώρος στο τρέχον σημείο. Τα min(currentSize, newSize) πρώτα bytes διατηρούν την τιμή τους void free(void *ptr); Αποδεσμεύει τον χώρο που δείχνεται από τον ptr, ο οποίος ΠΡΕΠΕΙ να έχει δεσμευτεί από την malloc/calloc/realloc

Διαχείριση μνήμης 2048 32768 Αρχικός σωρός p1 = calloc(4, 512)  2048 p2 = malloc(8192)  4096 p3 = realloc(p1, 4096)  12288 p4 = realloc(p3, 8192)  12288 p5 = realloc(p2, 4096)  4096 free(p5); p6 = malloc(80000)  ;;; Όταν ο διαθέσιμος σωρός εξαντληθεί, η βιβλιοθήκη εκτελεί την κλήση συστήματος sbrk (ή brk) για να ζητήσει περισσότερο σωρό Αν το αίτημα δεν ικανοποιηθεί, η malloc/calloc/realloc αποτυγχάνει

Διαχείριση μνήμης - Σημειώσεις Σφάλματα στη διαχείριση μνήμης (π.χ. αποδέσμευση χώρου που δεν δεσμεύτηκε, χρήση περισσοτέρων bytes απ’ ό,τι δεσμεύτηκαν) είναι συνήθως καταστροφικά και δύσκολο να ανιχνευθούν (το «κακό» γίνεται αρκετά αργότερα από την αιτία που το προκαλεί) Υπάρχουν υλοποιήσεις που κάνουν περαιτέρω ελέγχους (π.χ. dmalloc), εις βάρος της ταχύτητας Παράλειψη απελευθέρωσης της μνήμης οδηγεί σε διαρροή μνήμης, - πολύ κακό ειδικά για διεργασίες που δεν τερματίζουν Η δέσμευση ενός «καθαρού» ποσού μνήμης οδηγεί πραγματικά στη δέσμευση ενός μεγαλύτερου «μεικτού» ποσού μνήμης για λόγους διαχείρισης του χώρου Αν και η κλήση συστήματος sbrk μπορεί να μειώσει τον χώρο του σωρού, οι περισσότερες υλοποιήσεις βιβλιοθηκών δεν το κάνουν οπότε τα προγράμματα μόνο επεκτείνονται Οι malloc και sbrk ΔΕΝ ΠΡΕΠΕΙ να αναμειγνύονται Οι συναρτήσεις mallopt και mallinfo επιτρέπουν τον ορισμό και την εξέταση παραμέτρων λειτουργίας της βιβλιοθήκης

Διαχείριση μνήμης – alloca [alloca.c] Η στοίβα καθαρίζεται αυτόματα όταν τερματίζει η συνάρτηση συνεπώς δεν υπάρχει κίνδυνος διαρροής μνήμης Προφανώς δεν είναι κατάλληλη για δεδομένα που πρέπει να «ζουν» περισσότερο από τη συνάρτηση double findStdDev(FILE *inFile) { struct numberNode *start = NULL, *ptr, **nextPtr = &start, *newNode; double a, sum = 0, stddevSum = 0, mean; int nDoubles = 0; while (fscanf(inFile, "%lf", &a) == 1) { if ((newNode = alloca(sizeof(struct numberNode))) == NULL) break; sum += a; newNode->number = a; *nextPtr = newNode; nextPtr = &(newNode->next); nDoubles++; } if (nDoubles < 2) return 0; *nextPtr = NULL; mean = sum / nDoubles; for (ptr = start; ptr != NULL; ptr = ptr->next) stddevSum += pow(ptr->number - mean, 2); return sqrt(stddevSum / (nDoubles - 1)); int main(void) {printf("%lf\n", findStdDev(stdin)); return 0;}

setjmp και longjmp setjmp: σημειώνει την τρέχουσα θέση του προγράμματος σε μία μεταβλητή longjmp: ο έλεγχος του προγράμματος μεταφέρεται σε θέση που έχει σημειωθεί Η συνάρτηση που «σημείωσε» τη θέση ΔΕΝ ΠΡΕΠΕΙ να έχει τερματίσει Λειτουργεί και μεταξύ συναρτήσεων Χρήσιμος μηχανισμός για ανάκαμψη από σφάλματα και χειρισμό σημάτων

setjmp και longjmp - Παράδειγμα int fun4(int i){ if(i == 0) /* Λάθος */ return 10; } int fun3(int j){ if(j == 3) /* Λάθος */; return fun4(j + 1); int fun2(int p, int q){ if(p == 2) /* Λάθος */; return fun3(p + q) + fun3(p - q); int fun1(int l,int m, int n){ if((l + m + n)==0) /Λάθος */; return fun2(l + m, l + n);

Λύση 1 int fun4(int i, int *err){ if(i == 0) {*err = 4; return 0;} int fun3(int j, int *err){ int res; if(j == 3) {*err = 3; return 0;} res = fun4(j + 1, err); if (*err == 0) return res; else return 0; int fun2(int p, int q, int *err){ int res1, res2; if(p == 2) {*err = 2; return 0;} res1 = fun3(p + q, err); if (*err != 0) return 0; res2 = fun3(p - q, err); if (*err == 0) return res1 + res2; else return 0; int fun1(int l,int m, int n, int *err){ if((l + m + n)==0) {*err = 1; return 0;} res = fun2(l + m, l + n, err);

Λύση 2 jmp_buf myBuffer; int fun4(int i){ if(i == 0) longjmp(myBuffer, 4); return 10; } int fun3(int j){ if(j == 3) longjmp(myBuffer, 3); return fun4(j + 1); int fun2(int p, int q){ if(p == 2) longjmp(myBuffer, 2); return fun3(p + q) + fun3(p - q); int fun1(int l,int m, int n, int *err){ int res; if((l + m + n)==0) {*err = 1; return 0;} if ((res = setjmp(myBuffer)) == 0) return fun2(l + m, l + n); else { *err = res; return 0;

Όρια πόρων διεργασιών Σε κάθε διεργασία αντιστοιχούν όρια πόρων τα οποία μπορούν να τεθούν και να διαβαστούν με τις κλήσεις getrlimit και setrlimit #include <sys/time.h> #include <sys/resource.h> int getrlimit(int resource, struct rlimit *rptr); int setrlimit(int resource, const struct rlimit *rptr); struct rlimit { rlim_t rlim_cur; /* Τρέχον, ελαστικό όριο */ rlim_t rlim_max; /* Μέγιστο, ανελαστικό όριο */ } Τα όρια μπορούν να μειωθούν ή να αυξηθούν, μέχρι το ανελαστικό όριο Ο υπερ-χρήστης μπορεί να το υπερβεί κι αυτό

Όρια πόρων διεργασιών Όρια τίθενται για: Βλέπε και εντολή ulimit Όριο Περιγραφή RLIMIT_CORE Μέγιστο μέγεθος αρχείου core RLIMIT_CPU Μέγιστη απασχόληση της ΚΜΕ σε δευτερόλεπτα. Υπέρβαση οδηγεί στην αποστολή σήματος SIGXCPU RLIMIT_DATA Μέγιστο μέγεθος (αρχικοποιημένα δεδομένα + μη αρχικοποιημένα δεδομένα + σωρός) RLIMIT_FSIZE Μέγιστο μέγεθος αρχείου. Υπέρβαση οδηγεί στην αποστολή σήματος SIGXFSZ RLIMIT_NOFILE Μέγιστο πλήθος ανοικτών αρχείων (RLIMIT_OFILE στο BSD) RLIMIT_NPROC Μέγιστο πλήθος διεργασιών ανά πραγματική ταυτότητα χρήστη RLIMIT_STACK Μέγιστο μέγεθος στοίβας RLIMIT_VMEM Μέγιστο μέγεθος απεικονιζόμενης μνήμης Βλέπε και εντολή ulimit

Όρια πόρων διεργασιών [rlimit.c] #include <stdio.h> #include <sys/time.h> #include <sys/resource.h> void printLimit(const char *limitName, int theLimit) { struct rlimit lim; if (getrlimit(theLimit, &lim) == -1) { printf("%s: cannot get limit", limitName); return; } printf("%s: ", limitName); if (lim.rlim_cur == RLIM_INFINITY) printf("\tUnlimited \t"); else printf("\t%10ld\t", (long)(lim.rlim_cur)); if (lim.rlim_max == RLIM_INFINITY) printf("\tUnlimited\n"); else printf("\t%10ld\t", (long)(lim.rlim_max)); lim.rlim_cur = (lim.rlim_cur == RLIM_INFINITY) ? 500000L : lim.rlim_cur / 2; setrlimit(theLimit, &lim); } int main(void) { printLimit("RLIMIT_CORE", RLIMIT_CORE); printLimit("RLIMIT_CPU", RLIMIT_CPU); #define doLimit(x) printLimit(#x, x) doLimit(RLIMIT_CPU); doLimit(RLIMIT_FSIZE); return 0;

Έλεγχος διεργασιών Προσδιοριστές, δημιουργία. εκτέλεση, αναμονή, παραχώρηση ταυτότητας, λογιστική

Βασικοί προσδιοριστές διεργασιών #include <sys/types.h> #include <unistd.h> pid_t getpid(void); Ταυτότητα διεργασίας pid_t getppid(void); Ταυτότητα γονικής διεργασίας uid_t getuid(void); Ταυτότητα χρήστη uid_t geteuid(void); Ενεργός ταυτότητα χρήστη gid_t getgid(void); Ταυτότητα ομάδας gid_t getegid(void); Ενεργός ταυτότητα ομάδας Καμία από τις συναρτήσεις αυτές δεν έχει τιμή αποτυχίας

Συνάρτηση fork Ο μοναδικός τρόπος να δημιουργηθεί νέα διεργασία στο Unix #include <sys/types.h> #include <unistd.h> pid_t fork(void) Η νέα διεργασία καλείται θυγατρική διεργασία και είναι ακριβές αντίγραφο της διεργασίας που κάλεσε τη fork Τα τμήματα στοίβας, δεδομένων και σωρού αντιγράφονται Το τμήμα κώδικα μπορεί να διαμοιράζεται Πολλές υλοποιήσεις δεν κάνουν πλήρη αντιγραφή, αλλά χρησιμοποιούν την τεχνική αντιγραφή κατά την τροποποίηση Η συνάρτηση καλείται μία φορά αλλά επιστρέφει δύο φορές Μία επιστροφή στη γονική διεργασία όπου επιστρέφεται η ταυτότητα της θυγατρικής διεργασίας (-1 αν απέτυχε: γεμάτος πίνακας διεργασιών ή εξάντληση ορίου διεργασιών ανά χρήστη) Μία επιστροφή στη θυγατρική διεργασία όπου επιστρέφεται η τιμή 0 Δεν παρέχεται καμία εγγύηση για το ποια θα συνεχίσει πρώτη Αν απαιτείται συγχρονισμός, πρέπει να χρησιμοποιηθούν οι κατάλληλοι μηχανισμοί (επόμενο κεφάλαιο)

Συνάρτηση fork – Παράδειγμα [fork1.c] #include <sys/types.h> #include <unistd.h> int globvar = 6; /* μεταβλητή στα αρχικοποιημένα δεδομένα */ char buf[] = "a write to stdout\n"; int main(void) { int var; /* τοπικές μεταβλητές στη στοίβα */ pid_t pid; var = 88; if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1) perror("write error"); printf("before fork\n"); /* χωρίς εκκένωση της κανονικής εξόδου */ if ( (pid = fork()) < 0) perror("fork error"); else if (pid == 0) { /* θυγατρική */ globvar++; /* τροποποίηση μεταβλητών */ var++; } else sleep(2); /* γονική διεργασία */ printf("pid=%d, globvar = %d, var = %d\n", getpid(), globvar, var); exit(0); }

Συνάρτηση fork - Παράδειγμα command>./fork1 a write to stdout before fork pid = 2715, globvar = 7, var = 89 pid = 2714, globvar = 6, var = 88 command>./fork1 > ppp command>cat ppp pid = 2717, globvar = 7, var = 89 pid = 2716, globvar = 6, var = 88

Fork και διαμοιρασμός αρχείων Πιν. περιγρ. αρχείων Ενδείξεις αρχείου τρέχουσα μετατόπιση Δείκτης κόμβου-ι Πληροφορίες κόμβου-ι Πληροφορίες κόμβου-δ Τρέχον μέγεθος αρχείου flags ptr fd0 fd1 fd2 ... Ενδείξεις αρχείου τρέχουσα μετατόπιση Δείκτης κόμβου-ι Πληροφορίες κόμβου-ι Πληροφορίες κόμβου-δ Τρέχον μέγεθος αρχείου δ1 Πιν. περιγρ. αρχείων flags ptr fd0 fd1 fd2 ... Ενδείξεις αρχείου τρέχουσα μετατόπιση Δείκτης κόμβου-ι Πληροφορίες κόμβου-ι Πληροφορίες κόμβου-δ Τρέχον μέγεθος αρχείου δ2 Πίνακας διεργασιών Πίνακας αρχείων Πίνακας κόμβων-ι

Fork και διαμοιρασμός αρχείων Ο διαμοιρασμός της μετατόπισης είναι ιδιαίτερα σημαντικός Η αλλαγή της μετατόπισης από τη θυγατρική διεργασία ενημερώνει τη μετατόπιση του γονέα και αντιστρόφως Θεωρήστε τη σχέση φλοιού-εντολής Συνήθως ο γονέας αναμένει μέχρι να ολοκληρωθεί η εκτέλεση της θυγατρικής διεργασίας και κατόπιν συνεχίζει Εναλλακτικά, ο γονέας κλείνει τους περιγραφείς και αφήνει να ενημερώνει τα σχετικά αρχεία μόνο η θυγατρική διεργασία Αν γονέας και θυγατρική διεργασία γράφουν χωρίς συγχρονισμό στους περιγραφείς, τα δεδομένα θα είναι αναμεμειγμένα

Κληροδοτούμενες και μη ιδιότητες Κληροδοτούνται (όμοια στη γονική και τη θυγατρική διεργασία): πραγματική και ενεργός ταυτότητα χρήστη και ομάδας συμπληρωματικές ταυτότητες ομάδας προσδιοριστής ομάδας διεργασιών προσδιοριστής συνόδου τερματικό ελέγχου ένδειξη παραχώρησης ταυτότητας χρήστη και ομάδας τρέχων κατάλογος εργασίας πρωταρχικός κατάλογος Μάσκα δημιουργίας αρχείων μάσκα σημάτων και χειρισμός ένδειξη «κλείσιμο κατά τον τερματισμό» για περιγραφείς αρχείων περιβάλλον συσχετισμοί με τμήματα διαμοιραζόμενης μνήμης όρια πόρων

Κληροδοτούμενες και μη ιδιότητες ΔΕΝ κληροδοτούνται (διαφορετικές τιμές στη γονική και τη θυγατρική διεργασία): η τιμή που επιστρέφει η fork η ταυτότητα διεργασίας η ταυτότητα της γονικής διεργασίας οι τιμές των χρονομετρητών tms_utime, tms_stime, tms_cutime και tms_cstime κλειδώματα σε αρχεία που τίθενται από τον γονέα δεν κληροδοτούνται στις θυγατρικές διεργασίες τα εκκρεμή σήματα εγρήγορσης καθαρίζονται στη θυγατρική διεργασία το σύνολο των εκκρεμών σημάτων για τη θυγατρική διεργασία αρχικοποιείται στο κενό

Κύριες χρήσεις της fork Μία διεργασία «αναπαράγεται» προκειμένου να εκτελεστούν ταυτόχρονα διαφορετικά τμήματα του κώδικά της π.χ. εξυπηρέτες δικτύου, αναμένουν αίτηση και δημιουργούν αντίγραφο που αναλαμβάνει τη διεκπεραίωση της αίτησης, ενώ η «αρχική» διεργασία αναμένει επόμενη αίτηση Εκτέλεση διαφορετικού προγράμματος Η fork ακολουθείται από exec Πολλά συστήματα παρέχουν τη spawn, που είναι ουσιαστικά fork(); exec(…); Οι ανεξάρτητες fork/exec παρέχουν βαθμούς ελευθερίας, π.χ. κλείσιμο ή ανακατεύθυνση περιγραφέων αρχείων, χειρισμό σημάτων κ.λπ.

vfork Ίδια χρήση με τη fork, διαφορετική σημασιολογία για πιο αποτελεσματική υλοποίηση όταν πρόκειται να εκτελεσθεί άμεσα exec η σημασιολογία της vfork είναι περίεργη, και ειδικότερα στα σύγχρονα συστήματα με τεχνικές copy-on-write καλό είναι να αποφεύγεται Η vfork δημιουργεί τη νέα διεργασία χωρίς να αντιγράφει τον χώρο διευθύνσεων η θυγατρική διεργασία εκτελείται στον χώρο διευθύνσεων της γονικής, μέχρις ότου καλέσει την exec ή την exit Ο γονέας αναστέλλεται μέχρις ότου η θυγατρική διεργασία καλέσει την exec ή την exit Αυτό μπορεί να οδηγήσει σε αδιέξοδα, αν η θυγατρική διεργασία αναμένει ενέργειες από τη γονική, π.χ. διευθέτηση κλειδωμάτων

vfork – παράδειγμα [vfork.c] #include <sys/types.h> #include <unistd.h> int glob = 6; /* μεταβλητή στα αρχικοποιημένα δεδομένα */ int main(void) { int var; /* τοπική μεταβλητή στη στοίβα */ pid_t pid; var = 88; printf("before vfork\n"); /* χωρίς εκκένωση κανονικής εξόδου */ if ( (pid = vfork()) < 0) perror("vfork error"); else if (pid == 0) { /* θυγατρική διεργασία */ glob++; /* τροποποίηση μεταβλητών γονέα */ var++; printf("pid=%d, glob = %d, var = %d\n", getpid(), glob, var); _exit(0); /* Προσοχή: μόνο _exit, όχι exit! */ } /* γονέας – χωρίς sleep */ printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var); exit(0);

Κωδικοί εξόδου Μία διεργασία τερματίζεται με 5 τρόπους: Κανονικός τερματισμός εκτέλεση return από τη main εκτέλεση συνάρτησης exit() εκτέλεση κλήσης συστήματος _exit() Απότομος τερματισμός κλήση της συνάρτησης abort() τερματισμός λόγω παραλαβής σήματος Σε όλες τις περιπτώσεις, τελικά εκτελείται ο ίδιος κώδικας στον πυρήνα που κλείνει περιγραφείς αρχείων, απελευθερώνει μνήμη, αίρει κλειδώματα κ.λπ.

Κωδικοί εξόδου Ανεξαρτήτως τρόπου τερματισμού, είναι επιθυμητό να μπορεί να ειδοποιηθεί η γονική διεργασία σχετικά με τον τρόπο εξόδου ή τερματισμού της διεργασίας Όταν μία διεργασία καλεί τις exit ή _exit παρέχει στην αντίστοιχη συνάρτηση ως όρισμα έναν ακέραιο που είναι ο κωδικός εξόδου Όταν μία διεργασία τερματίζεται λόγω σήματος, ο πυρήνας δημιουργεί έναν κωδικό εξόδου που υποδεικνύει τον λόγο τερματισμού της διεργασίας Ο γονέας έχοντας τον κωδικό εξόδου της διεργασίας μπορεί να εξετάσει πώς αυτή τερμάτισε (επόμενη διαφάνεια)

Κωδικοί εξόδου κωδικός εξόδου  status #include <sys/wait.h> Μακροεντολή Περιγραφή WIFEXITED(status) WEXITSTATUS(status) Αληθές αν η διεργασία τερμάτισε κανονικά Τα 8 χαμηλότερης τάξης ψηφία της παραμέτρου στην _exit ή exit WIFSIGNALED(status) WTERMSIG(status) Αληθές αν η διεργασία τερμάτισε λόγω σήματος Ο αριθμός του σήματος που προκάλεσε τον τερματισμό της διεργασίας. Στα SVR4 και 4.3BSD+ ισχύει το WCOREDUMP(status): αληθές αν δημιουργήθηκε αρχείο αποτύπωσης μνήμης WIFSTOPPED(status) WSTOPSIG(status) Αληθές αν η διεργασία έχει σταματήσει λόγω σήματος. Ο αριθμός του σήματος που προκάλεσε το σταμάτημα της διεργασίας

Κωδικοί εξόδου [wait1.c 1o μέρος] #include <sys/types.h> #include <sys/wait.h> void pr_exit(int status) { if (WIFEXITED(status)) printf("normal termination, exit status = %d\n", WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status), #ifdef WCOREDUMP WCOREDUMP(status) ? " (core file generated)" : ""); #else ""); #endif else if (WIFSTOPPED(status)) printf("child stopped, signal number = %d\n", WSTOPSIG(status)); }

Ορφανά και ζόμπι «Φυσιολογική ροή»: μία διεργασία τερματίζει και άμεσα η γονική της ζητά την κατάσταση εξόδου. Η θυγατρική εξαφανίζεται Εναλλακτική #1: η γονική διεργασία τερματίζει πριν τη θυγατρική. Προκειμένου η θυγατρική να έχει γονέα, ως γονέας της τίθεται η διεργασία συστήματος init Η init ενσωματώνει κώδικα που ζητά τον κωδικό κατάστασης των θυγατρικών της διεργασιών, όταν αυτές τερματίζουν Πρακτικά, όταν τερματίζεται μία διεργασία, ο πυρήνας διατρέχει τον πίνακα διεργασιών για να δει αν η διεργασία που τερμάτισε είναι γονική κάποιας διεργασίας που είναι ενεργός Εναλλακτική #2: η θυγατρική διεργασία τερματίζει και ο γονέας της δεν ζητά τον κωδικό εξόδου Η θυγατρική διεργασία δεν εξαφανίζεται συνολικά, αλλά γίνεται ζόμπι (η ps τη σημειώνει με Z), καθώς ο πυρήνας απελευθερώνει μνήμη και άλλους πόρους αλλά διατηρεί τουλάχιστον την ταυτότητα διεργασίας και γονικής διεργασίας, τον κωδικό κατάστασης εξόδου και τους χρονομετρητές ΚΜΕ

Συναρτήσεις wait και waitpid Αποτελούν τον τρόπο με τον οποίο μία διεργασία πληροφορείται τον κωδικό εξόδου μιας θυγατρικής της διεργασίας Το γεγονός του τερματισμού το πληροφορείται με ένα σήμα SIGCHLD, που ο εξ ορισμού χειρισμός του είναι να αγνοείται #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int options); Τιμή επιστροφής: προσδιοριστής διεργασίας που τερμάτισε ή -1 αν προέκυψε σφάλμα η διεργασία δεν έχει θυγατρικές Ο κωδικός εξόδου επιστρέφεται στην παράμετρο statloc (αν ο δείκτης είναι NULL, δεν επιστρέφεται ο κωδικός εξόδου) Καλώντας τις wait/waitpid μία διεργασία μπορεί να: ανασταλεί (αν όλες οι θυγατρικές της εκτελούνται – η waitpid έχει τη δυνατότητα να μην οδηγεί σε αναστολή) επιστρέψει άμεσα τον προσδιοριστή και τον κωδικό εξόδου μίας θυγατρικής που έχει ήδη τερματίσει επιστρέψει σφάλμα (-1), αν η διεργασία δεν έχει θυγατρικές

wait – παράδειγμα [wait1.c 2o μέρος] #include <sys/types.h> #include <sys/wait.h> int main(void) { pid_t pid; int status; if ( (pid = fork()) < 0) perror("fork error"); else if (pid == 0) /* child */ exit(7); if (wait(&status) != pid) /* αναμονή */ perror("wait error"); pr_exit(status); /* εκτύπωση κατάστασης εξόδου */ else if (pid == 0) /* child */ abort(); /* παράγει SIGABRT */ if (wait(&status) != pid) /* αναμονή */ perror("wait error"); else if (pid == 0) /* child */ status /= 0; /* διαίρεση με το 0 προκαλεί SIGFPE */ pr_exit(status); /* εκτύπωση κατάστασης εξόδου */ exit(0); }

waitpid Αναμονή για μία συγκεκριμένη θυγατρική διεργασία pid_t waitpid(pid_t pid, int *statloc, int options); Αν δεν υπάρχει, πρέπει να καλείται συνεχώς η wait μέχρι να επιστρέψει τον προσδιοριστή διεργασίας της επιθυμητής θυγατρικής Στην waitpid μπορούν να προσδιορίζονται και ενδείξεις (καμία, μία ή περισσότερες) Ένδειξη Περιγραφή WNOHANG Αν δεν υπάρχουν άμεσα διαθέσιμα αποτελέσματα δεν αναστέλλει τη διεργασία αλλά επιστρέφει 0 WUNTRACED Εξετάζονται και θυγατρικές διεργασίες που έχουν σταματήσει WCONTINUED Εξετάζονται και θυγατρικές που είχαν σταματήσει και συνεχίστηκαν WNOWAIT Η διεργασία που εξετάζεται διατηρείται σε κατάσταση που μπορεί να εξετασθεί ξανά με ταυτόσημα αποτελέσματα Δυνατότητες για pid: pid == -1: οποιαδήποτε θυγατρική διεργασία (αντίστοιχη με wait + ενδείξεις) pid > 0: αναμονή για τη συγκεκριμένη διεργασία pid == 0: οποιαδήποτε διεργασία στην ίδια ομάδα διεργασιών με την καλούσα pid < 0: διεργασία στην ομάδα διεργασιών με προσδιοριστή abs(pid)

wait3 και wait4 Αντίστοιχες με wait, waitpid, επιστρέφουν και τους πόρους που καταναλώθηκαν από τη διεργασία #include <sys/types.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> pid_t wait3(int *statloc, int options, struct rusage *rusage); pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage); Στην πρόσθετη παράμετρο επιστρέφονται πληροφορίες για την εκτέλεση και τους πόρους της διεργασίας όπως: χρόνος χρήστη και συστήματος εναλλαγές πράξεις εισόδου και εξόδου σφάλματα σελίδων πλήθος μηνυμάτων που απεστάλησαν ή ελήφθησαν κ.ο.κ.

Οι συναρτήσεις exec Ο τρόπος με τον οποίο εκτελείται ένα συγκεκριμένο πρόγραμμα από τον δίσκο Δεν δημιουργείται νέα διεργασία και δεν αλλάζει η ταυτότητα διεργασίας Όλος ο χώρος διευθύνσεων της διεργασίας (κώδικας, δεδομένα, στοίβα, σωρός) αντικαθίσταται με αυτά του καθορισθέντος προγράμματος και η εκτέλεση ξεκινά από τη main #include <unistd.h> int execl(const char *pathname, const char *arg0, ... /* NULL */); int execv(const char *pathname, char * const argv[]); int execle(const char *pathname, const char *arg0, ... /* NULL, char * const envp[] */); int execve(const char *pathname, char * const argv[], char * const envp[] */); int execlp(const char *filename, const char *arg0, ... /* NULL */); int execvp(const char *filename, char * const argv[]);

Οι συναρτήσεις exec - Σημειώσεις Στις execlp και execvp εξετάζονται τα ακόλουθα: αν το filename περιέχει διαχωριστή καταλόγων (/) εκλαμβάνεται ως διαδρομή αλλιώς αναζητάται ως αρχείο στους καταλόγους που ορίζει η μεταβλητή περιβάλλοντος PATH, με τη σειρά που εμφανίζονται Στις execl… το τέλος των παραμέτρων σηματοδοτείται με τον δείκτη NULL [(char *)0] Όπου χρησιμοποιείται πίνακας (για παραμέτρους ή περιβάλλον), πρέπει αμέσως μετά το τελευταίο στοιχείο του που θέλουμε να χρησιμοποιήσουμε να υπάρχει ένα στοιχείο με τιμή NULL [(char *)0] Μνημονικός κανόνας: l = list, v = vector p = Χρήση PATH e = καθορισμός περιβάλλοντος

Κληροδοτούνται μέσω της exec Προσδιοριστής διεργασίας και γονικής διεργασίας Πραγματική και ενεργός ταυτότητα χρήστη και ομάδας Οι ενεργές ταυτότητες μπορεί να αλλάξουν αν το πρόγραμμα είναι setuid ή setgid Συμπληρωματικές ταυτότητες ομάδων Προσδιοριστής ομάδας διεργασιών Προσδιοριστής συνόδου τερματικό ελέγχου τρέχων κατάλογος εργασίας πρωταρχικός κατάλογος Μάσκα δημιουργίας αρχείων μάσκα σημάτων Εκκρεμή σήματα όρια πόρων Χρόνος μέχρι την αποστολή σήματος εγρήγορσης οι τιμές των χρονομετρητών tms_utime, tms_stime, tms_cutime και tms_cstime Τα αρχεία μπορεί να κλείνουν ή όχι, ανάλογα με την ένδειξη «κλείσιμο κατά την exec» - οι κατάλογοι κλείνουν οπωσδήποτε

Σχέση κλήσεων exec Στα περισσότερα συστήματα UNIX μόνο η execve είναι κλήση συστήματος, οι λοιπές είναι διαδικασίες βιβλιοθήκης execlp execl execle Διαμόρφωση argv Διαμόρφωση argv Διαμόρφωση argv execvp execv execve Αναζήτηση στα στοιχεία της PATH Χρήση της environ

exec – παράδειγμα [exec1.c 1/2] #include <sys/types.h> #include <sys/wait.h> char *env_init[] = {"USER=unknown", "PATH=/tmp", NULL}; char *args[] = {"echoall", "arg1", "second arg", "3rd arg", NULL}; int main(int argc, char *argv){ pid_t pid; if ( (pid = fork()) < 0) perror("fork error"); else if (pid == 0) {/* δίνουμε ορίσματα και περιβάλλον */ if (execle("echoall", "echoall-progam", "myarg1", "MY ARG2", (char *) 0, env_init) < 0) perror("execle error"); } if (waitpid(pid, NULL, 0) < 0) perror("wait error"); else if (pid == 0) { /* δίνουμε ορίσματα μόνο */ if (execvp("echoall", args) < 0) perror("execvp error"); puts("***END***"); exit(0);

exec – παράδειγμα [exec1.c 2/2] #include <stdio.h> int main(int argc, char *argv[]) { int i; extern char **environ; for (i = 0; i < argc; i++) printf("argv[%d] = %s\n", i, argv[i]); printf("-----------------\n"); for (i = 0; environ[i] != NULL; i++) printf("%s\n", environ[i]); return 0; } ΕΞΟΔΟΣ argv[0] = echoall-program argv[1] = myarg1 argv[2] = MY ARG2 ----------------- USER=unknown PATH=/tmp ***END*** argv[0] = echoall argv[1] = arg1 argv[2] = second argument argv[3] = 3rd arg HOME=/users/cst/costas PATH=/export/spare/oracle/OraHome1/bin:/usr/bin:.:/usr/sbin:/etc:/usr/local/bin:/usr/ccs/bin:/users/cst/costas/bin LOGNAME=costas Πολλές ακόμη γραμμές

Ιεραρχίες διεργασιών Μία διεργασία μπορεί να έχει πολλές θυγατρικές Κάθε διεργασία έχει ακριβώς μία γονική διεργασία Κάθε σύνδεση ενός χρήστη δημιουργεί μία σύνοδο Σύνδεση: login, rlogin/ssh, xterm κ.λπ. Η σύνοδος κανονικά έχει ένα τερματικό ελέγχου Κάθε σύνοδος μπορεί να έχει: Μία ομάδα διεργασιών προσκηνίου Οποιονδήποτε αριθμό ομάδων διεργασιών παρασκηνίου Οι ομάδες εξυπηρετούν τη διαχείριση σημάτων Π.χ. κατά την εκτέλεση της ls –l | grep "^-" | sort –n + 4 πατάμε CTRL-C, κάποια εντολή εκτελεί διαίρεση με το 0 κ.λπ.

Παράδειγμα ιεραρχίας Σύνοδος init getty tcsh (από getty/login) inetd sshd Ομάδα 1 Ομάδα 2 sh crammer_det crammer_det & vi Σύνοδος ls sort Ομάδα 3 ls –R / | sort > myfile &

Δυναμικές βιβλιοθήκες «Κλασική» λειτουργία μεταγλώττισης-εκτέλεσης Αρχείο C Προεπεξερ-γαστής Μεταγλωτ-τιστής Συνδέτης Εφαρμογή 3 3 Αρχεία ενσωμάτωσης Βιβλιοθήκες Όλος ο κώδικας (χρήστη και συστήματος) είναι ενσωματωμένος στην εφαρμογή

Δυναμικές βιβλιοθήκες Το κλασικό μοντέλο έχει περιορισμούς: Σπατάλη χώρου Ο κώδικας των βιβλιοθηκών αποθηκεύεται τόσες φορές όσα και τα προγράμματα που τις χρησιμοποιούν Τι συμβαίνει όταν εγκαθιστούμε επιδιορθώσεις για τις βιβλιοθήκες; Τα προγράμματα χρειάζονται επαναμεταγλώττιση Όλη η λειτουργικότητα καθορίζεται κατά τη μεταγλώττιση του προγράμματος Θα θέλαμε όμως να δίνουμε λειτουργικότητα δυναμικά, π.χ. σε έναν εξυπηρέτη διαδικτύου (web server) να μπορούμε να προσθέσουμε τη δυνατότητα εκτέλεσης αρχείων PHP χωρίς να χρειάζεται να τον επαναμεταγλωττίσουμε (κάτι που απαιτεί να έχουμε τον πρωτογενή κώδικα)

Δυναμικές βιβλιοθήκες Οι δυναμικές βιβλιοθήκες αντιμετωπίζουν αυτά τα προβλήματα Δύο υποκατηγορίες: Διαμοιραζόμενες βιβλιοθήκες Ο κώδικας και οι μεταβλητές τους φορτώνονται από το λειτουργικό σύστημα στο χώρο διευθύνσεων της διεργασίας όταν ξεκινάει η εφαρμογή Είναι γνωστές κατά τη μεταγλώττιση Δυναμικά συνδεόμενες βιβλιοθήκες Ο κώδικας και οι μεταβλητές τους φορτώνονται από το λειτουργικό σύστημα στο χώρο διευθύνσεων της διεργασίας όταν το ζητήσει η διεργασία κατά την εκτέλεσή της Δεν είναι απαραίτητα γνωστές κατά τη μεταγλώττιση, μπορεί να ορίζονται σε αρχεία διαμόρφωσης

Δημιουργία δυναμικής βιβλιοθήκης Μεταγλωττίζουμε τα αρχεία C ώστε να παράγουμε καταληκτικό κώδικα (object code) Στη μεταγλώττιση χρησιμοποιούμε την ειδική ένδειξη του gcc -fPIC (PIC: position-independent code) Καλούμε τον gcc για να συνδυάσει τα αρχεία που παρήχθησαν στο βήμα 1 σε ένα ενιαίο αρχείο δυναμικής βιβλιοθήκης Και πάλι χρησιμοποιούμε την ένδειξη -fPIC σε συνδυασμό με την ένδειξη -shared

Δημιουργία δυναμικής βιβλιοθήκης – Παράδειγμα (1) Έστω ότι θέλουμε να φτιάξουμε μία βιβλιοθήκη που θα περιέχει: Μία συνάρτηση στην οποία θα δίνουμε τη διαδρομή προς ένα αντικείμενο του συστήματος αρχείων και θα μας επιστρέφει σε μία συμβολοσειρά ένα λεκτικό που θα περιγράφει τον τύπο του αντικειμένου Μία συνάρτηση στην οποία θα δίνουμε τη διαδρομή προς ένα αντικείμενο του συστήματος αρχείων και θα μας επιστρέφει το μέγεθός του Πρότυπα συναρτήσεων: int fileType(const char *path, char *ftype, size_t nchars); off_t fileSize(const char *path); Η βιβλιοθήκη μας θα ονομάζεται libfileops.so

Δημιουργία δυναμικής βιβλιοθήκης – Παράδειγμα (2) Βήμα 1: Δημιουργούμε το αρχείο ενσωμάτωσης fileops.h #ifndef __FILEOPS_HEADER #define __FILEOPS_HEADER int fileType(const char *path, char *ftype, size_t nchars); off_t fileSize(const char *path); #endif

Δημιουργία δυναμικής βιβλιοθήκης – Παράδειγμα (3) Βήμα 2: γράφουμε τον κώδικα του fileops.c int fileType(const char *path, char *ftype, size_t nchars) { struct stat buf; char *desc_str; int res; if ((res = lstat(path, &buf)) < 0) return res; else if (S_ISREG(buf.st_mode)) desc_str = "regular file"; else if (S_ISDIR(buf.st_mode)) desc_str = "directory"; else if (S_ISCHR(buf.st_mode)) desc_str = "character special"; else if (S_ISBLK(buf.st_mode)) desc_str = "block special"; else if (S_ISLNK(buf.st_mode)) desc_str = "symbolic link"; else desc_str = "Something else"; (void)strncpy(ftype, desc_str, nchars); return 0; } off_t fileSize(const char *path) { struct stat buf; int res; if ((res = lstat(path, &buf)) < 0) return res; return buf.st_size;

Δημιουργία δυναμικής βιβλιοθήκης – Παράδειγμα (4) Βήμα 3: μεταγλωττίζουμε το αρχείο και παράγουμε τη βιβλιοθήκη gcc -c -fPIC -o fileops.o fileops.c -lc gcc -shared -fPIC -Wl,-soname,libfileops.so -o libfileops.so fileops.o Η βιβλιοθήκη είναι έτοιμη!

Χρήση δυναμικής βιβλιοθήκης – παράδειγμα (1) Βήμα 1: γράφουμε τον κώδικα του αρχείου (fileops_use.c) #include <stdio.h> #include <errno.h> #include "fileops.h" int main(int argc, char *argv[]) { int res, i; char descr[128]; off_t fsize; for (i = 1; i < argc; i++) { res = fileType(argv[i], descr, sizeof(descr)); if (res < 0) { perror(argv[i]); continue; } fsize = fileSize(argv[i]); printf("%s is of type %.*s and %ld bytes\n", argv[i], (int)(sizeof(descr)), descr, (long)fsize); return 0;

Χρήση δυναμικής βιβλιοθήκης – παράδειγμα (2) Μεταγλωττίζουμε: setenv LD_RUN_PATH `pwd` gcc -o fileops_use fileops_use.c libfileops.so Το πρόγραμμά μας είναι έτοιμο!

Ενημέρωση δυναμικής βιβλιοθήκης (1) Εμπλουτίζουμε τη δυναμική βιβλιοθήκη με δυνατότητα αναγνώρισης αρχείων τύπου FIFO και SOCK ("fileops1.c") int fileType(const char *path, char *ftype, size_t nchars) { struct stat buf; char *desc_str; int res; if ((res = lstat(path, &buf)) < 0) return res; else if (S_ISREG(buf.st_mode)) desc_str = "regular file"; else if (S_ISDIR(buf.st_mode)) desc_str = "directory"; else if (S_ISCHR(buf.st_mode)) desc_str = "character special"; else if (S_ISBLK(buf.st_mode)) desc_str = "block special"; else if (S_ISLNK(buf.st_mode)) desc_str = "symbolic link"; else if (S_ISFIFO(buf.st_mode)) desc_str = "FIFO"; #ifdef S_ISSOCK else if (S_ISSOCK(buf.st_mode)) desc_str = "socket"; #endif else desc_str = "Something else"; (void)strncpy(ftype, desc_str, nchars); return 0; }

Ενημέρωση δυναμικής βιβλιοθήκης (2) Βήμα 3: μεταγλωττίζουμε το αρχείο και παράγουμε τη βιβλιοθήκη gcc -c -fPIC -o fileops1.o fileops1.c -lc gcc -shared -fPIC -Wl,-soname,libfileops.so -o libfileops.so fileops1.o Η ενημερωμένη βιβλιοθήκη είναι έτοιμη και η πρόσθετη λειτουργικότητα άμεσα διαθέσιμη στο εκτελέσιμο fileops_use.c

Ενημέρωση διαμοιραζόμενων βιβλιοθηκών: μερικά θέματα (1) Δεν μπορούμε πάντα να ενημερώνουμε τις διαμοιραζόμενες βιβλιοθήκες με τον τρόπο αυτό. Δημιουργείται πρόβλημα όταν: Μία συνάρτηση κάνει διαφορετικά πράγματα απ’ ό,τι έκανε πριν (π.χ. αν αλλάξουμε τη fileType ώστε εκτός από το να επιστρέφει τη συμβολοσειρά να την τυπώνει επίσης) Αλλάξουν οι τύποι των δεδομένων που εξάγονται (π.χ. long από int) Προφανώς δεν αφορά τοπικά δεδομένα συναρτήσεων! Αφαιρεθούν συναρτήσεις ή δεδομένα που εξάγονται Αλλάξουν τα πρότυπα των συναρτήσεων που εξάγονται

Ενημέρωση διαμοιραζόμενων βιβλιοθηκών: μερικά θέματα (2) Για να μπορούμε να έχουμε νέες εκδόσεις διαμοιραζόμενων βιβλιοθηκών (που έχουν τις προηγούμενες ασυμβατότητες) και να διατηρείται η λειτουργία των προγραμμάτων που έχουν ήδη μεταγλωττισθεί και χρησιμοποιούν την παλιά προδιαγραφή: Οι βιβλιοθήκες έχουν μείζονα και ελάσσονα έκδοση π.χ. libfileutils.so.1.3 (1 μείζων έκδοση, 3  ελάσσων έκδοση) Όταν αλλάζουμε τη βιβλιοθήκη με τρόπο που δημιουργεί ασυμβατότητα, αλλάζουμε μείζονα έκδοση (π.χ. libfileutils.so.2.1) Όταν αλλάζουμε τη βιβλιοθήκη με τρόπο που δεν δημιουργεί ασυμβατότητα, αλλάζουμε ελάσσονα έκδοση (π.χ. libfileutils.so.1.4) Πάντα διατηρούμε έναν συμβολικό σύνδεσμο χωρίς έκδοση στην πιο πρόσφατη έκδοση της βιβλιοθήκης (libfileutils.so  libfileutils.so.2.1)

Δυναμική σύνδεση βιβλιοθήκης Χρήσιμη όταν δεν ξέρουμε τις βιβλιοθήκες που θα χρειαστούμε κατά τη μεταγλώττιση, αλλά αυτές ορίζονται στη συνέχεια π.χ. μέσω ενός αρχείου διαμόρφωσης Βασικό μοντέλο χρήσης: dlopen: δυναμική σύνδεση μιας βιβλιοθήκης dlsym: εύρεση ενός συμβόλου (ονόματος συνάρτησης ή μεταβλητής) στη βιβλιοθήκη. Επιστρέφεται η διεύθυνση του συμβόλου στην οποία πρέπει μετά να εκτελέσουμε μετατροπή τύπου dlclose: κλείσιμο της βιβλιοθήκης και απελευθέρωση των πόρων που καταλαμβάνει dlerror: αναφορά σφάλματος από τις ανωτέρω συναρτήσεις Οι παραπάνω συναρτήσεις ορίζονται στο αρχείο ενσωμάτωσης dlfcn.h

Δυναμική σύνδεση βιβλιοθήκης Πρότυπα συναρτήσεων void *dlopen(const char *file, int mode); Αν mode == RTLD_LAZY τότε τυχόν σύμβολα που χρειάζονται διαδικασίες της βιβλιοθήκης που ανοίγουμε θα αναζητηθούν όταν κληθούν οι διαδικασίες Αν mode == RTLD_NOW τότε τυχόν σύμβολα που χρειάζονται διαδικασίες της βιβλιοθήκης που ανοίγουμε θα αναζητηθούν κατά τη διαδικασία του ανοίγματος – αν δεν βρεθούν το άνοιγμα αποτυγχάνει void *dlsym(void *handle, const char *symbol); int dlclose(void *handle); char *dlerror(void);

Δυναμική σύνδεση βιβλιοθήκης – Παράδειγμα (1) Έστω ότι χρειάζεται σε ένα λογισμικό βιβλιοθήκης να πιστοποιούμε χρήστες βάσει username και password και να ελέγχουμε αν οι πιστοποιημένοι χρήστες είναι διαχειριστές Πρότυπο συνάρτησης: int authenticateUser(const char *username, const char *password, int *isAdmin); Η υλοποίηση της πιστοποίησης μπορεί να γίνεται ανάλογα με τις επιθυμίες του πελάτη: Από ένα αρχείο Από έναν εξυπηρέτη LDAP Από μία βάση δεδομένων mysql κ.ο.κ. Στο πρόγραμμά μας ορίζουμε ότι πρέπει να μας δίνεται μία βιβλιοθήκη που υλοποιεί τον έλεγχο συνθηματικών και ο πελάτης ορίζει ποιά βιβλιοθήκη θα χρησιμοποιηθεί Μέσω αρχείου διαμόρφωσης, παραμέτρου κ.ο.κ.

Δυναμική σύνδεση βιβλιοθήκης – Παράδειγμα (2) Διακρίβωση ταυτότητας – κυρίως πρόγραμμα (dlib-main.c) typedef int (*authFunction)(const char *, const char *, int *); int main(int argc, char *argv[]) { void *dlhandle = NULL; authFunction myAuthFunction; int result, isAdmin; char username[128], password[128]; if (argc == 1) {fprintf(stderr, "specify authentication dynamic library"); exit(1);} if ((dlhandle = dlopen(argv[1], RTLD_NOW)) == NULL) { fprintf(stderr, "cannot open %s: %s\n", argv[1], dlerror()); exit(1); } if ((myAuthFunction = dlsym(dlhandle, "authenticateUser")) == NULL) { fprintf(stderr, "%s: authenticateUser: %s\n", argv[1], dlerror()); exit(1);} printf("enter username: "); (void)scanf("%127s", username); printf("enter password: "); (void)scanf("%127s", password); result = (*myAuthFunction)(username, password, &isAdmin); if (result == -1) { printf("login incorrect\n"); exit(1); } else printf("login successfull; admin rights: %s\n", (isAdmin) ? "yes" : "no"); dlclose(dlhandle); return 0; }

Δυναμική σύνδεση βιβλιοθήκης – Παράδειγμα (3) Δυναμική βιβλιοθήκη με διακρίβωση ταυτότητας από αρχείο απλού κειμένου (dlib-ptext.c) int authenticateUser(const char *username, const char *password, int *isAdmin) { FILE *fp; char pwline[256], *elem; if ((fp = fopen("auth.txt", "r")) == NULL) { fprintf(stderr, "cannot open auth file\n"); return -1; } while (fgets(pwline, 256, fp) != NULL) { if ((elem = strtok(pwline, ":")) == NULL) continue; if (strcmp(elem, username) != 0) continue; if ((elem = strtok(NULL, ":")) == NULL) continue; if (strcmp(elem, password) != 0) continue; if (strcmp(elem, "admin\n") == 0) *isAdmin = 1; else *isAdmin = 0; fclose(fp); return 0; } fclose(fp); return -1; }

Δυναμική σύνδεση βιβλιοθήκης – Παράδειγμα (4) Μεταγλώττιση κυρίως προγράμματος setenv LD_RUN_PATH `pwd` gcc -Wall -o dlib-main dlib-main.c -ldl Μεταγλώττιση βιβλιοθήκης με διακρίβωση ταυτότητας από αρχείο απλού κειμένου gcc -c -fPIC -o dlib-ptext.o dlib-ptext.c -lc gcc -shared -fPIC -Wl,-soname,libptextauth.so -o libptextauth.so dlib-ptext.c Εκτέλεση προγράμματος ./dlib-main.c libptextauth.so

Δυναμική σύνδεση βιβλιοθήκης – Παράδειγμα (5) Δυναμική βιβλιοθήκη με διακρίβωση ταυτότητας από ldap (dlib-ldap.c) #include <stdio.h> #include <ldap.h> int authenticateUser(const char *username, const char *password, int *isAdmin) { LDAP *con; char dn[128]; con = ldap_init("tripoli2.uop.gr", 389); if ((con = ldap_init("tripoli2.uop.gr", 389)) == NULL) return -1; sprintf(dn, "uid=%s,ou=people,dc=uop,dc=gr", username); if (ldap_simple_bind_s(con, dn, password) != 0) return -1; if (strncmp(username, "adm", 3) == 0) *isAdmin = 1; else *isAdmin = 0; (void)ldap_unbind_s(con); return 0; }

Δυναμική σύνδεση βιβλιοθήκης – Παράδειγμα (6) Μεταγλώττιση βιβλιοθήκης με διακρίβωση ταυτότητας από αρχείο απλού κειμένου gcc -c -fPIC -o dlib-ldap.o dlib-ldap.c -lc gcc -shared -fPIC -Wl,-soname,libldapauth.so -o libldapauth.so dlib-ldap.o -lldap Εκτέλεση προγράμματος ./dlib-main libldapauth.so

Κατάλογοι για δυναμικά συνδεόμενες βιβλιοθήκες Οι κατάλογοι όπου θα αναζητηθούν οι δυναμικά συνδεόμενες βιβλιοθήκες (για τις οποίες δεν ορίζεται ρητώς διαδρομή π.χ. Εντός της dlopen) είναι: Οι εξ ορισμού διαδρομές βιβλιοθηκών του συστήματος Τυπικά /lib /usr/lib και πιθανώς /usr/local/lib Πολλά συστήματα δίνουν τη δυνατότητα ορισμού των defaults μέσω αρχείων διαμόρφωσης, π.χ. /etc/ld.so.conf Κατά τη μεταγλώττιση του προγράμματος, χρησιμοποιώντας τη μεταβλητή LD_RUN_PATH Κατά την εκτέλεση, οι βιβλιοθήκες πρέπει να βρίσκονται ακριβώς στην ίδια διαδρομή (ή σε κάποια από τις εξ ορισμού διαδρομές του συστήματος) Κατά την εκτέλεση του προγράμματος, χρησιμοποιώντας τη μεταβλητή LD_LIBRARY_PATH Π.χ. setnev LD_LIBRARY_PATH ~/mylibs:/some/other/dir