Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε

Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε

Linux Process Scheduling

Παρόμοιες παρουσιάσεις


Παρουσίαση με θέμα: "Linux Process Scheduling"— Μεταγράφημα παρουσίασης:

1 Linux Process Scheduling
Student: KARPOUZIS ATHANASIOS

2 Διεργασία / Process (1) Μία διεργασία (process) είναι στην ουσία μία συλλογή από δομές δεδομένων που περιγράφουν πόσο μακριά έχει φτάσει κάποιο πρόγραμμα που εκτελείται. Μοιάζουν στην ουσία με τους ανθρώπους, γεννιούνται, έχουν κάποιο χρόνο ζωής, έχουν την δυνατότητα να δημιουργήσουν απογόνους – νέες διεργασίες και τελικά πεθαίνουν (κύκλος ζωής). Μια διεργασία είναι: Ένα πρόγραμμα σε εκτέλεση Μια ασύγχρονη δραστηριότητα, Μια εκτελούμενη διαδικασία που συσχετίζεται με την ύπαρξη μιας δομής δεδομένων στο ΛΣ, τον περιγραφέα διεργασίας (process descriptor) ή μπλοκ ελέγχου διεργασίας (Process Control Block).

3 Διεργασία / Process (2) Μια διεργασία είναι μια οντότητα με τον δικό της χώρο διευθύνσεων, που αποτελείται από: την περιοχή κώδικα (text region), περιέχει τον κώδικα που εκτελεί ο επεξεργαστής Την περιοχή δεδομένων (data region), όπου αποθηκεύονται οι μεταβλητές και η δυναμικά παραχωρούμενη μνήμηπου χρησιμοποιεί ο επεξεργαστής κατά τη διάρκεια της εκτέλεσης Την περιοχή στοίβας (stack region), όπου αποθηκεύονται οι εντολές και οι τοπικές μεταβλητές για τις ενεργές κλήσεις διαδικασιών

4 Διεργασία / Process (3) Οι διεργασίες μπορούν να προχωρήσουν μόνον όταν υπάρχει το μέσον που θα εκτελέσει τα σχετικά προγράμματα. Αυτό το μέσο είναι ο επεξεργαστής (processor). Ανάλογα με τη φύση των εντολών, ο επεξεργαστής μπορεί να υλοποιηθεί αποκλειστικά με υλικό ή με συνδυασμό υλικού και λογισμικού. Παράδειγμα: μια CPU είναι ένας επεξεργαστής για εκτέλεση εντολών γλώσσας μηχανής, ενώ μια CPU μαζί με έναν διερμηνευτή (interpreter) μιας γλώσσας προγραμματισμού συνθέτουν έναν επεξεργαστή που εκτελεί εντολές της συγκεκριμένης γλώσσας προγραμματισμού.

5 Διεργασία / Process (4) Ένας επεξεργαστής εκτελεί εντολές μηχανής που βρίσκονται στην κύρια μνήμη σε μορφή προγράμματος. Για να εκτελεσθεί ένα πρόγραμμα δημιουργείται μια διεργασία για το συγκεκριμένο πρόγραμμα. Κατά τη διάρκεια του χρόνου οι εντολές προγράμματος που εκτελούνται δημιουργούν μια λίστα που ονομάζεται αποτύπωση της διεργασίας. Ένας επεξεργαστής μπορεί να απασχοληθεί σε διαφορετικές διεργασίες μέσω ενός αλγορίθμου χρονοδρομολόγησης (scheduling) που καθορίζει πότε και ποια διεργασία θα έχει κάθε φορά το δικαίωμα αποκλειστικής χρήσης του. Το ζητούμενο είναι να εξισορροπηθούν οι ανταγωνιστικές απαιτήσεις για την αποδοτικότητα (efficiency) όλου του συστήματος και για τις σχέσεις δικαιοσύνης (fairness) μεταξύ των διεργασιών.

6 Scheduler (Χρονοδρομολογητής)

7 Χρονοδρομολογητής (1) Όταν μια διεργασία βρίσκεται σε μια ουρά το Λ.Σ. με κάποιον τρόπο πρέπει να επιλέξει μια διεργασία από την ουρά, η λειτουργία αυτή επιτελείται από έναν χρονο-δρομολογητή. Long-term scheduler (or job scheduler) – αποφασίζει και επιλέγει τις διεργασίες που θα περιληφθούν στην ουρά των έτοιμων προς εκτέλεση διεργασιών (ready queue). Ελέγχει τον βαθμό πολυπρογραμματισμού. Short-term scheduler (or CPU scheduler) – επιλέγει ποια διεργασία θα είναι η επόμενη προς εκτέλεση και εκχωρεί τη χρήση της CPU. Medium-term scheduler – χρησιμοποιείται ειδικά σε time-sharing συστήματα ως ένα ενδιάμεσο επίπεδο χρονοδρομολόγησης. Χρησιμοποιεί ένα σχήμα εναλλαγής που μετακινεί περιοδικά διεργασίες από τη μνήμη και τις εγκαθιστά αργότερα για να συνεχίσουν από το σημείο που είχαν σταματήσει.

8 Χρονοδρομολογητής (2)

9 Περιγραφέας Διεργασίας (Process Descriptor)

