Λειτουργικά Συστήματα (ΗΥ-345)
Χειμερινό Εξάμηνο 2014-2015
Άσκηση 1



Φροντιστήριο: 9/10/2014
Παράδοση: 24/10/2014

Υλοποίηση Linux C shell: "csd_sh"


Σε αυτή την άσκηση θα χρειαστεί να φτιάξετε ένα πρόγραμμα σε γλώσσα C που θα υλοποιεί ένα C shell (command interpreter) με όνομα "csd_sh" στο λειτουργικό σύστημα Linux. Σε αυτήν την άσκηση θα εξοικιωθείτε με τη δημιουργία, τον συγχρονισμό, την εκτέλεση διεργασιών και τις κλήσεις συστήματος (system calls) fork(), wait() και exec(), με την επικοινωνία διεργασιών μέσω pipes και την μέτρηση χρόνου εκτέλεσης διεργασιών.

Το prompt του csd_sh shell θα είναι:

όπου αντί για user θα τυπώνει το όνομα του χρήστη που είναι logged-in στο σύστημα (δείτε την συνάρτηση βιβλιοθήκης getlogin()) και αντί για dir θα τυπώνει το τρέχον directory (δείτε την συνάρτηση βιβλιοθήκης getcwd()).
Με το csd_sh θα πρέπει να μπορεί ένας χρήστης:
  1. να δίνει τις εντολές του και αυτές να εκτελούνται
  2. να κατεθύνει την είσοδο των εντολών από αρχεία,
  3. να κατεθύνει την έξοδο των εντολών σε αρχεία,
  4. να κατεθύνει την έξοδο μιας εντολής στην είσοδο μίας άλλης εντολής χρησιμοποιώντας pipes,
  5. να έχει στην διάθεσή του κάποιες εσωτερικές εντολές του csd_sh, όπως την εντολή "csdTime" όπου κατά την διαδικάσία εκτέλεσης μιας οποιαδήποτε διεργασίας, θα μπορεί με την χρήση της εντολής αυτής να λαμβάνει 3 διαφορετικούς χρόνους εκτέλεσης: (δείτε για παράδειγμα την εντολή time στο λειτουργικό σύστημα Linux)

Πιο αναλυτικά, το csd_sh θα έχει τις παρακάτω λειτουργίες:
  1. Το shell θα διαβάζει εντολές από τον χρήστη και θα τις εκτελεί.

    Αφού διαβάσει μια γραμμή εντολών, το csd_sh θα χρησιμοποιεί το system call fork(), για να δημιουργήσει μια καινούρια διεργασία-παιδί η οποία θα εκτελεί την εντολή του χρήστη. Η εκτέλεση της εντολής από τη διεργασία-παιδί θα γίνεται με τη χρήση ενός από τα system calls της οικογένειας exec()

    Η πατρική διεργασία θα περιμένει τη διεργασία-παιδί να τελειώσει την εκτέλεσή της (χρησιμοποιώντας το system call wait()). Ύστερα, θα επιστρέφει στο prompt περιμένοντας τη νέα εντολή του χρήστη, εκτός αν ο χρήστης έχει πληκτρολογήσει το "&" στο τέλος της εντολής όπου αυτό σημαίνει ότι η διεργασία θα τρέχει ως background process, οπότε η πατρική διεργασία δεν θα περιμένει αλλά θα επιστρέφει αμέσως στο prompt για νέα εντολή. Σε αυτήν την περίπτωση πριν επιστρέψει στο prompt για να περιμένει νέα εντολή θα πρέπει πρώτα να τυπώνει το όνομα και το pid της διεργασίας που τρέχει στο παρασκήνιο.

  2. Ο χρήστης του csd_sh θα μπορεί να κατευθύνει την έξοδο μιας εντολής σε ένα αρχείο χρησιμοποιώντας το σύμβολο ">" ως εξής:

    όπου command η εντολή που εκτελείται και filename ένα όνομα αρχείου στο οποίο θα γραφτούν τα δεδομένα της εξόδου, π.χ., το

    θα πρέπει να γράφει τα περιεχόμενα του καταλόγου my_files στο αρχείο με όνομα output_file. Για αυτό θα σας φανεί χρήσιμο το system call dup2().
  3. Ο χρήστης του csd_sh θα μπορεί να κατευθύνει ένα αρχείο στην είσοδο του προγράμματος που εκτελεί το csd_sh χρησιμοποιοώντας το σύμβολο "<" ως εξής:

    όπου command η εντολή που εκτελείται και filename ένα όνομα αρχείου από το οποίο θα διαβαστούν τα δεδομένα της εισόδου, π.χ., το

    θα πρέπει να δίνει στο πρόγραμμα cat τα περιεχόμενα του αρχείου input_file για να τα εμφανίζει στην οθόνη. Για αυτήν την λειτουργιά θα σας φανεί επίσης χρήσιμο το system call dup2().
  4. Επίσης, η έξοδος μιας εντολής θα μπορεί να δίνεται σαν είσοδος σε μια άλλη εντολή που υπάρχει στην ίδια γραμμή εντολών και διαχωρίζονται με το σύμβολο "|" μεταξύ τους. Παραδείγματα:
    Για την υλοποίηση αυτής της λειτουργίας θα σας φανεί χρήσιμο το system call pipe().
  5. Εκτός από τις εντολές του χρήστη, το csd_sh θα υποστηρίζει και τις παρακάτω δικές του εντολές :

    1. cd: Η εντολή αυτή θα δέχεται ένα όρισμα που δηλώνει τον κατάλογο (directory) ο οποίος θα πρέπει να γίνει το νέο τρέχoν directory (δείτε την κλήση συστήματος chdir()).
    2. setenv/unsetenv: Με την εντολή setenv (setenv VAR [VALUE]) ο χρήστης θα πρέπει να μπορεί να προσθέσει/αλλάξει τις environment variables του shell environment (δείτε την κλήση συστήματος setenv()) π.χ.

      Mε την unsetenv (unsetenv VAR) ο χρήστης θα πρέπει να μπορεί να διαγράφει ένα environment variable από το shell environment (δείτε την κλήση συστήματος unsetenv()) π.χ.

      Προσοχή! Tα environment variables δεν θα πρέπει να συγχέονται με τα shell variables. Εδώ μπορείτε να βρείτε πληροφορίες για το τι είναι τα environment variables και που διαφέρουν από τα shell variables.
    3. csdTime: Η εντολή csdTime θα δέχεται σαν όρισμα ένα πρόγραμμα (μαζί με όλα τα ορίσματα που πιθανώς θα δέχεται το πρόγραμμα) το οποίο θα τρέχει κανονικά στο shell, αλλά στο τέλος της εκτέλεσής του θα εκτυπώνει επίσης στην κονσόλα (και όχι σε κάποιο αρχείο που ίσως έχει δωθεί για την έξοδο του προγράμματος) τους παρακάτω χρόνους εκτέλεσης του προγράμματος: (α) τον πραγματικό χρόνο που πέρασε (real time), (β) τον χρόνο που το πρόγραμμα έτρεξε στην CPU στο επίπεδο χρήστη (user time), και (γ) τον χρόνο που έτρεξε στην CPU το σύστημα για να εξυπηρετήσει το συγκεκριμενένο πρόγραμμα, π.χ., εκτελώντας system calls (system time). Για την μέτρηση των παραπάνω χρόνων θα σας φανούν χρήσιμα το system calls gettimeofday() και times().
    4. exit: Με την εντολή exit θα τερματίζεται η λειτουργία του csd_sh.


