ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ Διάλεξη 2 ΔΙΕΡΓΑΣΙΕΣ Οι διαφάνειες έχουν καθαρά επικουρικό χαρακτήρα στην παρουσίαση των διαλέξεων του μαθήματος. Δεν αντικαθιστούν σε καμία περίπτωση την παρακάτω βιβλιογραφία που αποτελεί και το βασικό εγχειρίδιο αναφοράς. Βιβλιογραφία: Σύγχρονα Λειτουργικά Συστήματα, A.S. Tanenbaum, 4 η έκδ., Κλειδάριθμος, 2018. Λειτουργικά Συστήματα, W. Stallings, 9 η έκδ., Τζιόλα, 2017. Λειτουργικά Συστήματα, A. Silberschatz, P. Galvin, G. Gagne, 7 η εκδ., ΙΩΝ, 2007.
Στόχοι Η διάλεξη αυτή έχει ως στόχο να εισάγει τον σπουδαστή στις διεργασίες και τον τρόπο διαχείρισής τους σε ένα υπολογιστικό σύστημα, και ειδικότερα: να γνωρίσει τη δομή, τα μοντέλα και τις καταστάσεις των διεργασιών να γνωρίσει τις εντολές δημιουργίας και διαχείρισης διεργασιών να είναι σε θέση να εφαρμόσει τις παραπάνω μεθόδους δημιουργίας και διαχείρισης διεργασιών για να επιλύσει προβλήματα. 2
Ενότητες Η έννοια της διεργασίας Καταστάσεις διεργασίας Εναλλαγή περιβάλλοντος λειτουργίας Πίνακας ελέγχου διεργασιών Δημιουργία και τερματισμός διεργασίας Παραδείγματα Ερωτήσεις 3
Η έννοια της διεργασίας Διεργασία (Process) ο μηχανισμός εκτέλεσης ενός προγράμματος Η διαδικασία (ενεργή οντότητα) περιέχει ένα σύνολο από συσχετισμένους με αυτή πόρους προκειμένου να εκτελεστεί, το πρόγραμμα (παθητική οντότητα). Το ίδιο πρόγραμμα μπορεί να εκτελείται από πολλές διεργασίες (με διαφορετικές ή και ίδιες ακολουθίες εκτέλεσης των εντολών). Το λειτουργικό μπορεί να εκτελεί πολλές διεργασίες «παράλληλα» μεταξύ τους. A process can be defined as: a program in execution an instance of a running program the entity that can be assigned to, and executed on, a processor a unit of activity characterized by a single sequential thread of execution, a current state, and an associated set of system resources 4
Η διεργασία αποτελεί ένα στιγμιότυπο της εκτέλεσης ενός προγράμματος (instance of a program in execution). (compiled & linked) Για παράδειγμα ένα σε ένα σύστημα όπου 20 χρήστες εκτελούν από κοινού ένα κειμενογράφο (π.χ. vi), τότε έχουμε 20 ξεχωριστές διεργασίες οι οποίες μοιράζονται τον ίδιο εκτελέσιμο κώδικα (share the same executable code).
Χώρος διευθύνσεων της διεργασίας Κάθε διεργασία έχει το δικό της χώρο διευθύνσεων. Ένας χώρος διευθύνσεων (address space) μιας διεργασίας είναι το σύνολο των εικονικών διευθύνσεων το οποίο μπορεί να χρησιμοποιήσει (μέσω των οποίων απευθύνεται στη φυσική μνήμη). Τυπικά αποτελείται από τα ακόλουθα τμήματα: environment: αποθηκεύονται μεταβλητές περιβάλλοντος και παράμετροι γραμμής εντολών stack: αποθηκεύονται μεταβλητές συναρτήσεων, επιστρεφόμενες τιμές και προσωρινές μεταβλητές heap: χρησιμοποιείται για επιπλέον χώρο διευθύνσεων αν απαιτηθεί κατά την εκτέλεση της διεργασίας (δυναμική δέσμευση μνήμης) data: για αρχικοποιημένες στατικές μεταβλητές και σταθερές bss: για μη αρχικοποιημένες στατικές και σφαιρικές μεταβλητές text/code: όπου αποθηκεύεται ο κώδικας (σε εκτελέσιμη μορφή, binary code).
Οι διεργασίες μέσω της εικονικής μνήμης (virtual to physical memory mapping) μπορούν να μοιράζονται το ίδιο φυσικό μπλοκ μνήμης (δηλ. πλαίσια σελίδων στη φυσική μνήμη), π.χ. τις ίδιες βιβλιοθήκες (shared libraries) και το ίδιο τμήμα του κώδικα (code/text), Εξαλείφεται έτσι η ανάγκη δημιουργίας αντιγράφων όλων των σελίδων των δεδομένων της αρχικής (γονικής) διεργασίας. Σημείωση: Περισσότερες σχετικές πληροφορίες για το χώρο διευθύνσεων των εκτελέσιμων και το τι μοιράζονται ως διεργασίες, παρατίθενται στο τέλος των διαφανειών ως ενημερωτικές διαφάνειες. Η εικονική ή ιδεατή μνήμη (virtual memory) θα μελετηθεί σε σχετική διάλεξη που ακολουθεί. 7
Τυπική μορφή των τμημάτων του εκτελέσιμου μιας διεργασίας στη μνήμη On 32-bit Linux systems, the code segment starts at address 0x08048000. Το text segment εμπεριέχει τον κώδικα σε εκτελέσιμη μορφή (binary image) της διεργασίας. Το data segment εμπεριέχει αρχικοποιημένες στατικές (static) μεταβλητές. Το bss segment εμπεριέχει μη αρχικοποιημένες στατικές (static) και σφαιρικές (global) μεταβλητές. Τη στοίβα (stack segment) για τα ορίσματα των συναρτήσεων, επιστροφές και τοπικές (automatic/local) μεταβλητές. Το σωρό (heap segment) (ελεύθερος χώρος) για τη δυναμική δέσμευση μνήμης. Επιπλέον, ένα τμήμα environment (συνήθως unmapped to user code) εμπεριέχει μεταβλητές περιβάλλοντος (environment variables) και ορίσματα γραμμής εντολών (command line arguments). Το μέγεθος των τμημάτων text+data+bss μιας διεργασίας προσδιορίζεται κατά τη μεταγλώττιση και παραμένει σταθερό κατά την εκτέλεση. Όμως μια διεργασία μπορεί κατά την εκτέλεση δυναμικά να επεκταθεί στο ελεύθερο χώρο (εικονικής) μνήμης του σωρού (π.χ. χρήση της malloc() στη C). Πηγή: https://huizistar.wordpress.com/2013/05/09/computer-system-chapter-7/ Ενημερωτική προαιρετική διαφάνεια
Καταστάσεις διεργασίας new: η διεργασία δημιουργείται running: η διεργασία εκτελείται σε κάποιον επεξεργαστή waiting ή blocked: η διεργασία αναμένει (απενεργοποιημένη) κάποιο συμβάν ή πόρο ready: η διεργασία αναμένει (ready queue ή run queue) να τις δοθεί (από το ΛΣ) κάποιος επεξεργαστής για την συνέχιση της εκτέλεσης της terminated: η διεργασία έχει ολοκληρώσει την εκτέλεσή της 9
10
Πηγή εικόνας: www.bogotobogo.com/linux/linux_process_and_signals.php 11
Αλλαγές κατάστασης διεργασίας running -> waiting: Μια διεργασία αλλάζει την κατάσταση της από running σε waiting συνήθως όταν ζητά να τις δοθεί κάποιος πόρος (π.χ. Ι/Ο) ή επιθυμεί να χρησιμοποιήσει κάποιον πόρο που δεν είναι διαθέσιμος και πρέπει να ελευθερωθεί. waiting -> ready: Μια διεργασία αλλάζει την κατάσταση της από waiting σε ready όταν τις δοθεί ο πόρος που ζητήθηκε ή συμβεί το γεγονός (σήμα) που περίμενε. running -> ready: Μια διεργασία αλλάζει την κατάσταση της από running σε ready συνήθως όταν η εκτέλεση της διακόπτεται από διακοπή υλικού (hardware interrupt) ή άλλη κλήση συστήματος (software trap). ready -> running: Μια διεργασία αλλάζει την κατάσταση της από ready σε running όταν τις δοθεί επεξεργαστής για να συνεχίσει την εκτέλεση της. 12
When none of the processes in main memory is in the Ready state, the OS swaps one of the blocked processes out on to disk into a suspend queue. Πηγή εικόνας: https://books.google.gr/books?id=3ie3uwre4l0c&printsec=frontcover&hl=el#v=onepage&q&f=false 13
Εναλλαγή περιβάλλοντος λειτουργίας (Context switch) Όταν η ΚΜΕ ανατίθεται σε μια νέα διεργασία για να συνεχίσει την εκτέλεση της, το ΛΣ πρέπει να σώσει την κατάσταση της τρέχουσας διεργασίας (η οποία εκτελείται εκείνη την στιγμή) και να φορτώσει την κατάσταση της νέας διεργασίας προς εκτέλεση. Αυτή η διαδικασία ονομάζεται εναλλαγή περιβάλλοντος λειτουργίας (context switch). O χρόνος που απαιτείται για την εκτέλεση της λειτουργίας αυτής είναι φόρτος (overhead) για το σύστημα, καθώς όσο ασχολείται με την εναλλαγή δεν εκτελεί επεξεργασίες για την εξυπηρέτηση των διεργασιών των χρηστών. Ο χρόνος καθυστέρησης για την εναλλαγή εξαρτάται: (α) από την πολυπλοκότητα του ΛΣ, και (β) την υποστήριξη από το υλικό του ΥΣ (κυρίως από τον επεξεργαστή και την μονάδα διαχείρισης μνήμης). 14
15
Η εναλλαγή περιβάλλοντος λειτουργίας γίνεται μέσω ειδικής κλήσης συστήματος, η οποία μπορεί να ενεργοποιηθεί είτε μέσα από κώδικα χειρισμού διακοπών (interrupt handler) είτε μέσα από άλλες συναρτήσεις βιβλιοθήκης ή κλήσεις συστήματος. Η ειδική αυτή κλήση εκτελείται στον χώρο μνήμης του συστήματος (kernel space) και πρώτα αποθηκεύει την κατάσταση της τρέχουσας διεργασίας (Α) στον πίνακα ελέγχου της διεργασίας αυτής στη μνήμη (user space), και στη συνέχεια φορτώνει την κατάσταση της νέας διεργασίας (Β) προς εκτέλεση χρησιμοποιώντας τις πληροφορίες που έχουν αποθηκευτεί στον αντίστοιχο πίνακα ελέγχου αυτής. Κατά την διάρκεια της εναλλαγής γίνεται και αλλαγή από την στοίβα συστήματος στην στοίβα της νέας διεργασίας, με αποτέλεσμα όταν επιστρέφει η κλήση συστήματος να συνεχίσει η εκτέλεση του κώδικα της νέας διεργασίας. 16
Πίνακας ελέγχου διεργασίας (Process Control Block, PCB) Περιέχει βασικές πληροφορίες που αφορούν την κάθε διεργασία που έχει φορτωθεί στο σύστημα (μνήμη) προς εκτέλεση. Κατάσταση διεργασίας (process state) (pid το αναγνωριστικό της διεργασίας). Μετρητής εντολών προγράμματος (program counter) Καταχωρητές της ΚΜΕ (CPU registers) Πληροφορίες για τον χρονοπρογραμματισμό της ΚΜΕ (CPU scheduling information) Πληροφορίες διαχείρισης μνήμης (memory-management information) Συλλογή στοιχείων διαχείρισης (accounting information) Πληροφορίες κατάστασης εισόδου/εξόδου (Ι/Ο status information) 17
Δημιουργία διεργασίας Όταν μια νέα διεργασία δημιουργείται είναι αντίγραφο της γονικής. Αποτελεί (λογικό) αντίγραφο του (εικονικού) χώρου διευθύνσεων της γονικής διεργασίας και εκτελεί τον ίδιο κώδικα με τη γονική, ξεκινώντας από την επόμενη εντολή που ακολουθεί την εντολή συστήματος που τη δημιουργεί, π.χ. στο Linux μετά τη fork(). Η γονική-διεργασία (αρχική) και η διεργασία-παιδί (αντίγραφο) μοιράζονται τον ίδιο κώδικα του προγράμματος (δηλ. τις ίδιες εικονικές σελίδες στη μνήμη που περιέχουν το τμήμα code/text). Το τμήμα του κώδικα code/text φορτώνεται μια φορά ως read-only έτσι ώστε να μοιράζεται με ασφάλεια (safely shared). Όμως διατηρούν διαφορετικά αντίγραφα των δεδομένων και στοίβας (δηλ. τα τμήματα Data, Stack και Heap), και έτσι οι οποιεσδήποτε αλλαγές σε αυτά αφορούν πλέον διαφορετικά δεδομένα και δεν έχουν καμία σχέση μεταξύ τους. Οι πατρικές-διεργασίες (parent processes) δημιουργούν διεργασίες-παιδιά (child processes), οι οποίες με τη σειρά τους μπορούν να δημιουργούν άλλες διεργασίες, δημιουργώντας έτσι ένα δένδρο διεργασιών (nested processes). 18
Σχηματική αναπαράσταση δημιουργίας διεργασιών στο Linux με χρήση της fork(). 19
Πηγή εικόνας: https://delightlylinux.wordpress.com/2012/06/25/what-is-pid-and-ppid/ 20
Όταν μια νέα διεργασία δημιουργείται ως αντίγραφο της γονικής, το αντίγραφο του τμήματος των δεδομένων (δηλ. οι εικονικές σελίδες στη μνήμη που περιέχουν το τμήμα των δεδομένων Data) στην πραγματικότητα δεν πραγματοποιείται αμέσως σε άλλο χώρο διευθύνσεων στη μνήμη. Ο λόγος είναι ότι η νέα διεργασία-παιδί συνήθως δεν τροποποιεί όλους τους πόρους (δεδομένα) που κληρονομεί από τον γονέα, και επιπλέον συχνά εκτελεί μια exec() η οποία και απαλείφει τον χώρο διευθύνσεών της (και ουσιαστικά και την ίδια). Οπότε αυτή η διαδικασία αντιγραφής θα είχε απλώς αποτελέσει άσκοπο φόρτο για το σύστημα. Συνήθως χρησιμοποιείται ένας μηχανισμός που αναφέρεται ως Copy On Write με τον οποίο τόσο η γονική διεργασία όσο και η διεργασία-παιδί επιτρέπεται να προσπελάζουν για ανάγνωση τις ίδιες σελίδες δεδομένων στη φυσική μνήμη, αλλά όταν οποιαδήποτε από τις δυο διεργασίες προσπαθήσει να γράψει σε μια από αυτές, τότε μόνο το σύστημα αντιγράφει τα περιεχόμενά της σε μια νέα φυσική σελίδα στη μνήμη. Copy On Write: virtual memory pages in both processes may refer to the same pages of physical memory until one of them writes to such a page: then it is copied. 21
Πριν την τροποποίηση της σελίδας δεδομένων C από το process 1. Μετά την τροποποίηση της σελίδας δεδομένων C από το process 1. Πηγή: https://www.cs.uic.edu/~jbell/coursenotes/operatingsystems/9_virtualmemory.html
Ο διαμοιρασμός των πόρων μπορεί να έχει ως εξής: Οι «γονείς και τα παιδιά» μοιράζονται όλους τους πόρους, ή Τα παιδιά παίρνουν υποσύνολο των πόρων, ή Οι πατρικές και οι θυγατρικές δεν μοιράζονται κανέναν πόρο Parent process is the original, creating, process Child process is the new process 23
Χώρος Διευθύνσεων: Η νέα διεργασία-παιδί αποτελεί αντίγραφο της διεργασίας-γονέα σε διαφορετικό χώρο διευθύνσεων (διαμοιραζόμενες το τμήμα του κώδικα, αλλά με διαφορετικό χώρο για τα δεδομένα και για τη στοίβα). Η νέα διεργασία-παιδί μοιράζεται το ίδιο τμήμα του κώδικα εκτέλεσης (code/text) αλλά δεσμεύει διαφορετικό χώρο διευθύνσεων στη μνήμη για τα δεδομένα και για τη στοίβα της (data & stack). 24
Εκτέλεση διεργασίας Οι πατρικές και οι θυγατρικές διεργασίες μπορούν να εκτελούνται ταυτόχρονα. Οι πατρικές-διεργασίες συνήθως περιμένουν (wait) τον τερματισμό των παιδιών. Οι διεργασίες-παιδιά μπορούν να εκτελούνται ταυτόχρονα* ή παράλληλα**. Οι νέες διεργασίες-παιδιά συνήθως φορτώνουν και εκτελούν (exec) κάποιο πρόγραμμα. Η διαφοροποίηση στο περιεχόμενο της εκτέλεσής τους, μεταξύ των διεργασιώνγονέων και των διεργασιών-παιδιών, επιτυγχάνεται με τη χρήση της if για τον έλεγχο του κωδικού pid της νεας διεργασίας που επιστρέφει η fork(). fork() (*) Ταυτόχρονα: εναλλάσονται κατά την εκτέλεσή τους στο χρόνο (π.χ. η υπερνημάτωση σε ένα πυρήνα) (**) Παράλληλα: εκτελούνται παράλληλα στο χρόνο (π.χ. σε διαφορετικούς πυρήνες/επεξεργαστές)
Κλήσεις συστήματος για τη διαχείριση διεργασιών στο Unix Με την κλήση συστήματος fork() δημιουργούνται νέες διεργασίες. Με την κλήση συστήματος exec() καλείται και εκτελείται μια άλλη διεργασία. Προσοχή: γίνεται αντικατάσταση του χώρου μνήμης της τρέχουσας διεργασίας (που την καλεί) με τη νέα διεργασία που καλείται. Με την κλήση συστήματος wait() η διεργασία-γονέας περιμένει (blocks) έως ότου κάποια από τις διεργασίεςπαιδιά τερματίζει exits (ή λαμβάνει κάποιο signal). Η wait() επιστρέφει το pid της διεργασίας που ολοκληρώθηκε. Με την κλήση συστήματος clone() δημιουργούνται νέα νήματα εκτέλεσης συστήματος (kernel threads of control). 26 The wait() is one of the two techniques used to monitor child processes: along with the SIGCHLD signal.
Παραδείγματα pid - ο κωδικός της διεργασίας (Process ID) By default, the maximum PID number is 32.767. In 64-bit architectures, can be up to 4.194.303. Η fork() επιστρέφει: την τιμή του νέου pid στην αρχική διεργασία-γονέα, και την τιμή 0 στη νέα διεργασία-παιδί. Η εκτέλεση της κλήσης fork() σε μια διεργασία (γονέας) δημιουργεί μια νέα πανομοιότυπη διεργασία (παιδί), σε διαφορετικό (εικονικό) χώρο διευθύνσεων (virtual address space), διαμοιραζόμενες το τμήμα του κώδικα (text/code), αλλά με διαφορετικό χώρο για το αντίγραφο των δεδομένων και για τη στοίβα. Συνεπώς, τυχόν αλλαγές στις παραμέτρους σε οποιαδήποτε από τις δύο διεργασίες δεν επηρεάζουν τις παραμέτρους στην άλλη. 27 pid 768 pid 767
Πηγή εικόνας: http://www.fmc-modeling.org/category/projects/apache/amp/a_1_unix_processes.html 28
Παράδειγμα κώδικα δημιουργίας διεργασίας με την κλήση fork() Η νέα διεργασία (διεργασία-παιδί) που δημιουργείται με την fork() αποτελεί αντίγραφο της γονικής διεργασίας (διεργασία-γονέας). Με τη χρήση της if για τον έλεγχο του pid (που επιστρέφει η fork()) επιτυγχάνεται η διαφοροποίηση στο περιεχόμενο της εκτέλεσής τους. 29
child is created here t 0 Η αρχική διεργασία γονέας συνεχίζει εδώ Η νέα διεργασία παιδί ξεκινά εδώ Πηγή εικόνας: www.csl.mtu.edu/cs4411.ck/www/notes/process/fork/create.html 30
parent continues execution here Parent Process t 0 t1 Πηγή εικόνας: https://www.cs.swarthmore.edu/~newhall/cs85/s08/hw02.html 31
Παράδειγμα δημιουργίας ενός απλού shell Ο φλοιός (shell) ή διερμηνέας εντολών (console/terminal) χρησιμοποιεί τη fork() για να δημιουργήσει μια διεργασία-παιδί που θα εκτελεί τις εντολές που δέχεται. Για κάθε εντολή, π.χ. ls, ο shell δημιουργεί με την fork() μια νέα διεργασία-παιδί. Στη συνέχεια, αυτή η νέα-διεργασία εκτελεί με χρήση της exec() την εντολή που δόθηκε, π.χ. την ls, αντικαθιστώντας τον εαυτό της στη μνήμη με την ls. Unix operating systems rely heavily on process creation to satisfy user requests. For example, the shell creates a new process that executes another copy of the shell whenever the user enters a command. 32
Ενδεικτικός συμβολικός κώδικας δημιουργίας ενός απλού shell Ερώτηση: στο παράδειγμα αυτό για ποιο λόγο είναι απαραίτητη η χρήση της fork(); 33
Παράδειγμα εκτέλεσης της εντολής ls Η διεργασία γονέας έχει PID=501 και η διεργασία παιδί έχει PID=748. 34
Τερματισμός διεργασίας Διακρίνονται οι ακόλουθες τρεις περιπτώσεις: 1. Η διεργασία εκτελεί την τελευταία εντολή και καλεί το ΛΣ (exit) Μεταφορά πληροφοριών προς την διεργασία-γονέα (μέσω wait). Οι πόροι της διεργασίας επανέρχονται στη διάθεση του ΛΣ. 2. Η γονική διεργασία μπορεί να διακόψει τη θυγατρική (abort) όταν: Η θυγατρική έχει υπερβεί τους πόρους που της είχαν ανατεθεί. Η εργασία που ανατέθηκε στη διεργασία-παιδί δεν χρειάζεται άλλο. 3. Η διεργασία-γονέας τερματίζει την εκτέλεση της, τότε σε αυτή την περίπτωση: To ΛΣ δεν επιτρέπει στη διεργασία-παιδί να συνεχίσει. Ενδέχεται να συμβούν διαδοχικοί τερματισμοί (για όλα τα παιδιά). 35
Δένδρο διεργασιών στο Unix Μια διεργασία που εκτελείται αλλά ο γονέας της έχει τερματιστεί αποτελεί orphan process την οποία αναλαμβάνει και διαχειρίζεται τον τερματισμό της η διεργασία init (με process id 1) του συστήματος. Σε περίπτωση που κάποια διεργασία-παιδί τερματίσει πριν ακόμα η διεργασία-γονέας εκτελέσει την wait(), τότε το σύστημα την μετατρέπει σε zombie process, δηλ. απελευθερώνει σχεδόν όλους τους πόρους που κατείχε, και ολοκληρωτικά μόλις η διεργασία-γονέας εκτελέσει την wait() (για να πληροφορηθεί πως τερμάτισε η διεργασία-παιδί). 36
Πηγή εικόνας: www.lynx.com/the-fork-call-posix-processes-and-parent-child-relationships/ 37
Παραδείγματα διεργασιών Εμφάνιση όλων των διεργασιών στο linux: ps -ef 38
Status code (STAT) Description Πηγή: www.bogotobogo.com/linux/linux_process_and_signals.php 39
Παράδειγμα δημιουργίας νέας διεργασίας στο οποίο η αρχική διεργασίαγονέας περιμένει τη νέα διεργασία-παιδί να τερματίσει. #include <stdio.h> main(){ int pid, status, chpid; pid = fork(); if (pid!=0) /* ΚΩΔΙΚΑΣ ΔΙΕΡΓΑΣΙΑΣ ΓΟΝΕΑ */ { printf( Διεργασία γονέας με PID %d και PPID %d\n,getpid(),getppid()); chpid=wait(&status); /* Επιστρέφει το PID της διεργασίας παιδί που τερματίζεται και τοποθετεί ένα κωδικό κατάστασης τερματισμού στη status*/ printf( Η διεργασία-παιδί με PID %d τερμάτισε με κωδικό εξόδου\n,pid,status); } else /* ΚΩΔΙΚΑΣ ΔΙΕΡΓΑΣΙΑΣ ΠΑΙΔΙ */ { printf( Διεργασία παιδί με PID %d και PPID %d\n,getpid(), getppid()); exit(42); } printf( Η διεργασία με PID %d τερματίζει \n,getpid()); } 40
Μεταγλώττιση & διασύνδεση Η μεταγλώττιση (compiling) ενός προγράμματος (σε CPU/machine-specific instructions & data specifications), και ουσιαστικά στη συνέχεια η διασύνδεση (linking) των διαφόρων object files & libraries, δημιουργεί το εκτελέσιμο αρχείο (executable binary) (π.χ. στο Linux στη μορφή ELF, Executable and Linking Format*), το οποίο εμπεριέχει: σε ένα τμήμα text/code όλες τις εντολές (instructions), και σε ένα άλλο τμήμα τα δεδομένα, το οποίο διαχωρίζεται σε δυο επιμέρους τμήματα που ονομάζονται data, για τα αρχικοποιημένα στατικά δεδομένα (initialized static data & literal constants), και bss, για τα μη αρχικοποιημένα (uninitialized static data). (*) Το αρχείο ELF εμπεριέχει όλες τις απαραίτητες πληροφορίες για τη φόρτωση του εκτελέσιμου κώδικα και τα σχετικά δεδομένα του προγράμματος στην (εικονική) μνήμη της διεργασίας. Φόρτωση & εκτέλεση Κατά την εκτέλεση, τα τμήματα του κώδικα και των δεδομένων φορτώνονται (loading) σε ξεχωριστούς χώρους διευθύνσεων της (εικονικής) μνήμης. Συνήθως ο κώδικας (text/code) δεσμεύει τις χαμηλότερες διευθύνσεις με τα τμήματα δεδομένων (data, bss) και τη στοίβα (stack & heap) σε υψηλότερες θέσεις, με τα κατάλληλα δικαιώματα (π.χ. το τμήμα του κώδικα είναι read-execute, ενώ τα τμήματα των δεδομένων είναι read-write). 41 Ενημερωτική προαιρετική διαφάνεια
Σχηματική αναπαράσταση των διαδικασιών δημιουργίας και φόρτωσης του εκτελέσιμου Τα εκτελέσιμα αρχεία (executable image) δεν είναι αναγκαίο να φορτώνονται εξολοκλήρου στη μνήμη, παρά μόνο όποτε απαιτούνται από τη διεργασία (demand loading/paging). Πηγή εικόνας: www.pinterest.com/pin/405535141431055207/ 42 Ενημερωτική προαιρετική διαφάνεια Με την τεχνική αυτή η εικονική μνήμη (σελίδες) φορτώνεται στη φυσική μνήμη (πλαίσια σελίδων), μόνο όταν η διεργασία επιχειρεί να τη χρησιμοποιήσει.
Παράδειγμα Π.χ. στο Linux, όταν φορτώνει το ELF (executable image) στον εικονικό χώρο διευθύνσεων της διεργασίας (process's virtual address space), στην πραγματικότητα δεν το φορτώνει στη φυσική μνήμη (π.χ. τα τμήματα code & data), αλλά προσδιορίζει στον πυρήνα τις απαραίτητες δομές που περιγράφουν τη διεργασία, όπως τις δομές mm_struct, vm_area_struct και τους πίνακες σελίδων της (page tables), με τους οποίους γίνεται η αντιστοίχισή του (χαρτογράφησή του) στη φυσική μνήμη (virtual to physical memory mapping), όπου και δηλώνει ότι υφίσταται αλλά δεν έχει φορτωθεί ακόμα. Όταν η διεργασία εκτελείται και κάνει αναφορές (μέσα από την εικονική μνήμη) στον κώδικα και τα δεδομένα της, τότε εξαναγκάζεται (προκύπτουν page faults) να τα φορτώσει στη φυσική μνήμη. Φυσικά τμήματα του κώδικα που δεν χρησιμοποιηθούν δεν θα φορτωθούν ποτέ στη φυσική μνήμη. Τα περιεχόμενα του εικονικού χώρου διευθύνσεων της κάθε διεργασίας περιγράφονται από τη δομή mm_struct της δομής task_struct. Πηγή: https://www.tldp.org/ldp/tlk/kernel/processes.html 43 Ενημερωτική προαιρετική διαφάνεια
Ο εικονικός χώρος διευθύνσεων μιας διεργασίας και οι δομές του πυρήνα που την διαχειρίζονται The contents of each process's virtual memory is described by a mm_struct data structure pointed at from its task_struct. The process's mm_struct data structure also contains information about the loaded executable image and a pointer to the process's page tables. It contains pointers to a list of vm_area_struct data structures, each representing an area of virtual memory within this process. This linked list is in ascending virtual memory order. When a process allocates virtual memory, Linux does not actually reserve physical memory for the process. Instead, it describes the virtual memory by creating a new vm_area_struct data structure. This is linked into the process's list of virtual memory. When the process attempts to write to a virtual address within that new virtual memory region then the system will page fault. The processor will attempt to decode the virtual address, but as there are no Page Table Entries for any of this memory, it will give up and raise a page fault exception, leaving the Linux kernel to fix things up. Linux looks to see if the virtual address referenced is in the current process's virtual address space. If it is, Linux creates the appropriate PTEs and allocates a physical page of memory for this process. The code or data may need to be brought into that physical page from the filesystem or from the swap disk. The process can then be restarted at the instruction that caused the page fault and, this time as the memory physically exists, it may continue. 44 Ενημερωτική προαιρετική διαφάνεια
Παράδειγμα των τμημάτων του εκτελέσιμου μιας διεργασίας στη μνήμη Πηγή εικόνας: https://whitedome.com.au/re4son/slae-5-1-linuxx86meterpreterreverse_tcp-shellcode-analysis/ 45 Ενημερωτική προαιρετική διαφάνεια
Τυπική μορφή του εκτελέσιμου ELF Οι πίνακες (header tables) στο εκτελέσιμο (executable image) περιγράφουν πως θα γίνει η τοποθέτησή του στην (εικονική) μνήμη της διεργασίας. Το τμήμα Segment/Program header περιγράφει τα segments που χρησιμοποιούνται κατά την εκτέλεση (at run time). Επιπλέον περιλαμβάνει το σημείο εκκίνησης της εκτέλεσης του προγράμματος (διεύθυνση της πρώτης εντολής προς εκτέλεση, στο τμήμα.init). Το τμήμα Section header υποδεικνύει το σύνολο των sections του εκτελέσιμου (binary). Πηγή: https://huizistar.wordpress.com/2013/05/09/computer-system-chapter-7/ 46 Ενημερωτική προαιρετική διαφάνεια
Πηγή: http://nairobi-embedded.org/040_elf_sec_seg_vma_mappings.html 47 Ενημερωτική προαιρετική διαφάνεια
Τυπική μορφή των τμημάτων του εκτελέσιμου μιας διεργασίας στη μνήμη On 32-bit Linux systems, the code segment starts at address 0x08048000. Το text segment εμπεριέχει τον κώδικα σε εκτελέσιμη μορφή (binary image) της διεργασίας. Το data segment εμπεριέχει αρχικοποιημένες στατικές (static) μεταβλητές. Το bss segment εμπεριέχει μη αρχικοποιημένες στατικές (static) και σφαιρικές (global) μεταβλητές. Τη στοίβα (stack segment) για τα ορίσματα των συναρτήσεων, επιστροφές και τοπικές (automatic/local) μεταβλητές. Το σωρό (heap segment) (ελεύθερος χώρος) για τη δυναμική δέσμευση μνήμης. Επιπλέον, ένα τμήμα environment (συνήθως unmapped to user code) εμπεριέχει μεταβλητές περιβάλλοντος (environment variables) και ορίσματα γραμμής εντολών (command line arguments). Το μέγεθος των τμημάτων text+data+bss μιας διεργασίας προσδιορίζεται κατά τη μεταγλώττιση και παραμένει σταθερό κατά την εκτέλεση. Όμως μια διεργασία μπορεί κατά την εκτέλεση δυναμικά να επεκταθεί στο ελεύθερο χώρο (εικονικής) μνήμης του σωρού (π.χ. χρήση της malloc() στη C). Πηγή: https://huizistar.wordpress.com/2013/05/09/computer-system-chapter-7/ Ενημερωτική προαιρετική διαφάνεια
Παράδειγμα Πηγή: https://proprogramming.org/memory-layout-of-c-program/ 49 Ενημερωτική προαιρετική διαφάνεια
50 Ενημερωτική προαιρετική διαφάνεια
Παράδειγμα On 32-bit Linux systems, the code segment starts at address 0x08048000, while on 64-bit at 0x00400000. The data segment follows at the next 4 KB aligned address. The run-time heap follows on the first 4 KB aligned address past the read/write segment and grows up via calls to the malloc library. "Text Segment" "Data Segment" "BSS Segment" Πηγή: https://gist.github.com/cmcdragonkai/10ab53654b2aa6ce55c11cfc5b2432a4 51 Ενημερωτική προαιρετική διαφάνεια
Παράδειγμα Πηγή εικόνας: http://perugini.cps.udayton.edu/teaching/books/spuc/www/lecture_notes/processes.html 52 Ενημερωτική προαιρετική διαφάνεια
Πηγή: http://www.cs.uleth.ca/~holzmann/c/system/ Ενημερωτική προαιρετική διαφάνεια
Shared libraries Linux processes use libraries of commonly useful code, for example file handling routines. It does not make sense that each process has its own copy of the library, Linux uses shared libraries that can be used by several running processes at the same time. The code and the data from these shared libraries must be linked into this process's virtual address space and also into the virtual address space of the other processes sharing the library. A shared library is an object module that, at run time, can be loaded at an arbitrary memory address and linked with a program in memory. This process is known as dynamic linking and is performed by a program called a dynamic linker. Shared libraries are shared in two different ways. First, in any given file system, there is exactly one.so file for a particular library. Second, a single copy of the.text section of a shared library in memory can be shared by different running processes. Linking can be performed at compile time by static linkers, and at load time and run time by dynamic linkers. Linkers manipulate binary files called object files, which come in three different forms: relocatable, executable, and shared. Relocatable object files are combined by static linkers into an executable object file that can be loaded into memory and executed. Shared object files (shared libraries) are linked and loaded by dynamic linkers at run time, either implicitly when the calling program is loaded and begins executing, or on demand, when the program calls functions from the dlopen library. Πηγή: https://www.tldp.org/ldp/tlk/kernel/processes.html Πηγή: https://huizistar.wordpress.com/2013/05/09/computer-system-chapter-7/ 54 Ενημερωτική προαιρετική διαφάνεια
So, a dynamically linked image, does not contain all of the code and data required to run. Some of it is held in shared libraries that are linked into the image at run time. The ELF shared library's tables are also used by the dynamic linker when the shared library is linked into the image at run time. Linux uses several dynamic linkers, ld.so.1, libc.so.1 and ldlinux.so.1, all to be found in /lib. The libraries contain commonly used code such as language subroutines. Without dynamic linking, all programs would need their own copy of the these libraries and would need far more disk space and virtual memory. In dynamic linking, information is included in the ELF image's tables for every library routine referenced. The information indicates to the dynamic linker how to locate the library routine and link it into the program's address space. 55 Ενημερωτική προαιρετική διαφάνεια
Ταυτόχρονες συντρέχουσες διεργασίες (Simultaneous concurrent processes) Οι κλήσεις (ρουτίνες) του Λ.Σ. θα πρέπει να μπορούν να ανακαλούνται προς εκτέλεση (reentrant*), δηλ. να επιτρέπουν στους επεξεργαστές (ουσιαστικά τα νήματα) να εκτελούν ταυτόχρονα τον κώδικα της ίδιας ροής εντολών, χωρίς να προκαλούνται αδιέξοδα ή άλλες μη έγκυρες λειτουργίες. (*) Reentrant code a computer program or routine that is written so that the same copy in memory can be shared by multiple users (actors/threads). The program can be interrupted to give another user a turn to use the program. It can be interrupted in the middle of its execution (called by another actor/thread), before its previous invocation complete execution, and then be safely called again ("re-entered") and complete correctly its execution. That is, it is possible to re-enter the code while it's already running and still produce correct results. Reentrant code means that a program can have more than one thread executing concurrently. 56 Ενημερωτική προαιρετική διαφάνεια
Ερωτήσεις Περιγράψτε τι είναι η διεργασία (process) και τις καταστάσεις που μπορεί να βρεθεί μια διεργασία. Δίνονται οι ακόλουθες περιπτώσεις αλλαγών στις καταστάσεις μιας διεργασίας: (i) running -> waiting, (ii) running -> ready, (iii) waiting -> ready, (iv) waiting -> running, (v) ready -> running Αιτιολογείστε πότε συμβαίνουν (αν συμβαίνουν) οι αλλαγές στις καταστάσεις της διεργασίας για τις παραπάνω περιπτώσεις. Περιγράψτε τι γνωρίζετε για την εναλλαγή περιβάλλοντος λειτουργίας (context switch). Περιγράψτε το περιεχόμενο και τη χρησιμότητα του πίνακα ελέγχου διεργασίας (PCB - Process Control Block). Περιγράψτε τι γνωρίζετε για τη δημιουργία, εκτέλεση και τερματισμό των διεργασιών. Η εκτέλεση της κλήσης fork σε μια διεργασία (γονική) δημιουργεί μια νέα διεργασία (θυγατρική). Εξηγήστε αν οι τυχόν επερχόμενες αλλαγές στις παραμέτρους σε μια από τις δύο διεργασίες επηρεάζουν τις παραμέτρους στην άλλη και γιατί; 57