10 Περιγραφέας Διεργασίας (Process Descriptor) (1)
Πρόκειται για μία δομή που διατηρεί πληροφορίες όσον αφορά την προτεραιότητα της διεργασίας, των χώρο διευθύνσεων που της αντιστοιχεί (address space), την κατάσταση της διεργασίας κτλ. Στην υλοποίηση του λειτουργικού συστήματος Linux είναι η struct task_struct. struct task_struct { volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ void *stack; atomic_t usage; unsigned int flags; /* per process flags, defined below */ unsigned int ptrace; 1123 int lock_depth; /* BKL lock depth */ 1125 1126#ifdef CONFIG_SMP 1127#ifdef __ARCH_WANT_UNLOCKED_CTXSW int oncpu; 1129#endif 1130#endif 1131 int prio, static_prio, normal_prio; unsigned int rt_priority; const struct sched_class *sched_class; struct sched_entity se; struct sched_rt_entity rt; ……………….

11 Περιγραφέας Διεργασίας (Process Descriptor) (2)
Η δομή task_struct.

12 Περιγραφέας Διεργασίας (Process Descriptor) (3) Κατάσταση Διεργασίας (Process State)
Κάθε φορά η διεργασία μπορεί να έχει μόνο μία κατάσταση, η οποία μπορεί να αλλάζει. Μαζί με την κατάσταση της διεργασίας υπάρχεις και η κατάσταση εξόδου της διεργασίας σε ξεχωριστό πεδίο.

13 Περιγραφέας Διεργασίας (Process Descriptor) (4) Κατάσταση Διεργασίας (Process State)
Καταστάσεις Διεργασίας: TASK_RUNNING Η διεργασία τρέχει στον επεξεργαστή η περιμένει στην ουρά για να εκτελεστεί. TASK_INTERRUPTIBLE Η διεργασία κοιμάται, μέχρι κάποιο σήμα που προέρχεται από μία κατάσταση που άλλαξε, να την ξυπνήσει. Η διεργασία μπορεί να περίμενε κάποιους πόρους, ή να έγινε κάποιο hardware interrupt, ή κάποιο άλλο σήμα. Γυρίζει σε κατάσταση TASK_RUNNING. TASK_UNINTERRUPTIBLE Όπως η παραπάνω κατάσταση με την διαφορά ότι κάποιο σήμα δεν ξυπνάει την διεργασία και παραμένει μπλοκαρισμένη. Γενικά είναι σπάνιο να βρεθεί διεργασία σε αυτή την κατάσταση.

14 Περιγραφέας Διεργασίας (Process Descriptor) (5) Κατάσταση Διεργασίας (Process State)
Καταστάσεις Διεργασίας: TASK_STOPPED H εκτέλεση της διεργασία σταματάει, η διεργασία σταματά κατόπιν κάποιων από αυτών των σημάτων SIGSTOP SIGTSTP SIGTTIN SIGTTOU TASK_TRACED Η εκτέλεση μια διεργασίας σταμάτησε από τον debugger. Όταν μια διεργασία παρακολουθείται από κάποια άλλη όπως πχ ένας debugger με την ptrace() κλήση συστήματος, κάποιο σήμα μπορεί να φέρει την διεργασία σε αυτή την κατάσταση.

15 Περιγραφέας Διεργασίας (Process Descriptor) (5α) Κατάσταση Διεργασίας (Process State)

16 Περιγραφέας Διεργασίας (Process Descriptor) (5β) Στιγμιότυπο Εκτέλεσης

17 Περιγραφέας Διεργασίας (Process Descriptor) (6) Κατάσταση Εξόδου Διεργασίας (Process State)
Καταστάσεις εξόδου (τερματισμού) διεργασίας: EXIT_ZOMBIE Η διεργασία τερματίστηκε αλλά η διεργασία πατέρας δεν έχει εκτελέσει την κλήση συστήματος waitpid ή wait ώστε να επιστρέψει πληροφορίες για την νεκρή διεργασία. Πριν ολοκληρωθεί η κλήση αυτή συστήματος ο πυρήνας δεν μπορεί να ελευθερώσει / απομακρύνει δεδομένα που αφορούν την νεκρή διεργασία γιατί ο πατέρας μπορεί να τα χρειάζεται. EXIT_DEAD Τελική κατάσταση, η διεργασία αφαιρείται από το σύστημα γιατί ο πατέρας πήρε αποτέλεσμα από την κλήση συστήματος waitpid ή wait.

18 Περιγραφέας Διεργασίας (Process Descriptor) (7) Αναγνωρίζοντας μία Διεργασία
Κάθε διεργασία έχει δείκτες σε περιγραφέα διεργασιών με μέγεθος 32-bit Κάθε διεργασία έχει το δικό της αναγνωριστικό διεργασίας (Process ID (PID)) 16-bit (~32767 για συμβατότητα) Το σύστημα Linux αντιστοιχεί διαφορετικά PID με κάθε διεργασία. Για να πάρουμε το αναγνωριστικό της διεργασίας χρησιμοποιούμε την κλήση συστήματος getpipd() και για να πάρουμε το αναγνωριστικό της διεργασίας πατέρα χρησιμοποιούμε την κλήση συστήματος getppid().

19 Περιγραφέας Διεργασίας (Process Descriptor) (8) Διάγραμμα Καταστάσεων στο Linux (Process State)

20 Αλλαγή Διεργασίας (Process Switch)

21 Αλλαγή Διεργασίας (Process Switch) (1)
Ο πυρήνας μπορεί να μπλοκάρει την εκτέλεση μίας διεργασίας που τρέχει στον επεξεργαστή ώστε να συνεχίσει την εκτέλεση κάποιας άλλης που προηγουμένως είχε μπλοκαριστεί. Όλες οι διεργασίες μοιράζονται τους καταχωρητές του επεξεργαστή αλλά έχουν τον δικό τους χώρο διευθύνσεων (address space). Πριν συνεχίσει ο επεξεργαστής μία διεργασία που είχε μπλοκαριστεί πρέπει να φορτωθούν στους καταχωρητές οι τιμές που αφορούσαν την διεργασία αυτή. Το σύνολο των δεδομένων που πρέπει να φορτωθούν στους καταχωρητές πριν συνεχίσει η διεργασία ονομάζεται hardware context. Στο Linux ένα τμήμα του hardware context βρίσκεται στον file descriptor ενώ το υπόλοιπο αποθηκεύεται σε μία άλλη δομή την Kernel Mode Stack.

22 Αλλαγή Διεργασίας (Process Switch) (2)
Θα χρησιμοποιήσουμε δύο μεταβλητές: Prev: τοπική μεταβλητή που αναφέρεται στον file descriptor της διεργασίας που βγαίνει. Next: τοπική μεταβλητή που αναφέρεται στην διεργασία που μπαίνει για να εκτελεστεί. Η αλλαγή διεργασίας συμβαίνει μόνο σε επίπεδο πυρήνα με τα ανώτερα δικαιώματα. Οι παλαιότερες εκδόσεις του Linux εκμεταλλεύονται από την υποστήριξη σε υλικό που έδινε η αρχιτεκτονική 80x86 και μπορούσαν να κάνουν την αλλαγή διεργασίας με μία far jmp εντολή.

23 Αλλαγή Διεργασίας (Process Switch) (3)
Η αλλαγή της διεργασίας γίνεται βηματικά με ένα σύνολο από mov (move) εντολές οι οποίες εξασφαλίζουν την εγκυρότητα των δεδομένων που φορτώνονται. Ειδικά ελέγχονται οι τιμές των καταχωρητών ds, es που μπορεί να έχουν πειραχτεί από έναν κακόβουλο χρήστη. Αυτό το είδος ελέγχου δεν είναι δυνατό όταν πραγματοποιείται αλλαγή με την εντολή far jmp που αναφέρθηκε. Το σύνολο του χρόνου που απαιτείται τόσο με την παλιά όσο και με την νέα μέθοδο είναι σχεδόν ο ίδιος. Οι τιμές των καταχωρητών που χρησιμοποιούνται από την διεργασία σε επίπεδο χρήστη αποθηκεύονται όλες στην Kernel Mode Stack πριν γίνει η αλλαγή διεργασίας.

24 Αλλαγή Διεργασίας (Process Switch) (4) Εκτελώντας την Αλλαγή Διεργασίας
Γίνεται σε δύο (2) βήματα: Αλλάζοντας το Page Global Directory για να εγκατασταθεί ένα νέο address space. Αλλάζοντας το Kernel Mode Stack και το hardware context που διαθέτουν όλες τις πληροφορίες που χρειάζονται για να εκτελέσει ο πυρήνας την νέα διεργασία. (καταχωρητές επεξεργαστή) Το δεύτερο βήμα γίνεται μέσω ενός macro του switch_to macro το οποίο δέχεται τρεις (3) παραμέτρους: prev, next, last. Η τελευταία παράμετρος χρησιμοποιείται ώστε να διατηρήσει μία αναφορά στην διεύθυνση του file descriptor της τελευταίας ενεργής διεργασίας, ώστε να μπορεί ο πυρήνας να γράφει στους καταχωρητές τα δεδομένα που χρειάζεται στο process switching.

25 Αλλαγή Διεργασίας (Process Switch) (5) Εκτελώντας την Αλλαγή Διεργασίας
Η αλλαγή από την διεργασία Α->Β, διατηρεί την πληροφορία για την Α διεργασία στην διεύθυνση μνήμης που βρίσκεται η last μεταβλητή, ώστε να μπορούν να γραφτούν στον eax καταχωρητή τα δεδομένα για την τελευταία διεργασία που έχει μπλοκαριστεί.

26 Πολιτική Δρομολόγησης (Scheduling Policy)

27 Πολιτική Δρομολόγησης (1)
H πολιτική δρομολόγησης του Linux βασίζεται σε time sharing technique. Πολλαπλές διεργασίες τρέχουν με τεχνικές πολυπλεξίας χρόνου καθώς ο επεξεργαστής διαιρείται σε τμήματα (slices), ένα για κάθε διεργασία που πρόκειται να εκτελεστεί. Διακρίνουμε τρεις (3) κλάσεις από διεργασίες: Interactive Processes Batch Processes Real – time Processes

28 Πολιτική Δρομολόγησης (2)
Interactive Processes: Ασχολούνται συνεχώς με τους χρήστες και σπαταλούν πολύ χρόνο περιμένοντας για events όπως το πάτημα κουμπιών, κλικ ποντικιού κτλ. Όταν λάβουν είσοδο η διεργασία πρέπει να ξυπνήσει άμεσα, αλλιώς το σύστημα δεν θα ανταποκρίνεται στις αιτήσεις. Batch Processes: Δεν χρειάζονται την παρέμβαση του χρήστη και κυρίως τρέχουν στο παρασκήνιο εκτελώντας σημαντικές για το σύστημα διαδικασίες. Real – Time Processes: Έχουν μεγάλη σημασία για το σύστημα, δεν μπορούν να μπλοκαριστούν από χαμηλότερης προτεραιότητας διεργασίες και πρέπει να τρέχουν όσο το δυνατόν συντομότερα. (video or audio processing)

29 Πολιτική Δρομολόγησης (3) Κλήσεις Συστήματος που αφορούν την δρομολόγηση

30 Πολιτική Δρομολόγησης (4)
Αλλαγή διεργασίας για δύο (2) λόγους: Διεργασία με μεγαλύτερη προτεραιότητα από την τρέχουσα διεργασία Αλλαγή διεργασίας όταν λήξει το κβάντο χρόνου που της έχει δοθεί από το σύστημα (time quantum). Όταν συμβαίνει αυτό το TIF_NEED_RESCHED πεδίο στην δομή thread_info παίρνει λογική τιμή true οπότε ο χρονοδρομολογητής αναλαμβάνει την αλλαγή. Οι διεργασίες στο Linux είναι preemptive ενώ σε αντίθεση με την προηγούμενη έκδοση την 2.5 που δεν είναι preemptive. Preemptive: Το να μπορείς να τρέχεις πολλαπλές διεργασίες / νήματα στον ίδιο επεξεργαστή δίνοντας την εντύπωση πως τρέχουν παράλληλα, η κάθε μία στο κβάντο χρόνου που της αντιστοιχεί.

31 Πολιτική Δρομολόγησης (5) Thread info Structure

32 Αλγόριθμος Δρομολόγησης (Scheduling Algorithm)

33 Πότε συμβαίνει μία δρομολόγηση (1)

34 Πότε συμβαίνει μία δρομολόγηση (2)
Χρειαζόμαστε όλους τους επεξεργαστές στο σύστημα να δουλεύουν Συνήθως έχουμε πολλές διεργασίες που είναι στην ουρά των έτοιμων προς εκτέλεση διεργασιών και περιμένουν τον επεξεργαστή. Δεν πρέπει να καταναλώνει μία διεργασία πλήρως τον επεξεργαστή για μεγάλο χρονικό διάστημα. Ο πυρήνας του Linux 2.6 είναι preemptive αλλά χρησιμοποιεί spinlocks(SMP), έχει δε την δυνατότητα να απενεργοποιεί την δυνατότητα του αυτή σε ειδικές περιστάσεις.

35 Ο αλγόριθμος δρομολόγησης (1)
Στις πρώτες εκδόσεις Linux ήταν απλός και ευθύς, σε κάθε αλλαγή διεργασίας ο πυρήνας έψαχνε μέσα σε όλες τις διεργασίες που βρίσκονταν σε κατάσταση εκτέλεσης (runnable processes), υπολόγιζε σε αυτές τις προτεραιότητες τους και επέλεγε την καλύτερη από αυτές, αυτή με την μεγαλύτερη προτεραιότητα. Μειονέκτημα εδώ αφορούσε τον χρόνο επιλογής της κατάλληλης διεργασίας καθώς βρίσκεται σε άμεση εξάρτηση με τον αριθμό των διεργασιών που σαρώνονται από τον πυρήνα (χιλιάδες διεργασίες συνεπάγεται σε μεγάλη σπατάλη χρόνου). Δεν γινόταν βέλτιστη αξιοποίηση πολλαπλών επεξεργαστών σε μεγάλα υπολογιστικά συστήματα (high end systems).

36 Ο αλγόριθμος δρομολόγησης (2)
Στις νεότερες εκδόσεις δηλαδή στην έκδοση του Linux 2.6 ο αλγόριθμος βελτιώθηκε, έγινε περισσότερο πολύπλοκος. Δυνατότητα επιλογής της διεργασίας που θα τρέξει σε πολύ γρηγορότερο χρονικό διάστημα, συγκεκριμένα με χρονική πολυπλοκότητα Ο(1) (constant time) χωρίς ουσιαστικά να εξαρτάται από το σύνολο των διεργασιών που βρίσκονται στο σύστημα. Βελτιώθηκε η διαχείριση πολλαπλών επεξεργαστών καθώς κάθε επεξεργαστής έχει το δικό του σύνολο (ουρά) από διεργασίες που βρίσκονται σε φάση εκτέλεσης.

37 Ο αλγόριθμος δρομολόγησης (3)
Τρεις (3) κλάσεις δρομολόγησης: SCHED_FIFO SCHED_RR SCHED_NORMAL

38 Ο αλγόριθμος δρομολόγησης (4)
SCHED_FIFO First in First Out (FIFO) σε πραγματικό χρόνο διεργασία. Όταν ο χρονοδρομολογητής βάζει μία διεργασία στον επεξεργαστή αφήνει τον περιγραφέα διεργασίας στην τωρινή θέση στην λίστα με τις διεργασίες που βρίσκονται για εκτέλεση. SCHED_RR Round Robin σε πραγματικό χρόνο διεργασία. Όταν ο χρονοδρομολογητής βάζει μία διεργασία στον επεξεργαστή αφήνει τον περιγραφέα διεργασίας στην τελική θέση στην λίστα με τις διεργασίες που βρίσκονται για εκτέλεση. SCHED_NORMAL Συμβατική διεργασία με κβάντα χρόνου.

39 Ο αλγόριθμος δρομολόγησης (5)
Στατική προτεραιότητα, είναι μια τιμή που χρησιμοποιεί ο χρονοδρομολογητής για να δηλώσει την προτεραιότητα της διεργασίας. Εύρος τιμών 100 = μέγιστη προτεραιότητα 139 = ελάχιστη προτεραιότητα Μια νέα διεργασία κληρονομεί την στατική προτεραιότητα του πατέρα, όμως κάποιος χρήστης μπορεί να αλλάξει την τιμή αυτή με τις κλήσεις συστήματος nice() ή set_priority().

40 Ο αλγόριθμος δρομολόγησης (6) Base time Quantum
Είναι το κβάντο χρόνου που δίνει ο χρονοδρομολογητής σε μία διεργασία που έχει καταναλώσει το προηγούμενο κβάντο. Μαθηματικός τύπος για την στατική προτεραιότητα και το κβάντο χρόνου: Παρατηρούμε ότι όσο μεγαλύτερη στατική προτεραιότητα τόσο μεγαλύτερο και το κβάντο χρόνου.

41 Ο αλγόριθμος δρομολόγησης (7) Τυπικές τιμές στατικής προτεραιότητας για μία συμβατική διεργασία

42 Ο αλγόριθμος δρομολόγησης (8) Δυναμική Προτεραιότητα και μέσος χρόνος αδράνειας
Πέρα από την στατική προτεραιότητα ορίζεται και η δυναμική προτεραιότητα που είναι και αυτός ένας αριθμός από το και ορίζεται από την παρακάτω φόρμουλα: Dynamic priority = max (100, min( static_priority – bonus + 5, 139) ) H τιμή bonus κυμαίνεται από 0 έως 10. Τιμή μικρότερη από 5 δηλώνει penalty και μειώνει την δυναμική προτεραιότητα ενώ τιμή μεγαλύτερη από 5 δηλώνει premium και αυξάνει την δυναμική προτεραιότητα. Η τιμή του bonus εξαρτάται από τις προηγούμενες τιμές της διεργασίας, μάλιστα από τις τιμές του μέσου χρόνου όπου η διεργασία βρίσκεται σε κατάσταση αδράνειας (sleep time).

43 Ο αλγόριθμος δρομολόγησης (9)

44 Ο αλγόριθμος δρομολόγησης (10)
Για να αποφασιστεί αν μία διεργασία είναι interactive ή batch χρησιμοποιείται η ακόλουθη φόρμουλα: Είναι interactive εάν: dynamic_priority <= 3* static_priority/4+28 Που είναι ισοδύναμο με: Bonus-5 >= static_priority/4-28 Ο όρος static_priority/4-28 ονομάζεται interactive delta και τιμές του αυτού παρουσιάζονται στον παρακάτω πίνακα.

45 Ο αλγόριθμος δρομολόγησης (11)
Description Static priority Nice value Base time quantum Interactivedelta Sleep time threshold Highest static priority 100 -20 800 ms -3 299 ms High static priority 110 -10 600 ms -1 499 ms Default static priority 120 100 ms +2 799 ms Low static priority 130 +10 50 ms +4 999 ms Lowest static priority 139 +19 5 ms +6 1199 ms

46 Ο αλγόριθμος δρομολόγησης (12) Ενεργές(Active) / Περατωμένες(Expired) Διεργασίες
Ενεργές ονομάζονται οι διεργασίες που δεν έχουν ξεπεράσει το κβάντο χρόνου που τους έχει ανατεθεί και για αυτό τον λόγο μπορούν να τρέχουν. Περατωμένες ονομάζονται οι διεργασίες που έχουν ολοκληρώσει το κβάντο χρόνου τους και για αυτό το λόγο πρέπει να περιμένουν μέχρι κάποια από τις ενεργές διεργασίες τελειώσει. Περιοδικά, ο ρόλος των διεργασιών αλλάζει δυναμικά.

47 Ο αλγόριθμος δρομολόγησης (13) Real time process scheduling
Κάθε διεργασία πραγματικού χρόνου σχετίζεται με μία τιμή προτεραιότητας πραγματικού χρόνου που είναι μία τιμή που κυμαίνεται: 1 = μέγιστη προτεραιότητα 99 = ελάχιστη προτεραιότητα Οι διεργασίες πραγματικού χρόνου θεωρούνται πάντα ενεργές με την μέγιστη προτεραιότητα η οποία μπορεί να αλλάξει από την χρήστη με την βοήθεια κλήσεων συστήματος όπως οι: sched_setparam( ) sched_set_scheduler( )

48 Ο αλγόριθμος δρομολόγησης (14) Real time process scheduling replacement (1)
Μια διεργασία πραγματικού χρόνου αντικαθίσταται μόνο όταν ικανοποιούνται κάποια ή κάποιες από τις παρακάτω συνθήκες: Η διεργασία αντικαθίσταται από μία άλλη με μεγαλύτερη προτεραιότητα Η διεργασία εκτελεί μία διαδικασία μπλοκαρίσματος και μπαίνει σε κατάσταση αδράνειας (TASK_INTERRUPTIBLE ή TASK_UNINTERRUPTIBLE) Η διεργασία σταματά (TASK_STOPPED ή TASK_TRACED), ή σκοτώνεται με κατάσταση (EXIT_ZOMBIE ή EXIT_DEAD). Η διεργασία εθελοντικά απελευθερώνει τον επεξεργαστή καλώντας την κλήση συστήματος sched_yield( )

49 Ο αλγόριθμος δρομολόγησης (15) Real time process scheduling replacement (2)
Μια διεργασία πραγματικού χρόνου αντικαθίσταται μόνο όταν ικανοποιούνται κάποια ή κάποιες από τις παρακάτω συνθήκες: Η διεργασία είναι Round Robin (SCHED_RR) και έχει εξαντλήσει το χρονικό κβάντο της. Οι κλήσεις συστήματος nice( ) και setpriority( ) όταν εφαρμόζονται σε μία διεργασία τύπου Round Robin δεν αλλάζουν την προτεραιότητα της σε πραγματικό χρόνο, αλλά αλλάζουν το χρονικό κβάντο της διεργασίας. Το βασικό κβάντο αυτών των διεργασιών δεν εξαρτάται από την προτεραιότητα σε πραγματικό χρόνο των διεργασιών αυτών, αλλά από την στατική προτεραιότητα.

50 Ο αλγόριθμος δρομολόγησης (16) Δομές που χρησιμοποιούνται από τον χρονοδρομολογητή (1)
Σημαντικότερη από όλες τις δομές είναι η δομή Runqueue για τον χρονοδρομολογητή σε Linux λειτουργικά συστήματα με πυρήνα 2.6 Κάθε επεξεργαστής στο σύστημα έχει την δική του δομή Runqueue. Όλες οι Runqueue δομές αποθηκεύονται σε μεταβλητές ανά επεξεργαστή τύπου Runqueue. this_rq( ) macro: Δηλώνει την διεύθυνση στην μνήμη της δομής για τον τοπικό επεξεργαστή cpu_rq(n) macro: Δηλώνει την διεύθυνση στην μνήμη της δομής για τον επεξεργαστή με αναγνωριστικό (n).

51 Ο αλγόριθμος δρομολόγησης (17) Δομές που χρησιμοποιούνται από τον χρονοδρομολογητή (2)
Type Name Description spinlock_t lock Spin lock protecting the lists of processes unsigned long nr_running Number of runnable processes in the runqueue lists cpu_load CPU load factor based on the average number of processes in the runqueue nr_switches Number of process switches performed by the CPU nr_uninterruptible Number of processes that were previously in the runqueue lists and are now sleeping in TASK_UNINTERRUPTIBLE state (only the sum of these fields across all runqueues is meaningful) expired_timestamp Insertion time of the eldest process in the expired lists unsigned long long timestamp_last_tick Timestamp value of the last timer interrupt task_t * curr Process descriptor pointer of the currently running process (same as current for the local CPU) idle Process descriptor pointer of the swapper process for this CPU

52 Ο αλγόριθμος δρομολόγησης (18) Δομές που χρησιμοποιούνται από τον χρονοδρομολογητή (3)
Type Name Description struct mm_struct * prev_mm Used during a process switch to store the address of the memory descriptor of the process being replaced prio_array_t * active Pointer to the lists of active processes expired Pointer to the lists of expired processes prio_array_t [2] arrays The two sets of active and expired processes int best_expired_prio The best static priority (lowest value) among the expired processes atomic_t nr_iowait Number of processes that were previously in the runqueue lists and are now waiting for a disk I/O operation to complete struct sched_domain * sd Points to the base scheduling domain of this CPU active_balance Flag set if some process shall be migrated from this runqueue to another (runqueue balancing) push_cpu Not used task_t * migration_thread Process descriptor pointer of the migration kernel thread struct list_head migration_queue List of processes to be removed from the runqueue

53 Ο αλγόριθμος δρομολόγησης (19) Δομές που χρησιμοποιούνται από τον χρονοδρομολογητή (4)
Το πεδίο arrays είναι ένας πίνακας που αποτελείται από δύο δομές τύπου prio_array_t. Κάθε μία από τις δομές αυτές αντιπροσωπεύει ένα σύνολο από διεργασίες και περιέχει 140 συνδεδεμένες λίστες, η κάθε μία από αυτές για μία πιθανή προτεραιότητα της διεργασίας, ένα priority bitmap και ένα μετρητή για το πόσες διεργασίες υπάρχουν στο σύνολο. struct prio_array { unsigned int nr_active; unsigned long bitmap[BITMAP_SIZE]; struct list_head queue[MAX_PRIO]; 215 };

54 Ο αλγόριθμος δρομολόγησης (20) Δομές που χρησιμοποιούνται από τον χρονοδρομολογητή (5)
Παρατηρούμε ότι το πεδίο active δείχνει σε έναν από τους δύο πίνακες όπως επίσης και το πεδίο expired.

55 Ο αλγόριθμος δρομολόγησης (21) Πεδία που χρησιμοποιούνται από τον περιγραφέα διεργασίας (process descriptor) για την δρομολόγηση Type Name Description unsigned long thread_info->flags Stores the TIF_NEED_RESCHED flag, which is set if the scheduler must be invoked unsigned int thread_info->cpu Logical number of the CPU owning the runqueue to which the runnable process belongs state The current state of the process int prio Dynamic priority of the process static_prio Static priority of the process struct list_head run_list Pointers to the next and previous elements in the runqueue list to which the process belongs prio_array_t * array Pointer to the runqueue's prio_array_t set that includes the process sleep_avg Average sleep time of the process unsigned long long timestamp Time of last insertion of the process in the runqueue, or time of last process switch involving the process last_ran Time of last process switch that replaced the process activated Condition code used when the process is awakened policy The scheduling class of the process (SCHED_NORMAL, SCHED_RR, or SCHED_FIFO) cpumask_t cpus_allowed Bit mask of the CPUs that can execute the process time_slice Ticks left in the time quantum of the process first_time_slice Flag set to 1 if the process never exhausted its time quantum rt_priority Real-time priority of the process

56 Ο αλγόριθμος δρομολόγησης (22) Πεδία που χρησιμοποιούνται από τον περιγραφέα διεργασίας (process descriptor) για την δρομολόγηση Κατά την δημιουργία μιας νέας διεργασίας η copy_process() καλεί την sched_fork() και θέτει το time_slice τόσο για την διεργασία όσο και για τον πατέρα της διεργασίας με τον ακόλουθο τρόπο: p->time_slice = (current->time_slice + 1) >> 1; current->time_slice >>= 1; Current = διεργασία πατέρας P = διεργασία παιδί Ο αριθμός των tick που απομένουν για τον πατέρα μοιράζονται σε δύο μισά, ένα για τον πατέρα και ένα για την διεργασία παιδί. Επίσης η κλήση συστήματος copy_process() αρχικοποιεί τα εξής: p->first_time_slice = 1; p->timestamp = sched_clock( );

57 Functions Used by the scheduler(Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή)

58 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή
scheduler_tick( ) try_to_wake_up( ) recalc_task_prio( ) schedule( ) load_balance()

59 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (1) scheduler_tick( )
Κώδικας από το αρχείο sched.c void scheduler_tick(void) 5135 { int cpu = smp_processor_id(); struct rq *rq = cpu_rq(cpu); struct task_struct *curr = rq->curr; 5139 sched_clock_tick(); 5141 spin_lock(&rq->lock); update_rq_clock(rq); update_cpu_load(rq); curr->sched_class->task_tick(rq, curr, 0); spin_unlock(&rq->lock); 5147 perf_counter_task_tick(curr, cpu); 5149 5150 #ifdef CONFIG_SMP rq->idle_at_tick = idle_cpu(cpu); trigger_load_balance(rq, cpu); 5153 #endif 5154 }

60 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητής (2) scheduler_tick( )
Αποθηκεύει στο πεδίο timestamp_last_tick της Runqueue δομής την τιμή του καταχωρητή Time Stamp Counter (TSC) αφού έχει γίνει η μετατροπή της σε nanoseconds. Η τιμή αυτή λαμβάνεται από την συνάρτηση sched_clock( ). Ελέγχει αν η τωρινή διεργασία είναι μία διεργασία αντικατάστασης (swapper process) της τοπικής CPU. Αν είναι τότε εκτελούνται τα ακόλουθα: Ελέγχει αν το current->array δείχνει στην ενεργή λίστα της τοπικής δομής runqueue. Εάν όχι τότε η διεργασία έχει εξαντλήσει το κβάντο χρόνου αλλά δεν έχει ακόμα αντικατασταθεί. Θέτει το TIF_NEED_RESCHED ώστε να γίνει βιαίως η επαναχρονοδρομολόγηση. Λαμβάνει γνώση για το this_rq()->lock.

61 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (3) scheduler_tick( )
Μειώνει τον μετρητή των κβάντων χρόνου της διεργασίας και ελέγχει αν το κβάντο χρόνου έχει εξαντληθεί. Απελευθερώνει τον this_rq()->lock. Καλεί την συνάρτηση rebalance_tick( ) που διασφαλίζει ότι οι δομές runqueues τον CPUs θα έχουν τον ίδιο αριθμό από διεργασίες που δύναται να εκτελεστούν.

62 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (4) scheduler_tick( )
Εάν η διεργασία είναι FIFO διεργασία πραγματικού χρόνου η συνάρτηση δεν κάνει τίποτα, αν είναι όμως Round Robin μικραίνει το κβάντο χρόνου και ελέγχει αν το κβάντο έχει εξαντληθεί. if (current->policy == SCHED_RR && !--current->time_slice) { current->time_slice = task_timeslice(current); current->first_time_slice = 0; set_tsk_need_resched(current); list_del(&current->run_list); list_add_tail(&current->run_list, this_rq( )->active->queue+current->prio); }

63 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (5) scheduler_tick( )
Σαν πρώτο βήμα γεμίζεται ο μετρητής με την κλήση της task_timeslice. Το πεδίο first_time_slice γίνεται μηδέν. Το πεδίο αυτό παίρνει τιμή από την copy_process() κατά την διαδικασία δημιουργίας διεργασίας από την fork(). Καλείται η set_tsk_need_resched( ) με σκοπό να θέσει την τιμή της σταθεράς TIF_NEED_RESCHED. Καλείται η βασικότατη συνάρτηση schedule ώστε να γίνει η αλλαγή της διεργασίας με μία νέα διεργασία που θα έχει μεγαλύτερη προτεραιότητα.

64 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (5) try_to_wake_up( )
Η συνάρτηση αυτή ξυπνάει μία διεργασία που είναι είτε σταματημένη είτε σε κατάσταση αδράνειας και την θέτει σε κατάσταση TASK_RUNNING τοποθετώντας την στην runqueue του τοπικού επεξεργαστή. Παράμετροι που δέχεται η συνάρτηση: Τον δείκτη περιγραφέα (p) της διεργασίας που πρόκειται να ξυπνήσει. Μία μάσκα από την κατάσταση της διεργασίας που πρόκειται να ξυπνήσει Μια μεταβλητή (sync) που εμποδίζει την διεργασία που θα ξυπνήσει να βγάλει από τον επεξεργαστή (preempt) την διεργασία που εκτελείται.

65 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (6) try_to_wake_up( )
Καλεί την task_rq_lock( ) συνάρτηση για να απενεργοποιήσει τα τοπικά interrupts. Ελέγχει αν η κατάσταση της διεργασίας ανήκει στην μάσκα με τις καταστάσεις states που περνιέται σαν όρισμα στην συνάρτηση. Αν το p->array δεν είναι NULL, η διεργασία ανήκει ήδη στην δομή runqueue. Σε ένα σύστημα με πολλούς επεξεργαστές ελέγχει αν η διεργασία που πρόκειται να ξυπνήσει θα πρέπει να μεταφερθεί από την runqueue του τελευταίο επεξεργαστή που χρησιμοποιήθηκε, σε αυτήν που ανήκει σε κάποιον άλλο επεξεργαστή.

66 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (7) try_to_wake_up( )
Κριτήρια επιλογής runqueue. Εάν κάποιος επεξεργαστής στο σύστημα είναι αδρανής, επιλέγει την δική του runqueue για στόχο. Εάν ο φόρτος του προηγούμενου σε χρήστη επεξεργαστή είναι σημαντικά μικρότερος από τον φόρτο του τωρινού επιλέγει το runqueue του προηγούμενου. Εάν η διεργασία έχει εκτελεστεί πρόσφατα επιλέγει το παλαιό runqueue για στόχο. Εάν η μεταφορά της διεργασίας στην τοπική CPU μειώσει τον φόρτο ανάμεσα στους επεξεργαστές τότε η επιλογή είναι το τοπικό runqueue.

67 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (8) recalc_task_prio( )
Ενημερώνει τον μέσο όρο του χρόνου που η διεργασία βρίσκεται σε κατάσταση αναστολής και την δυναμική προτεραιότητα της διεργασίας. Παίρνει σαν παράμετρο ένα δείκτη στον περιγραφέα διεργασίας (p) και ένα timestamp now το οποίο υπολογίζεται κατόπιν της κλήσης συστήματος sched_clock( ).

68 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (9) recalc_task_prio( )
Η συνάρτηση εκτελεί τα παρακάτω: Αποθηκεύει στην μεταβλητή sleep_time το αποτέλεσμα από το παρακάτω min (now - p->timestamp, 109 ) Εάν το sleep_time δεν είναι μεγαλύτερο από το μηδέν, δεν υπολογίζει τον μέσο χρόνο που η διεργασία κοιμάται, απλά υπολογίζει την δυναμική προτεραιότητα. Ελέγχει αν η διεργασία δεν είναι ένα νήμα πυρήνα, εάν ξυπνάει από κατάσταση TASK_UNINTERRUPTIBLE και εάν βρισκόταν σε κατάσταση αναστολής περισσότερο από ένα δοσμένο χρονικό διάστημα. Αν οι τρεις αυτές συνθήκες αληθεύουν τότε η συνάρτηση θέτει το p->sleep_avg στην τιμή των 900 ticks. Εκτελεί το CURRENT_BONUS macro για να υπολογίσει την bonus τιμή του προηγούμενο μέσου χρόνου αδράνειας.

69 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (10) recalc_task_prio( )
Η συνάρτηση εκτελεί τα παρακάτω: Εάν η διεργασία είναι TASK_UNINTERRUPTIBLE και δεν είναι νήμα πυρήνα εκτελεί τα παρακάτω βήματα: Ελέγχει αν το p->sleep_avg είναι >= από το κατώφλι το sleep time, αν είναι τότε μηδενίζει την τοπική μεταβλητή sleep_avg. Ελέγχει αν το άθροισμα του sleep_avg + p->sleep_avg είναι >= από το κατώφλι του sleep time και θέτει το p->sleep_avg να είναι το κατώφλι του sleep time. Προσθέτει sleep_time στον μέσο χρόνο αδράνειας. Ελέγχει αν το p->sleep_avg υπερβαίνει τα 1000 ticks σε nanoseconds, αν ναι η συνάρτηση τα κόβει στα 1000 πάντα σε nanoseconds.

70 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (11) recalc_task_prio( )
Η συνάρτηση εκτελεί τα παρακάτω: Εάν η διεργασία είναι TASK_UNINTERRUPTIBLE και δεν είναι νήμα πυρήνα εκτελεί τα παρακάτω βήματα: Ελέγχει αν το p->sleep_avg είναι >= από το κατώφλι το sleep time, αν είναι τότε μηδενίζει την τοπική μεταβλητή sleep_avg. Ελέγχει αν το άθροισμα του sleep_avg + p->sleep_avg είναι >= από το κατώφλι του sleep time και θέτει το p->sleep_avg να είναι το κατώφλι του sleep time. Προσθέτει sleep_time στον μέσο χρόνο αδράνειας. Ελέγχει αν το p->sleep_avg υπερβαίνει τα 1000 ticks σε nanoseconds, αν ναι η συνάρτηση τα κόβει στα 1000 πάντα σε nanoseconds.

71 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (12) schedule( ) (2.4)
Πρόκειται για την βασική συνάρτηση που κάνει το scheduling δηλαδή την αλλαγή της τρέχουσας διεργασίας με μία άλλη από την ουρά των προς εκτέλεση διεργασιών. Αλλαγές σε σχέση με τις προηγούμενες εκδόσεις πυρήνα 2.4, 1400 γραμμές κώδικα Τρεις μόνο βασικές δομές Η βασική δομή είναι η schedule_data(), περιέχει ένα δείκτη προς την διεργασία που τρέχει και το timestamp από την τελευταία εκτέλεση της συνάρτησης. Υπάρχει μόνο μία runqueue και αυτή έχει την δομή της συνδεδεμένης λίστας

72 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (13) schedule( ) (2.4)
struct schedule_data { struct task_struct * curr; cycles_t last_schedule; } schedule_data; char __pad [SMP_CACHE_BYTES]; Πολύ απλή δομή Ορίζεται στο αρχείο sched.c Περιέχει το time stamp της τελευταίας αλλαγής διεργασίας Περιέχει επίσης ένα δείκτη στην διεργασία που τρέχει.

73 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (14) schedule( ) (2.4)
Reschedule_idle κοιτάζει απλά αν η διεργασία που βγήκε από την κατάσταση εκτέλεσης πρέπει να μεταβεί σε διαφορετικό επεξεργαστή. Δεν χρησιμοποιεί counter και nice άμεσα, χρησιμοποιεί μία συνάρτηση με όνομα goodness() για να ελέγξει τις προτεραιότητες. Η συνάρτηση goodness() λαμβάνει υπ' όψιν το κόστος της μετάβασης από έναν επεξεργαστή σε έναν άλλο.

74 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (15) schedule( ) (2.6)
5700 γραμμές κώδικα. Καλείται όταν η τρέχουσα διεργασία πρέπει να μπλοκαριστεί, άμεση κλήση. Στην περίπτωση αυτή: Εισάγεται η τρέχουσα διεργασία στην ουρά αυτών που περιμένων. Αλλάζει η κατάσταση της τρέχουσας σε ASK_INTERRUPTIBLE ή σε TASK_UNINTERRUPTIBLE. Καλείται η συνάρτηση schedule(). Ελέγχεται αν υπάρχουν πόροι, αν υπάρχουν βγαίνει η διεργασία από την ουρά αναμονής.

75 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (16) schedule( ) (2.6)
Καλείται και έμμεσα με ένα «τεμπέλικο» τρόπο (lazy invocation) με το να θέσουμε την TIF_NEED_RESCHED μεταβλητή σε 1. Παραδείγματα αυτής της μεθόδου: Όταν η τρέχουσα διεργασία έχει εξαντλήσει το κβάντο χρόνου της. Όταν μια διεργασία που ξυπνάει έχει προτεραιότητα μεγαλύτερη από αυτή της τρέχουσας. Όταν πραγματοποιείται μία sched_setscheduler( ) κλήση συστήματος. Βασικός στόχος είναι να κάνει την next μεταβλητή να δείχνει στον περιγραφέα διεργασίας της διεργασίας που έχει επιλεγεί για να αντικαταστήσει την τρέχουσα (current).

76 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (17) schedule( ) (2.6)
Η συνάρτηση ξεκινά αρχικά με το να απενεργοποιεί το preemption και να αρχικοποιεί κάποιες μεταβλητές: need_resched: preempt_disable( ); prev = current; rq = this_rq( ); if (prev->lock_depth >= 0) up(&kernel_sem); now = sched_clock( ); run_time = now - prev->timestamp; if (run_time > ) run_time = ;

77 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (18) schedule( ) (2.6)
Κατόπιν η συνάρτηση εξετάζει την κατάσταση της προηγούμενης διεργασίας. if (prev->state != TASK_RUNNING && !(preempt_count() & PREEMPT_ACTIVE)) { if (prev->state == TASK_INTERRUPTIBLE && signal_pending(prev)) prev->state = TASK_RUNNING; else { if (prev->state == TASK_UNINTERRUPTIBLE) rq->nr_uninterruptible++; deactivate_task(prev, rq); } Η deactivate_task απομακρύνει την διεργασία από το runqueue.

78 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (19) schedule( ) (2.6)
rq->nr_running--; dequeue_task(p, p->array); p->array = NULL; Κατόπιν η συνάρτηση ελέγχει για τον αριθμό των διεργασιών που βρίσκονται στο runqueue. Αν υπάρχουν καλείται η dependent_sleeper( ) Αν ο πυρήνας υποστηρίζει τεχνολογία hyper threading, τότε η διεργασία που πρόκειται να επιλέξει ο δρομολογητής για εκτέλεση ελέγχεται και με βάση την προτεραιότητα της επιλέγεται ή όχι για να αντικαταστήσει στον λογικό επεξεργαστή του φυσικού επεξεργαστή την υπάρχουσα. if (rq->nr_running) { if (dependent_sleeper(smp_processor_id( ), rq)) { next = rq->idle; goto switch_tasks; }

79 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (20) schedule( ) (2.6)
Αν δεν υπάρχουν διεργασίες προς εκτέλεση τότε επιλέγονται μερικές από την συνάρτηση idle_balance( ) από κάποιο άλλο runqueue στο τοπικό runqueue. if (!rq->nr_running) { idle_balance(smp_processor_id( ), rq); next = rq->idle; rq->expired_timestamp = 0; wake_sleeping_dependent(smp_processor_id( ), rq); if (!rq->nr_running) goto switch_tasks; }

80 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (21) schedule( ) (2.6)
Ελέγχονται αν υπάρχουν διεργασίες που είναι ενεργές. Αν δεν υπάρχουν ανταλλάσσονται τα περιεχόμενα από τα πεδία active / expired της runqueue δομής. array = rq->active; if (!array->nr_active) { rq->active = rq->expired; rq->expired = array; rq->expired_timestamp = 0; rq->best_expired_prio = 140; }

81 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (22) schedule( ) (2.6)
Αναζητάτε μία διεργασία προς εκτέλεση στον πίνακα prio_array_t της δομής. idx = sched_find_first_bit(array->bitmap); next = list_entry(array->queue[idx].next, task_t, run_list); if (next->prio >= 100 && next->activated > 0) { unsigned long long delta = now - next->timestamp; if (next->activated == 1) delta = (delta * 38) / 128; array = next->array; dequeue_task(next, array); recalc_task_prio(next, next->timestamp + delta); enqueue_task(next, array); } next->activated = 0;

82 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (23) schedule( ) (2.6)
Η συνάρτηση πρέπει να επιλέξει την κατάλληλη διεργασία για να αντικαταστήσει την υπάρχουσα διεργασία. switch_tasks: prefetch(next); Το prefetch macro βελτιώνει την απόδοση της συνάρτησης schedule. Πριν γίνει η αντικατάσταση της διεργασίας εκτελούνται οι ακόλουθες συναρτήσεις: clear_tsk_need_resched(prev); rcu_qsctr_inc(prev->thread_info->cpu); Η clear_tsk_need_resched(prev) αλλάζει την τιμή της μεταβλητής TIF_NEED_RESCHED.

83 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (24) schedule( ) (2.6)
prev->sleep_avg -= run_time; if ((long)prev->sleep_avg <= 0) prev->sleep_avg = 0; prev->timestamp = prev->last_ran = now; Σε περίπτωση που η διεργασία που αντικαθίσταται είναι η ίδια με αυτή που πρέπει να αντικαταστήσει την υπάρχουσα κάτι το οποίο συμβαίνει αν δεν υπάρχουν άλλες με ίση η μεγαλύτερη προτεραιότητα στο runqueue, συμβαίνει το εξής: if (prev == next) { spin_unlock_irq(&rq->lock); goto finish_schedule; }

84 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (25) schedule( ) (2.6)
Αμέσως μετά το content switch και την συνάρτηση schedule ακολουθούν οι συναρτήσεις: barrier( ); finish_task_switch(prev); Η πρώτη θέτει ένα φράγμα στην χρήση της μνήμης οδηγώντας έτσι στην βελτίωση του κώδικα, ενώ η δεύτερη απελευθερώνει το spin lock της runqueue και ενεργοποιεί τα τοπικά interrupts. mm = this_rq( )->prev_mm; this_rq( )->prev_mm = NULL; prev_task_flags = prev->flags; spin_unlock_irq(&this_rq( )->lock); if (mm) mmdrop(mm); if (prev_task_flags & PF_DEAD) put_task_struct(prev);

85 Συναρτήσεις που χρησιμοποιούνται από τον χρονοδρομολογητή (26) schedule( ) (2.6)
Τελευταίες γραμμές κώδικα για την συνάρτηση: finish_schedule: prev = current; if (prev->lock_depth >= 0) _ _reacquire_kernel_lock( ); preempt_enable_no_resched(); if (test_bit(TIF_NEED_RESCHED, &current_thread_info( )->flags) goto need_resched; return; Παρατηρούμε ότι αποκτάται το kernel lock και ενεργοποιείται ξανά το kernel preemption και ελέγχεται αν κάποια άλλη διεργασία έχει θέσει την TIF_NEED_RESCHED για την διεργασία αυτή, αν ναι όλη η συνάρτηση schedule ξαναεκτελείται από την αρχή αλλιώς η συνάρτηση τερματίζει.

86 Κλήσεις συστήματος που σχετίζονται με την δρομολόγηση (1)
Nice() : Επιτρέπει στην διεργασία να αλλάζει την προτεραιότητα της, η ακέραια τιμή που υπάρχει στην παράμετρο increment χρησιμοποιείται για να αλλάξει το nice πεδίο του περιγραφέα διεργασίας. GetPriority / SetPriority GetPriority: Επιστρέφει 20 – την κατώτατη τιμή της nice. SetPriority: Θέτει τις προτεραιότητες για όλες τις διεργασίες που ανήκουν σε κάποιο group. sched_getaffinity( ) και sched_setaffinity( ) : Επιστρέφουν και θέτουν την τιμή της affinity mask . Αυτή η μάσκα αποθηκεύεται στην cpus_allowed μεταβλητή του περιγραφέα διεργασίας.

87 Σχηματική Αναπαράσταση (1)

88 Σχηματική Αναπαράσταση (2)

89 Βιβλιογραφία http://tldp.org/LDP/lki/lki-2.html

90 Βιβλιογραφία


Κατέβασμα ppt "Linux Process Scheduling"

Παρόμοιες παρουσιάσεις


Διαφημίσεις Google