Reference - man pages
Ένα man page περιγράφει τον τρόπο λειτουργίας ενός προγράμματος, ενός system call ή μιας library function. Η εμφάνιση ενός man page γίνεται με τη χρήση της εντολής:

man(1)

Για να δείτε το man page (σε Linux) που αναφέρεται στη foo(N), κάνετε:

% man -S N foo

Ο συμβολισμός foo(N) αναφέρεται στο man page που περιγράφει τη foo στη κατηγορία (section) 'Ν'.

Λίστα με χρήσιμα man pages για την Aσκηση 1

Σας παραθέτουμε man pages με system calls που μπορεί να χρειαστείτε για την υλοποίηση της άσκησης. Η παρακάτω λίστα δεν είναι δεσμευτική. Μπορείτε να χρησιμοποιήσετε και εναλλακτικούς τρόπους.

fork(2)
exec(3)
execv(3)
wait(2)
waitpid(2)
pipe(2)
dup2(2)
times(2)
time(2)
time(1)
sh(1)
bash(1)
setenv(3)
gettimeofday(2)
chdir(2)
getcwd(2)
getlogin(2)
 

Παρατηρήσεις

  1. Η άσκηση είναι ατομική. Τυχόν αντιγραφές μπορούν να ανιχνευθούν εύκολα από κατάλληλο πρόγραμμα και θα μηδενιστούν. Συμπεριλάβετε το όνομα σας και το λογαριασμό σας (account) σε όλα τα αρχεία.
  2. Κατασκευάστε ένα αρχείο Makefile, έτσι ώστε πληκτρολογώντας make all να γίνεται η μεταγλώττιση (compilation) του προγράμματος και να παράγεται το εκτελέσιμο αρχείο. Επίσης πληκτρολογώντας make clean να καθαρίζονται όλα τα περιττά αρχεία, και να μένουν μόνο τα αρχεία που χρειάζονται για τη μεταγλώττιση.
  3. Επιπλέον, γράψτε και ένα αρχείο readme.txt το πολύ 30 γραμμών που να περιέχει επεξηγήσεις για τον τρόπο υλοποίησης και παραδείγματα για τη χρήση του csd_sh που υλοποιήσατε.
  4. Τοποθετήστε σε ένα κατάλογο όλα τα αρχεία που χρειάζονται για την άσκηση 1. Παραδώστε τα παραπάνω αρχεία χρησιμοποιώντας το πρόγραμμα turnin (πληκτρολογήστε turnin assignment_1@hy345 directory_name από τον κατάλογο που περιέχει τον κατάλογο directory_name με τα αρχέια της άσκησης).
  5. Σε πολλές περιπτώσεις τα ονόματα των συναρτήσεων βιβλιοθήκης είναι ενδεικτικά. Μπορείτε να χρησιμοποιήσετε όποια σας βολεύουν.