440 likes | 594 Views
Εργαστήριο Λειτουργικών Συστημάτων Μάθημα 6oυ Εξαμήνου, Τομέας Λογικού και Υπολογιστών. Δρ. Α. Κομνηνός / Δρ. Χ. Μακρής akomninos@ceid.upatras.gr , makri@ceid.upatras.gr. Σύνοψη 9 ης ∆ιάλεξης. Λειτουργικό Σύστημα Minix ∆ιεργασίες
E N D
Εργαστήριο Λειτουργικών Συστημάτων Μάθημα 6oυ Εξαμήνου, Τομέας Λογικού και Υπολογιστών Δρ. Α. Κομνηνός/Δρ. Χ. Μακρής akomninos@ceid.upatras.gr, makri@ceid.upatras.gr
Σύνοψη 9ης ∆ιάλεξης • Λειτουργικό Σύστημα Minix • ∆ιεργασίες • Χρονοπρογραμματισμός στα αλληλεπιδραστικά συστήματα • Υλοποίηση Μηχανισμού Χρονοπρογραμματισμού
Εκτέλεση ∆ιεργασιών • Οι διεργασίες εκτελούνται σειριακά ανά διαθέσιμο επεξεργαστή Μόνο μια διεργασία είναι ενεργή ανά πάσα στιγμή • Οι πληροφορίες για κάθε διεργασία βρίσκονται: στο επίπεδο του πυρήνα, στον διαχειριστή διεργασιών και στον διαχειριστή αρχείων • Το Λ.Σ. χρονοπρογραμματίζει τις διεργασίες – στο επίπεδο του πυρήνα και στον διαχειριστή διεργασιών
Κύκλος Ζωής ∆ιεργασιών • Τέσσερα βασικά γεγονότα οδηγούν στην δημιουργία νέων διεργασιών • Αρχικοποίηση του συστήματος • Μια διεργασία εκτελεί την κλήση συστήματος για την δημιουργία νέας διεργασίας • ́Ενας χειριστής εκτελεί μια εντολή • Εκκίνηση μιας εργασίας (batch job) • Οι διεργασίες τερματίζουν εάν προκύψει ένας από τους παρακάτω λόγους • Κανονικός τερματισμός – έξοδος • Τερματισμός λόγω λάθους • Απότομος τερματισμός • Τερματισμός από μια άλλη διεργασία
Ιεράρχηση ∆ιεργασιών • ́Οταν μια διεργασία δημιουργήσει μια νέα διεργασία (μέσω κάποιας κλήσης στο σύστημα) • Θεωρούμε ότι υπάρχει σχέση γονέα – παιδιού • Μια διεργασία παιδί μπορεί να δημιουργήσει και αυτή νέες διεργασίες • ∆ημιουργείται μια ιεραρχία διεργασιών – ένα δένδρο • Οι διεργασίες μπορούν να επικοινωνήσουν με τα κατώτερα επίπεδα του Λ.Σ. είτε αυτόνομα είτε ως ιεραρχία διεργασιών (ομάδα) • π.χ. ένα σήμα παραδίδεται σε όλες τις διεργασίες • Το Minix χρησιμοποιεί δύο ‘ειδικές’ διεργασίες που δημιουργούνται κατά την αρχικοποίησης του συστήματος • Reincarnation Server και init • δημιουργούν όλες τις άλλες διεργασίες του συστήματος
Reincarnation Server καιinit • Το init είναι η πρώτη διεργασία που εκτελείται κατά την αρχικοποίηση του συστήματος • Εκτελεί τα scripts που βρίσκονται στον φάκελο /etc/rc μέσω του reincarnation server • Η βασική λειτουργία του reincarnation server (rs) είναι η εκκίνηση των servers και των οδηγών (device drivers) • Επομένως όλοι οι servers και οι οδηγοί είναι ‘παιδιά’ του rs • Αν κάποια διεργασία τερματίσει τότε θα ειδοποιηθεί ο rs • Παρακολουθεί την εκτέλεση του και αν διαπιστώσει κάποιο πρόβλημα έχει το δικαίωμα να επανεκκινησει την διεργασία • Με αυτόν τον τρόπο το Minix προσφέρει λειτουργίες αυτο-ίασης • Η τελευταία διεργασία που ξεκινάει η init είναι η getty που είναι υπεύθυνη για τις κονσόλες των χειριστών
Εκτέλεση ∆ιεργασιών • Οι διεργασίες είναι ανεξάρτητες οντότητες • Μπορούν να αλληλεπιδράσουν μεταξύ τους, να επικοινωνήσουν και να συγχρονιστούν cat chapter1 chapter2 | grep tree • Η grep είναι έτοιμη να ‘τρέξει’ όμως περιμένει είσοδο από την cat • Επομένως μια διεργασία μπλοκάρει επειδή δεν μπορεί να συνεχίσει την εκτέλεση της • Υπάρχουν τέσσερις πιθανοί τρόποι μετάβασης μεταξύ των καταστάσεων μιας διεργασίας:
Υλοποίηση ∆ιεργασιών • Η υλοποίηση του μοντέλου των διεργασιών βασίζεται στην διατήρηση του ‘πίνακα στοιχείων διεργασιών’ (process table) • Περιέχει μια εγγραφή για κάθε διεργασία • Η διαχείριση των διεργασιών δεν γίνεται σε ένα σημείο • Ορισμένες λειτουργίες διαχείρισης γίνεται στο επίπεδο του πυρήνα • Κάποιες άλλες λειτουργίες γίνονται από τον διαχειριστή διεργασιών και τον διαχειριστή αρχείων • ́Αρα δεν υπάρχει ένας πίνακας αλλά τρεις !
Πίνακες Στοιχείων ∆ιεργασιών
Πίνακας ∆ιεργασιών – Σταθερές • Στο αρχείο /usr/src/include/minix/com.h ορίζονται κάποιες σταθερές για όλο το σύστημα • Στο αρχείο /usr/src/include/minix/sys_config.h ορίζονται κάποιες σταθερές για όλο το σύστημα // Number of tasks #define NR_TASKS 4 // Number of processes #define _NR_PROCS 100 // Number of system processes #define _NR_SYS_PROCS 32
Πίνακας ∆ιεργασιών του ∆ιαχειριστή ∆ιεργασιών (1) EXTERN structmproc { /* points to text, data, stack */ structmem_mapmp_seg[NR_LOCAL_SEGS]; char mp_exitstatus; /* status when process exits */ /* storage for signal # for killed procs */ char mp_sigstatus; pid_tmp_pid; /* process id */ intmp_endpoint; /* kernel endpoint id */ pid_tmp_procgrp; /* pid of process group */ pid_tmp_wpid; /* pid process is waiting for */ intmp_parent; /* index of parent process */ /* Child user and system times */ clock_tmp_child_utime; /* cumulative user time */ clock_tmp_child_stime; /* cumulative sys time */ ...
Πίνακας ∆ιεργασιών του ∆ιαχειριστή ∆ιεργασιών (2) ... /* Signal handling information. */ sigset_tmp_ignore; /* 1 means ignore the signal, 0 me sigset_tmp_catch; /* 1 means catch the signal, 0 mean sigset_t mp_sig2mess; /* 1 means transform into notify sigset_tmp_sigmask; /* signals to be blocked */ sigset_t mp_sigmask2; /* saved copy of mp_sigmask */ sigset_tmp_sigpending; /* pending signals to be handled */ structsigactionmp_sigact[_NSIG + 1]; /* as in sigact */ vir_bytesmp_sigreturn; /* address of C library __sig */ struct timer mp_timer; /* watchdog timer for alarm(2) */ /* Scheduling priority. */ signed intmp_nice; /* nice is PRIO_MIN..PRIO_MAX, sta char mp_name[PROC_NAME_LEN]; /* process name */ } mproc[NR_PROCS];
Πίνακας ∆ιεργασιών του Συστήματος Αρχείων (1) EXTERN structfproc { mode_tfp_umask; /* mask set by umask system call */ structinode *fp_workdir; /* pointer to working direct structinode *fp_rootdir; /* pointer to current root d structfilp *fp_filp[OPEN_MAX];/* the file descriptor fd_setfp_filp_inuse; /* which fd’s are in use? */ uid_tfp_realuid; /* real user id */ uid_tfp_effuid; /* effective user id */ gid_tfp_realgid; /* real group id */ gid_tfp_effgid; /* effective group id */ dev_tfp_tty; /* major/minor of controlling tty */ intfp_fd; /* place to save fd if rd/wr can’t finish * ...
Πίνακας ∆ιεργασιών του Συστήματος Αρχείων (2) ... char *fp_buffer; /* place to save buffer if rd/wr can’ intfp_nbytes; /* place to save bytes if rd/wr can’t intfp_cum_io_partial; /* partial byte count if rd/wr char fp_suspended; /* set to indicate process hanging char fp_revived; /* set to indicate process being revi intfp_task; /* which task is proc suspended on */ char fp_sesldr; /* true if proc is a session leader */ char fp_execced; /* true if proc has exec()ced after f pid_tfp_pid; /* process id */ long fp_cloexec; /* bit map for POSIX Table 6-2 FD_CLO intfp_endpoint; /* kernel endpoint number of this pro } fproc[NR_PROCS];
∆ομή Πίνακα ∆ιεργασιών του Πυρήνα (1) • Στο αρχείο /usr/src/kernel/proc.h ορίζεται η δομή του πίνακα των διεργασιών (για τον πυρήνα) structproc { // process’ registers saved in stack frame structstackframe_sp_reg; // number of this process (for fast access) proc_nr_tp_nr; // system privileges structure structpriv *p_priv; // process is runnable only if zero short p_rts_flags; // flags that do suspend the process short p_misc_flags;
∆ομή Πίνακα ∆ιεργασιών του Πυρήνα (2) // current scheduling priority char p_priority; // maximum scheduling priority char p_max_priority; // number of scheduling ticks left char p_ticks_left; // quantum size in ticks char p_quantum_size; // memory map (T, D, S) structmem_mapp_memmap[NR_LOCAL_SEGS]; // user/sys time in ticks clock_tp_user_time; clock_tp_sys_time;
∆ομή Πίνακα ∆ιεργασιών του Πυρήνα (3) // pointer to next ready process structproc *p_nextready; // head of list of procs wishing to send structproc *p_caller_q; // link to next proc wishing to send structproc *p_q_link; // pointer to passed message buffer message *p_messbuf; // from whom does process want to receive? intp_getfrom_e; // to whom does process want to send? intp_sendto_e; // bit map for pending kernel signals sigset_tp_pending;
∆ομή Πίνακα ∆ιεργασιών του Πυρήνα (4) // name of the process, including \0 char p_name[P_NAME_LEN]; // endpoint number, generation-aware intp_endpoint; // Debug info for scheduler intp_ready, p_found; }
Χρονοπρογραμματισμός διεργασιών • Κάθε φορά που προκύπτει ένα interrupt -- είναι μια ευκαιρία να επανεκτιμήσει το Λ.Σ. ποια διεργασία δικαιούται να χρησιμοποιήσει τον επεξεργαστή (να ενεργοποιηθεί) • Το Minix 3 χρησιμοποιεί ένα σύστημα ουρών πολλαπλών επιπέδων • Ορίζει 16 ουρές – κάθε ουρά αντιπροσωπεύει ένα επίπεδο προτεραιότητας • Οι διεργασίες που είναι IDLE βρίσκονται στο χαμηλότερο επίπεδο • Το System Task και το ρολόι εκτελούνται στο υψηλότερο επίπεδο • Εκτός από την προτεραιότητα που ορίζεται από την ουρά, χρησιμοποιούμε και τον μηχανισμό quantum • Κάθε διεργασία δικαιούται να χρησιμοποιήσει τον επεξεργαστή μόνο για ένα συγκεκριμένο χρονικό διάστημα – quantum • ́Οταν ολοκληρωθεί το quantum γίνεται block έως ότου ξανά έρθει η σειρά της • Το μέγεθος του quantum είναι διαφορετικό για κάθε κατηγορία διεργασιών
Χρονοπρογραμματισμός εκ περιτροπής • Κάθε διεργασία δικαιούται να χρησιμοποιήσει τον επεξεργαστή μόνο για ένα συγκεκριμένο χρονικό διάστημα – quantum • ́Οταν ολοκληρωθεί το quantum (κβάντο χρόνου) γίνεται block έως ότου ξανά έρθει η σειρά της • Τοποθετείται στο τέλος της ουράς (a) Η λίστα των εκτελέσιμων διεργασιών (b) Η λίστα των εκτελέσιμων διεργασιών μετά το πέρας του κβάντου (quantum) της διεργασίας Β
Χρονοπρογραμματισμός με βάση την προτεραιότητα • ́Ολες οι διεργασίες δεν είναι εξίσου σημαντικές • Σε κάθε διεργασία αντιστοιχίζεται μια προτεραιότητα • Σε κάθε χρονική στιγμή, εκτελείται η (έτοιμη) διεργασία με την υψηλότερη προτεραιότητα (priority scheduling) • Σε κάθε διεργασία αντιστοιχίζεται ένα μέγιστο κβάντο (quantum) • Καθορίζει πόσο χρόνο επιτρέπεται να εκτελείται κάθε φορά • Οι προτεραιότητες μπορούν να ανατεθούν στατικά ή δυναμικά
Χρονοπρογραμματισμός στο Minix 3 • Βασίζεται στην προηγούμενη τεχνική • Σε κάθε ουρά γίνεται εκ περιτροπής • Ορίζει 16 ουρές • Θέτει διαφορετικό quantum για κάθε διεργασία • Μπορούν εύκολα να αλλάξουν • Γίνεται από τον πυρήνα
Εσωτερική ∆ομή Πυρήνα – Σταθερές • Στο αρχείο /usr/src/kernel/proc.h include/minix/config.h ορίζονται οι σταθερές (για τον πυρήνα) που αφορούν τα θέματα χρονοπρογραμματισμού Process scheduling constants // MUST equal minimum priority + 1 #define NR_SCHED_QUEUES 16 // highest, used for kernel tasks #define TASK_Q 0 // default (shouldcorrespond to nice 0) #define USER_Q 7 // lowest, only IDLE processgoeshere #define IDLE_Q 15 // highest/lowestepriority for userprocesses #define MAX_USER_Q 0 #define MIN_USER_Q 14
Εσωτερική ∆ομή Πυρήνα – Καθολικές Μεταβλητές (1) • Στο αρχείο /usr/src/kernel/proc.h - proc.cορίζονται οι καθολικές μεταβλητές (για τον πυρήνα) για τον πίνακα των διεργασιών και κάποιοι βοηθητικοί πίνακες // process table EXTERN structprocproc[NR_TASKS + NR_PROCS]; // pointers to process table slots EXTERN struct proc *pproc_addr[NR_TASKS+NR_PROCS]; // ptrs to ready list headers EXTERN struct proc *rdy_head[NR_SCHED_QUEUES]; // ptrs to ready list tails EXTERN structproc *rdy_tail[NR_SCHED_QUEUES];
Εσωτερική ∆ομή Πυρήνα – Καθολικές Μεταβλητές (2) • Η rdy_head έχει 1 εγγραφή για κάθε ουρά • Κάθε εγγραφή είναι δείκτης (pointer) στην διεργασία που είναι στην αρχή της ουράς • Η rdy_tail είναι ένας αντίστοιχος πίνακας • ∆είχνει στην διεργασία που είναι στο τέλος της ουράς
Εσωτερική ∆ομή Πυρήνα – Καθολικές Μεταβλητές (3) • Στο αρχείο /usr/src/kernel/glo.h ορίζονται οι καθολικές μεταβλητές (για τον πυρήνα) • Είναι αναφορές (pointers) στον πίνακα των διεργασιών που διατηρεί ο πυρήνας /* previously running process */ EXTERN structproc *prev_ptr; /* pointer to currently running process */ EXTERN structproc *proc_ptr; /* next process to run after restart() */ EXTERN structproc *next_ptr; /* process to bill for clock ticks */ EXTERN structproc *bill_ptr;
Εσωτερική ∆ομή Πυρήνα – Αρχικοποίηση ∆ομών • Στο αρχείο /usr/src/kernel/table.c ορίζονται οι αρχικές διεργασίες και η προτεραιότητα και το κβάντο που θα έχουν • Το κβάντο (quantum) πρέπει να είναι θετικό – ορίζεται σε tick του ρολογιού • Οι εγγραφές πρέπει να εμφανίζονται με την ίδια σειρά με το image -- πρώτα πρέπει να είναι τα kernel tasks • PUBLIC structboot_image image[] = { • /* process nr, pc, flags, qs, queue, stack, traps, ipcto, call, name */ • { IDLE, idle_task, IDL_F, 8, IDLE_Q, IDL_S, 0, 0, 0, "idle" }, • { CLOCK,clock_task, TSK_F, 8, TASK_Q, TSK_S, TSK_T, 0, 0, "clock" }, • { SYSTEM, sys_task, TSK_F, 8, TASK_Q, TSK_S, TSK_T, 0, 0, "system"}, • { HARDWARE, 0, TSK_F, 8, TASK_Q, HRD_S, 0, 0, 0, "kernel"}, • { PM_PROC_NR, 0, SRV_F, 32, 3, 0, SRV_T, SRV_M, PM_C, "pm" }, • { FS_PROC_NR, 0, SRV_F, 32, 4, 0, SRV_T, SRV_M, FS_C, "fs" }, • { RS_PROC_NR, 0, SRV_F, 4, 3, 0, SRV_T, SYS_M, RS_C, "rs" }, • { DS_PROC_NR, 0, SRV_F, 4, 3, 0, SRV_T, SYS_M, DS_C, "ds" }, • { TTY_PROC_NR, 0, SRV_F, 4, 1, 0, SRV_T, SYS_M, TTY_C, "tty" }, • { MEM_PROC_NR, 0, SRV_F, 4, 2, 0, SRV_T, SYS_M, MEM_C, "mem" }, • { LOG_PROC_NR, 0, SRV_F, 4, 2, 0, SRV_T, SYS_M, DRV_C, "log" }, • { INIT_PROC_NR, 0, USR_F, 8, USER_Q, 0, USR_T, USR_M, 0, "init" }, • };
Γενικά θέματα • Ο χρονοπρογραμματισμός γίνεται για κάθε ουρά εκ περιτροπής • ́Οταν η τρέχουσα διεργασία χρησιμοποιήσει όλο το quantum που της αναλογεί, μεταφέρεται στο τέλος της ουράς και ανανεώνεται το quantum • Αν όμως η διεργασία μπλοκάρει, όταν ενεργοποιηθεί ξανά, τοποθετείται στην αρχή της ουράς • Χρησιμοποιεί το υπολειπόμενο quantum • Χρησιμοποιούμε τον πίνακα rdy_tail για να προσθέτουμε γρήγορα/εύκολα μια διεργασία στο τέλος της ουράς • ́Οταν μια διεργασία μπλοκάρει ή τερματίσει τότε η διεργασία αφαιρείται από την ουρά • Οι ουρές περιέχουν μόνο τις διεργασίες που ‘τρέχουν’
Αλγόριθμος Χρονοπρογραμματισμού • Ο αλγόριθμος είναι σχετικά απλός: • Εντόπισε την ουρά με την μεγαλύτερη προτεραιότητα που δεν είναι άδεια • Επέλεξε την διεργασία που είναι στην αρχή της ουράς • Ενεργοποίησε την διεργασία • Αν δεν επιλεχθεί κάποια διεργασία, τότε εκτελείται η διεργασία IDLEΟ εντοπισμός της ουράς / διεργασίας υλοποιείται από την συνάρτηση pick_proc • Οι συναρτήσεις enqueue και dequeue χρησιμοποιούνται για την επεξεργασία των ουρών • επί της ουσίας, υλοποιούν ‘βασικές’ λειτουργίες σε ουρές • χρησιμοποιούν την συνάρτηση sched που αποφασίζει σε ποία ουρά πρέπει να προστεθεί η διεργασία • Ορίζονται στο αρχείο /usr/src/kernel/proc.c
Συνάρτηση pick_proc PRIVATE void pick_proc() { register structproc *rp; int q; /* Check each of the scheduling queues for ready p for (q=0; q < NR_SCHED_QUEUES; q++) { if ( (rp = rdy_head[q]) != NIL_PROC) { next_ptr = rp; /* run process ’rp’ n if (priv(rp)->s_flags & BILLABLE) bill_ptr = rp;/* bill for system time*/ return; } } }
Συνάρτηση sched 00597 PRIVATE void sched(rp, queue, front) 00598 register structproc *rp; /* process to be scheduled */ 00599 int *queue; /* return: queue to use */ 00600 int *front; /* return: front or back */ 00601 { 00606 inttime_left = (rp->p_ticks_left > 0); /* quantum fully consumed */ 00607 00608 /* Check whether the process has time left. Otherwise give a new quantum 00609 * and lower the process' priority, unless the process already is in the 00610 * lowest queue. 00611 */ 00612 if (! time_left) { /* quantum consumed ? */ 00613 rp->p_ticks_left = rp->p_quantum_size; /* give new quantum */ 00614 if (rp->p_priority < (IDLE_Q-1)) { 00615 rp->p_priority += 1; /* lower priority */ 00616 } 00617 } 00618 00619 /* If there is time left, the process is added to the front of its queue, 00620 * so that it can immediately run. The queue to use simply is always the 00621 * process' current priority. 00622 */ 00623 *queue = rp->p_priority; 00624 *front = time_left; 00625 }
Συνάρτηση enqueue (1) 00502 PRIVATE void enqueue(rp) 00503 register structproc *rp; /* this process is now runnable */ 00504 { 00510 int q; /* scheduling queue to use */ 00511 int front; /* add to front or back */ 00512 00513 #if DEBUG_SCHED_CHECK 00514 check_runqueues("enqueue"); 00515 if (rp->p_ready) kprintf("enqueue() already ready process\n"); 00516 #endif 00517 00518 /* Determine where to insert to process. */ 00519 sched(rp, &q, &front); 00520
Συνάρτηση enqueue (2) 00521 /* Now add the process to the queue. */ 00522 if (rdy_head[q] == NIL_PROC) { /* add to empty queue */ 00523 rdy_head[q] = rdy_tail[q] = rp; /* create a new queue */ 00524 rp->p_nextready = NIL_PROC; /* mark new end */ 00525 } 00526 else if (front) { /* add to head of queue */ 00527 rp->p_nextready = rdy_head[q]; /* chain head of queue */ 00528 rdy_head[q] = rp; /* set new queue head */ 00529 } 00530 else { /* add to tail of queue */ 00531 rdy_tail[q]->p_nextready = rp; /* chain tail of queue */ 00532 rdy_tail[q] = rp; /* set new queue tail */ 00533 rp->p_nextready = NIL_PROC; /* mark new end */ 00534 } 00535 00536 /* Now select the next process to run. */ 00537 pick_proc(); 00538 00539 #if DEBUG_SCHED_CHECK 00540 rp->p_ready = 1; 00541 check_runqueues("enqueue"); 00542 #endif 00543 }
Συνάρτηση dequeue (1) • ́Οταν αφαιρούμε μια διεργασία, ελέγχουμε αν είναι κάποια που εκτελείται στην περιοχή του πυρήνα (kernel space) • Αρχικά ελέγχουμε την ακεραιότητα της μνήμης του πυρήνα (κοινή για τις διεργασίες πυρήνα και καθορισμένη από το hardware) • Εάν βρεθεί πρόβλημα, είναι αδύνατο να το διορθώσουμε ...
Συνάρτηση dequeue (2) 00548 PRIVATE void dequeue(rp) 00549 register structproc *rp; /* this process is no longer runnable */ 00550 { 00551 /* A process must be removed from the scheduling queues, for example, because 00552 * it has blocked. If the currently active process is removed, a new process 00553 * is picked to run by calling pick_proc(). 00554 */ 00555 register int q = rp->p_priority; /* queue to use */ 00556 register structproc **xpp; /* iterate over queue */ 00557 register structproc *prev_xp; 00558 00559 /* Side-effect for kernel: check if the task's stack still is ok? */ 00560 if (iskernelp(rp)) { 00561 if (*priv(rp)->s_stack_guard != STACK_GUARD) 00562 panic("stack overrun by task", proc_nr(rp)); 00563 } 00564 00565 #if DEBUG_SCHED_CHECK 00566 check_runqueues("dequeue"); 00567 if (! rp->p_ready) kprintf("dequeue() already unready process\n"); 00568 #endif 00569
Συνάρτηση dequeue (3) 00570 /* Now make sure that the process is not in its ready queue. Remove the 00571 * process if it is found. A process can be made unready even if it is not 00572 * running by being sent a signal that kills it. 00573 */ 00574 prev_xp = NIL_PROC; 00575 for (xpp = &rdy_head[q]; *xpp != NIL_PROC; xpp = &(*xpp)->p_nextready) { 00576 00577 if (*xpp == rp) { /* found process to remove */ 00578 *xpp = (*xpp)->p_nextready; /* replace with next chain */ 00579 if (rp == rdy_tail[q]) /* queue tail removed */ 00580 rdy_tail[q] = prev_xp; /* set new tail */ 00581 if (rp == proc_ptr || rp == next_ptr) /* active process removed */ 00582 pick_proc(); /* pick new process to run */ 00583 break; 00584 } 00585 prev_xp = *xpp; /* save previous in chain */ 00586 } 00587 00588 #if DEBUG_SCHED_CHECK 00589 rp->p_ready = 0; 00590 check_runqueues("dequeue"); 00591 #endif 00592 }
MINIX 3.2.1. • (fromhttp://www.minix3.org/docs/scheduling/report.pdf) Δύο προσεγγίσεις είχαν συζητηθεί σε όρους χρόνους απόφασης δρομολόγησης: event-driven και pro-active δρομολόγηση . Στην πρώτη περίπτωση ο δρομολογητής θα είναι αδρανής και θα αντιδρά σε μυνήματα που στέλνονται απο τον πυρηνα. Για παράδειγμα όταν τελειώσει το κβάντο χρόνου (non quantum). Στην pro-active o δρομολογητής περιοδικά θα ρωτάει τον πυρήνα για την κατασταση της διεργασίας και θα κάνε προσαρμογές όπως απαιτείται. Ένας event-driven δρομολογητής απαντεί πιο γρήγορα σε αλλαγές στη διεργασία όμως απαιτεί kernel call up καιεξάρτηση απο ένα user-mode scheduling. Eν αντιθέσει με ενα prο-active scheduler σε περίπτωση αποτυχίας ο πυρήνας θα δρομολογήσει τη διεργασία.
Interface Πόλλές πολιτικές έχουν υλοποιηθεί μέσα από ένα πολύ απλό interface. Δύο system library routines έχουν εκτεθεί στο user mode, sys_schedctlκαι sys_schedule. Στέλνουνε μήνυμα στον πύρηνα απαιτώντας την εκκίνηση της δρομολόγησης μίας συγκεκριμένης διεργασίας και δινουν στη διεργασία κβάντα και μία προτεραιότητα αντίστοιχα . Οταν μίας διεργασίας τελειώσει το κβάντο ο user-mode δρομολογητής ενημερώνεται με ένα μήνυμα το οποίο περιέχει system και process feedback που ο δρομολογητής μπορεί να χρησιμοποιήσει για την πραγματοποίηση αποφάσεων δρομολόγησης.
Kernel Implementation O πυρήνας διατηρεί n ουρές προτεραιότητας και υλοποιεί ένα απλό preemptive round robin scheduler που προκρίνει τη διεργασία που έιναι στη κεφαλή της ουράς προτεραιότητας. Όταν μίας διεργασίας τελειώνει το κβάντο ο πυρήνας θέτει το RTS_NO_QUANTUM flag απομακρύνει τη διεργασία από την ουρά και την μαρκάρει ως not runnable. Process table entries char p_priority; /* current process running */ u64_t p_cpu_time_left; /* time left to use the cpu */ unsigned p_quantum_size_ms; /* assigned time quantum in ms*/ struct proc *p_scheduler; /* who gets out of quantum msg */ O δρομολογητής ενημερώνεται όταν μία διεργασία τελειώσει από κβάντο από μήνυμα στο δρομολογητή εκ μέρους της διεργασίας. System calls Δύο system library routines έχουν εκτεθεί στο user mode, sys_schedctl και sys_schedule. Οsys_schedctlκαλείται από το user mode scheduler για την δρομολόγηση μίας διεργασίας. Ο πυρηνας θα επισημάνει το scheduler endpoint και θα στείλει το out of quantum στο δρομολογητή. O δρομολογητής θα πάρει την απόφασή του και θα επαναδρομολογήσει χρησιμοποιώντας τη sys_schedule system call βάζοντας τη διεργασία σε μία διαφορετική ουρά ή με διαφορετικό κβάντο. Το sys_scheduleμπορεί να αλλάξει την προτεραιότητα και το κβάντο μία τρέχουσας διεργασίας.
Default Kernel Policy Αρχικά οι διεργασίες μπορεί να μην εχουν κβάντα, και ίσως ο δρομολογητής να έχει μπλοκάρει στην εκτέλεσή του, συνεπώς ίσως να είναι επιθυμητό ο πυρήνας να αναλάβει τη δρομολόγηση για ένα μικρό χρονικό διάστημα μέχρις ότου ξεκινήσει ο νέος δρομολογητής. Τέλος για την αποφυγή αδιεξόδων ίσως να είναι απαραίτητο να έχουμε το πυρήνα δρομολογήσει μερικούς εξυπηρετητές όπως ο scheduler και RS. H εκ των προτέρων πολιτικη του πυρήνα και εφαρμόζεται όταν η διεργασία p_schedulerείναι ίση με ΝULL. ΄Όταν η διεργασία τελειώσει από κβάντα γίνεται preempted αλλά αμέσως τοποθετείται στο τελος της τρέχουσας ουρας προτεραιότητας με ένα νέο κβάντο (βασισμένο στο p_quantum_size_ms).
User Mode Implementation Process Manager (PM) Το PM’s process table, έχει δύο πεδία που σχετίζονται με τη δρομολόγηση: mp_niceκαι mp_scheduler. O PM είναι η μόνη user mode process που ξέρει ποιο δρομολογείται και από ποιον, η πληροφορία αποθηκεύεται στο mp_schedulerή στο KERNEL. SCHED O δρομολογητής μπλοκάρει περιμένοντας μηνύματα ελέγχου από το PM ή out of quantum μηνύματα από διεργασίες. Στη λήψη ενός μηνύματος ελέγχου ο δρομολογητής θα αναλάβει ή θα εγκαταλείψει μία συγκεκριμένη διεργασία (σε συνδυασμό fork, exit). Όταν ο δρομολογητής λαβει ένα out-of-quantum μήνυμα χρειάζεται να επαναδρομολογήσει μία διεργασία χρησιμοποιώντας sys_schedule. H συγκεκριμένη πολιτική μιμείται την πολιτική που είχε προηγουμένως υλοποιηθεί στον πυρήνα, και κάθε διεργασία που δεν έχει πια κβάντο κατεβαίνει σε προτεραιότητα κατά ένα στη συνέχεια ο δρομολογητής θα ανεβεί μία ουρά κατά ένα.
(fromhttp://www.minix3.org/docs/scheduling/report.pdf) Eκκίνηση του Συστήματος Η main() του πυρήνα θα συμπληρώσει τον πίνακα από το struct boot_image . Οι διεργασίες παίρνουν ένα προσδιορισμένο κβάντο αλλά όχι δρομολογητή πράγμα που σημαίνει ότι ο προγραμματισμός γίνεται από τον πυρήνα . PM γεμίζει τον τοπικό πίνακα διεργασιών από την boot_imageδομή στο τέλος της main() θα στείλει στον δρομολογητή μία αίτηση για την εκκίνηση της δρομολόγησης. Θα γίνει αίτηση δρομολόγησης της init και η init θα είναι η μόνη μη non-privileged process στο PM και χωρίς κβάντο.
Δημιουργία νέας διεργασίας Κανονικά οι διεργασίες παίρνουν το μισό κβάντο της γονικής διεργασίας Σε ποια προτεραιότητα η διεργασία θα τοποθετηθεί εξαρτάται από το user mode scheduler, εντουτοις είναι επιθυμητό για τις διεργασίες παιδιά να κληρονομήσουν όχι μόνο τη βασική προτεραιοτητα της γονικής διαδικασίας αλλά και τη τρέχουσας προτεραιότητα. Η βασική προτεραιότητα είναι η γενική προτεραιότητα βασικά η ψηλότερη προτεραιότητα που η διεργασία μπορεί να έχει μία χρονική στιγμή. Η βασική προτεραιότητα είναι τοπική στον scheduler, δεν επιδεικνύεται στον πυρήνα και τυπικά επηρεάζεται από το nice. H τρέχουσα προτεραιότητα επιδεικνύει σε ποια ουρά θα μπει μία διεργασία.