Cron στο Linux: ιστορικό, χρήση και συσκευή

Cron στο Linux: ιστορικό, χρήση και συσκευή

Ο κλασικός έγραψε ότι οι χαρούμενες ώρες δεν παρακολουθούν. Εκείνες τις άγριες εποχές δεν υπήρχαν ούτε προγραμματιστές ούτε Unix, αλλά σήμερα οι προγραμματιστές ξέρουν σίγουρα: η cron θα παρακολουθεί τον χρόνο αντί για αυτούς.

Τα βοηθητικά προγράμματα της γραμμής εντολών είναι και αδυναμία και αγγαρεία για μένα. Τα sed, awk, wc, cut και άλλα παλιά προγράμματα εκτελούνται από σενάρια στους διακομιστές μας καθημερινά. Πολλά από αυτά έχουν σχεδιαστεί ως εργασίες για το cron, έναν χρονοπρογραμματιστή αρχικά από τη δεκαετία του '70.

Για πολύ καιρό χρησιμοποιούσα το cron επιφανειακά, χωρίς να μπω σε λεπτομέρειες, αλλά μια μέρα, όταν αντιμετώπισα ένα σφάλμα κατά την εκτέλεση ενός σεναρίου, αποφάσισα να το εξετάσω διεξοδικά. Έτσι εμφανίστηκε αυτό το άρθρο, ενώ γράφοντάς το εξοικειώθηκα με το POSIX crontab, τις κύριες επιλογές cron σε δημοφιλείς διανομές Linux και τη δομή ορισμένων από αυτές.

Χρησιμοποιείτε Linux και εκτελείτε cron tasks; Ενδιαφέρεστε για την αρχιτεκτονική εφαρμογών συστήματος στο Unix; Τότε είμαστε στο δρόμο μας!

περιεχόμενο

Προέλευση των ειδών

Η περιοδική εκτέλεση προγραμμάτων χρήστη ή συστήματος είναι μια προφανής αναγκαιότητα σε όλα τα λειτουργικά συστήματα. Ως εκ τούτου, οι προγραμματιστές συνειδητοποίησαν την ανάγκη για υπηρεσίες που τους επιτρέπουν να σχεδιάζουν και να εκτελούν κεντρικά εργασίες εδώ και πολύ καιρό.

Τα λειτουργικά συστήματα που μοιάζουν με Unix εντοπίζουν την προέλευσή τους στην έκδοση 7 Unix, που αναπτύχθηκε στη δεκαετία του '70 του περασμένου αιώνα στα Bell Labs, συμπεριλαμβανομένου του διάσημου Ken Thompson. Η έκδοση 7 Unix περιελάμβανε επίσης το cron, μια υπηρεσία για την τακτική εκτέλεση εργασιών υπερχρήστη.

Ένα τυπικό σύγχρονο cron είναι ένα απλό πρόγραμμα, αλλά ο αλγόριθμος λειτουργίας της αρχικής έκδοσης ήταν ακόμη πιο απλός: η υπηρεσία ξυπνούσε μία φορά το λεπτό, διάβαζε έναν πίνακα με εργασίες από ένα μόνο αρχείο (/etc/lib/crontab) και εκτελούσε για το superuser εκείνες τις εργασίες που θα έπρεπε να έχουν εκτελεστεί την τρέχουσα στιγμή.

Στη συνέχεια, βελτιωμένες εκδόσεις της απλής και χρήσιμης υπηρεσίας παρασχέθηκαν με όλα τα λειτουργικά συστήματα που μοιάζουν με Unix.

Γενικευμένες περιγραφές της μορφής crontab και οι βασικές αρχές λειτουργίας του βοηθητικού προγράμματος συμπεριλήφθηκαν στο κύριο πρότυπο λειτουργικών συστημάτων τύπου Unix - POSIX - το 1992, και έτσι το cron από ένα de facto πρότυπο έγινε de jure πρότυπο.

Το 1987, ο Paul Vixie, έχοντας ερευνήσει τους χρήστες του Unix σχετικά με τις επιθυμίες τους για το cron, κυκλοφόρησε μια άλλη έκδοση του δαίμονα που διόρθωσε ορισμένα από τα προβλήματα του παραδοσιακού cron και επέκτεινε τη σύνταξη των αρχείων πίνακα.

Με την τρίτη έκδοση του Vixie cron άρχισε να πληροί τις απαιτήσεις POSIX, επιπλέον, το πρόγραμμα είχε μια ελεύθερη άδεια ή μάλλον δεν υπήρχε καθόλου άδεια, εκτός από τις επιθυμίες στο README: ο συγγραφέας δεν παρέχει εγγυήσεις, το όνομα του συγγραφέα δεν μπορεί να διαγραφεί και το πρόγραμμα μπορεί να πωληθεί μόνο μαζί με τον πηγαίο κώδικα. Αυτές οι απαιτήσεις αποδείχτηκαν συμβατές με τις αρχές του ελεύθερου λογισμικού που κέρδιζε δημοτικότητα εκείνα τα χρόνια, έτσι μερικές από τις βασικές διανομές Linux που εμφανίστηκαν στις αρχές της δεκαετίας του '90 έλαβαν το Vixie cron ως το σύστημα τους και το αναπτύσσουν ακόμα σήμερα.

Συγκεκριμένα, η Red Hat και η SUSE αναπτύσσουν ένα fork του Vixie cron - cronie και το Debian και το Ubuntu χρησιμοποιούν την αρχική έκδοση του Vixie cron με πολλά patches.

Ας εξοικειωθούμε πρώτα με το crontab του βοηθητικού προγράμματος χρήστη που περιγράφεται στο POSIX, μετά από το οποίο θα δούμε τις επεκτάσεις σύνταξης που παρέχονται στο Vixie cron και τη χρήση παραλλαγών του Vixie cron σε δημοφιλείς διανομές Linux. Και τέλος, το cherry on the cake είναι η ανάλυση της συσκευής cron daemon.

Crontab POSIX

Εάν το αρχικό cron λειτουργούσε πάντα για τον υπερχρήστη, οι σύγχρονοι προγραμματιστές συχνά ασχολούνται με εργασίες απλών χρηστών, κάτι που είναι πιο ασφαλές και βολικό.

Τα Cron παρέχονται ως ένα σύνολο δύο προγραμμάτων: το διαρκώς εκτελούμενο cron daemon και το βοηθητικό πρόγραμμα crontab που είναι διαθέσιμο στους χρήστες. Το τελευταίο σάς επιτρέπει να επεξεργάζεστε πίνακες εργασιών ειδικά για κάθε χρήστη του συστήματος, ενώ ο δαίμονας εκκινεί εργασίες από πίνακες χρήστη και συστήματος.

В Πρότυπο POSIX η συμπεριφορά του δαίμονα δεν περιγράφεται με κανέναν τρόπο και επισημοποιείται μόνο το πρόγραμμα χρήστη crontab. Η ύπαρξη μηχανισμών για την εκκίνηση εργασιών χρήστη, φυσικά, υπονοείται, αλλά δεν περιγράφεται λεπτομερώς.

Καλώντας το βοηθητικό πρόγραμμα crontab, μπορείτε να κάνετε τέσσερα πράγματα: να επεξεργαστείτε τον πίνακα εργασιών του χρήστη στο πρόγραμμα επεξεργασίας, να φορτώσετε τον πίνακα από ένα αρχείο, να εμφανίσετε τον τρέχοντα πίνακα εργασιών και να διαγράψετε τον πίνακα εργασιών. Παραδείγματα για το πώς λειτουργεί το βοηθητικό πρόγραμμα crontab:

crontab -e # редактировать таблицу задач
crontab -l # показать таблицу задач
crontab -r # удалить таблицу задач
crontab path/to/file.crontab # загрузить таблицу задач из файла

Κατά την κλήση crontab -e θα χρησιμοποιηθεί ο επεξεργαστής που καθορίζεται στην τυπική μεταβλητή περιβάλλοντος EDITOR.

Οι ίδιες οι εργασίες περιγράφονται στην ακόλουθη μορφή:

# строки-комментарии игнорируются
#
# задача, выполняемая ежеминутно
* * * * * /path/to/exec -a -b -c
# задача, выполняемая на 10-й минуте каждого часа
10 * * * * /path/to/exec -a -b -c
# задача, выполняемая на 10-й минуте второго часа каждого дня и использующая перенаправление стандартного потока вывода
10 2 * * * /path/to/exec -a -b -c > /tmp/cron-job-output.log

Τα πρώτα πέντε πεδία των εγγραφών: λεπτά [1..60], ώρες [0..23], ημέρες του μήνα [1..31], μήνες [1..12], ημέρες της εβδομάδας [0. .6], όπου 0 είναι Κυριακή. Το τελευταίο, έκτο, πεδίο είναι μια γραμμή που θα εκτελεστεί από τον τυπικό διερμηνέα εντολών.

Στα πρώτα πέντε πεδία, οι τιμές μπορούν να παρατίθενται διαχωρισμένες με κόμματα:

# задача, выполняемая в первую и десятую минуты каждого часа
1,10 * * * * /path/to/exec -a -b -c

Ή με παύλα:

# задача, выполняемая в каждую из первых десяти минут каждого часа
0-9 * * * * /path/to/exec -a -b -c

Η πρόσβαση χρήστη στον προγραμματισμό εργασιών ρυθμίζεται στο POSIX από τα αρχεία cron.allow και cron.deny, στα οποία αναφέρονται οι χρήστες με πρόσβαση στο crontab και οι χρήστες χωρίς πρόσβαση στο πρόγραμμα, αντίστοιχα. Το πρότυπο δεν ρυθμίζει με κανέναν τρόπο τη θέση αυτών των αρχείων.

Σύμφωνα με το πρότυπο, τουλάχιστον τέσσερις μεταβλητές περιβάλλοντος πρέπει να περάσουν στα προγράμματα που έχουν ξεκινήσει:

  1. HOME - αρχικός κατάλογος χρήστη.
  2. LOGNAME — σύνδεση χρήστη.
  3. Το PATH είναι το μονοπάτι όπου μπορείτε να βρείτε τυπικά βοηθητικά προγράμματα συστήματος.
  4. SHELL — διαδρομή προς τον χρησιμοποιούμενο διερμηνέα εντολών.

Σημειωτέον, το POSIX δεν λέει τίποτα σχετικά με το από πού προέρχονται οι τιμές για αυτές τις μεταβλητές.

Best seller - Vixie cron 3.0pl1

Ο κοινός πρόγονος των δημοφιλών παραλλαγών cron είναι το Vixie cron 3.0pl1, που παρουσιάστηκε στη λίστα αλληλογραφίας comp.sources.unix το 1992. Θα εξετάσουμε τα κύρια χαρακτηριστικά αυτής της έκδοσης με περισσότερες λεπτομέρειες.

Το Vixie cron διατίθεται σε δύο προγράμματα (cron και crontab). Ως συνήθως, ο δαίμονας είναι υπεύθυνος για την ανάγνωση και την εκτέλεση εργασιών από τον πίνακα εργασιών του συστήματος και τους πίνακες εργασιών μεμονωμένων χρηστών και το βοηθητικό πρόγραμμα crontab είναι υπεύθυνο για την επεξεργασία πινάκων χρηστών.

Πίνακας εργασιών και αρχεία ρυθμίσεων

Ο πίνακας εργασιών υπερχρήστη βρίσκεται στο /etc/crontab. Η σύνταξη του πίνακα συστήματος αντιστοιχεί στη σύνταξη του Vixie cron, με την εξαίρεση ότι η έκτη στήλη σε αυτό υποδεικνύει το όνομα του χρήστη για λογαριασμό του οποίου εκκινείται η εργασία:

# Запускается ежеминутно от пользователя vlad
* * * * * vlad /path/to/exec

Οι πίνακες εργασιών κανονικού χρήστη βρίσκονται στο /var/cron/tabs/όνομα χρήστη και χρησιμοποιούν την ίδια σύνταξη. Όταν εκτελείτε το βοηθητικό πρόγραμμα crontab ως χρήστης, αυτά είναι τα αρχεία που επεξεργάζονται.

Η διαχείριση των λιστών των χρηστών με πρόσβαση στο crontab γίνεται στα αρχεία /var/cron/allow και /var/cron/deny, όπου χρειάζεται απλώς να εισαγάγετε το όνομα χρήστη σε ξεχωριστή γραμμή.

Εκτεταμένη σύνταξη

Σε σύγκριση με το Crontab POSIX, η λύση του Paul Vixey περιέχει πολλές πολύ χρήσιμες τροποποιήσεις στη σύνταξη των πινάκων εργασιών του βοηθητικού προγράμματος.

Έχει γίνει διαθέσιμη μια νέα σύνταξη πίνακα: για παράδειγμα, μπορείτε να καθορίσετε ημέρες της εβδομάδας ή μήνες ονομαστικά (Δευτ., Τρίτη και ούτω καθεξής):

# Запускается ежеминутно по понедельникам и вторникам в январе
* * * Jan Mon,Tue /path/to/exec

Μπορείτε να καθορίσετε το βήμα μέσω του οποίου ξεκινούν οι εργασίες:

# Запускается с шагом в две минуты
*/2 * * * Mon,Tue /path/to/exec

Τα βήματα και τα διαστήματα μπορούν να συνδυαστούν:

# Запускается с шагом в две минуты в первых десять минут каждого часа
0-10/2 * * * * /path/to/exec

Υποστηρίζονται διαισθητικές εναλλακτικές στη συνήθη σύνταξη (επανεκκίνηση, ετήσια, ετήσια, μηνιαία, εβδομαδιαία, καθημερινή, μεσάνυχτα, ωριαία):

# Запускается после перезагрузки системы
@reboot /exec/on/reboot
# Запускается раз в день
@daily /exec/daily
# Запускается раз в час
@hourly /exec/daily

Περιβάλλον εκτέλεσης εργασιών

Το Vixie cron σάς επιτρέπει να αλλάξετε το περιβάλλον των εφαρμογών που εκτελούνται.

Οι μεταβλητές περιβάλλοντος USER, LOGNAME και HOME δεν παρέχονται απλώς από τον δαίμονα, αλλά λαμβάνονται από ένα αρχείο passwd. Η μεταβλητή PATH έχει οριστεί σε "/usr/bin:/bin" και η μεταβλητή SHELL έχει οριστεί σε "/bin/sh". Οι τιμές όλων των μεταβλητών εκτός από το LOGNAME μπορούν να αλλάξουν στους πίνακες χρηστών.

Ορισμένες μεταβλητές περιβάλλοντος (κυρίως το SHELL και το HOME) χρησιμοποιούνται από την ίδια τη cron για την εκτέλεση της εργασίας. Δείτε πώς μπορεί να μοιάζει η χρήση του bash αντί του τυπικού sh για την εκτέλεση προσαρμοσμένων εργασιών:

SHELL=/bin/bash
HOME=/tmp/
# exec будет запущен bash-ем в /tmp/
* * * * * /path/to/exec

Τελικά, όλες οι μεταβλητές περιβάλλοντος που ορίζονται στον πίνακα (που χρησιμοποιούνται από το cron ή χρειάζονται από τη διεργασία) θα περάσουν στην τρέχουσα εργασία.

Για την επεξεργασία αρχείων, το crontab χρησιμοποιεί το πρόγραμμα επεξεργασίας που καθορίζεται στη μεταβλητή περιβάλλοντος VISUAL ή EDITOR. Εάν το περιβάλλον όπου εκτελέστηκε το crontab δεν έχει αυτές τις μεταβλητές καθορισμένες, τότε χρησιμοποιείται το "/usr/ucb/vi" (το ucb είναι πιθανώς το Πανεπιστήμιο της Καλιφόρνια, Μπέρκλεϋ).

cron σε Debian και Ubuntu

Οι προγραμματιστές του Debian και οι παράγωγες διανομές κυκλοφόρησαν εξαιρετικά τροποποιημένη έκδοση Vixie cron έκδοση 3.0pl1. Δεν υπάρχουν διαφορές στη σύνταξη των αρχείων πίνακα· για τους χρήστες είναι το ίδιο Vixie cron. Μεγαλύτερο νέο χαρακτηριστικό: Υποστήριξη syslog, SELinux и PAM.

Λιγότερο αισθητές, αλλά απτές αλλαγές περιλαμβάνουν τη θέση των αρχείων διαμόρφωσης και των πινάκων εργασιών.

Οι πίνακες χρηστών στο Debian βρίσκονται στον κατάλογο /var/spool/cron/crontabs, ο πίνακας συστήματος είναι ακόμα εκεί - στο /etc/crontab. Οι πίνακες εργασιών για συγκεκριμένο πακέτο του Debian τοποθετούνται στο /etc/cron.d, από όπου ο δαίμονας cron τους διαβάζει αυτόματα. Ο έλεγχος πρόσβασης χρήστη ελέγχεται από τα αρχεία /etc/cron.allow και /etc/cron.deny.

Το προεπιλεγμένο κέλυφος εξακολουθεί να είναι /bin/sh, το οποίο στο Debian είναι ένα μικρό φλοιό συμβατό με POSIX παύλα, ξεκίνησε χωρίς να διαβάσει καμία διαμόρφωση (σε μη διαδραστική λειτουργία).

Το ίδιο το Cron στις πιο πρόσφατες εκδόσεις του Debian εκκινείται μέσω του systemd και η διαμόρφωση εκκίνησης μπορεί να προβληθεί στο /lib/systemd/system/cron.service. Δεν υπάρχει τίποτα ιδιαίτερο στη διαμόρφωση της υπηρεσίας· οποιαδήποτε πιο λεπτή διαχείριση εργασιών μπορεί να γίνει μέσω μεταβλητών περιβάλλοντος που δηλώνονται απευθείας στο crontab κάθε χρήστη.

cronie σε RedHat, Fedora και CentOS

cronie — fork του Vixie cron έκδοση 4.1. Όπως και στο Debian, η σύνταξη δεν έχει αλλάξει, αλλά έχει προστεθεί υποστήριξη για PAM και SELinux, εργασία σε σύμπλεγμα, παρακολούθηση αρχείων με χρήση inotify και άλλες δυνατότητες.

Η προεπιλεγμένη διαμόρφωση είναι στα συνηθισμένα σημεία: ο πίνακας συστήματος είναι στο /etc/crontab, τα πακέτα τοποθετούν τους πίνακές τους στο /etc/cron.d, οι πίνακες χρηστών πηγαίνουν στο /var/spool/cron/crontabs.

Ο δαίμονας εκτελείται υπό τον έλεγχο systemd, η διαμόρφωση της υπηρεσίας είναι /lib/systemd/system/crond.service.

Σε διανομές τύπου Red Hat, το /bin/sh χρησιμοποιείται από προεπιλογή κατά την εκκίνηση, που είναι το τυπικό bash. Θα πρέπει να σημειωθεί ότι κατά την εκτέλεση εργασιών cron μέσω /bin/sh, το κέλυφος bash ξεκινά σε λειτουργία συμβατή με POSIX και δεν διαβάζει καμία πρόσθετη διαμόρφωση, που εκτελείται σε μη διαδραστική λειτουργία.

cronie στο SLES και στο openSUSE

Η γερμανική διανομή SLES και το παράγωγό του openSUSE χρησιμοποιούν το ίδιο cronie. Ο δαίμονας εδώ εκκινείται επίσης στο systemd, η διαμόρφωση της υπηρεσίας βρίσκεται στο /usr/lib/systemd/system/cron.service. Διαμόρφωση: /etc/crontab, /etc/cron.d, /var/spool/cron/tabs. Το /bin/sh είναι το ίδιο bash που εκτελείται σε μη διαδραστική λειτουργία συμβατή με το POSIX.

Συσκευή Vixie cron

Οι σύγχρονοι απόγονοι του cron δεν έχουν αλλάξει ριζικά σε σύγκριση με το Vixie cron, αλλά εξακολουθούν να έχουν αποκτήσει νέα χαρακτηριστικά που δεν απαιτούνται για την κατανόηση των αρχών του προγράμματος. Πολλές από αυτές τις επεκτάσεις είναι κακώς σχεδιασμένες και προκαλούν σύγχυση στον κώδικα. Ο αρχικός πηγαίος κώδικας cron από τον Paul Vixey είναι ευχάριστο να διαβάζεται.

Επομένως, αποφάσισα να αναλύσω τη συσκευή cron χρησιμοποιώντας το παράδειγμα ενός προγράμματος cron που είναι κοινό και στους δύο κλάδους ανάπτυξης - Vixie cron 3.0pl1. Θα απλοποιήσω τα παραδείγματα αφαιρώντας τα ifdef που περιπλέκουν την ανάγνωση και παραλείποντας μικρές λεπτομέρειες.

Το έργο του δαίμονα μπορεί να χωριστεί σε διάφορα στάδια:

  1. Αρχικοποίηση προγράμματος.
  2. Συλλογή και ενημέρωση της λίστας εργασιών προς εκτέλεση.
  3. Ο κύριος βρόχος cron λειτουργεί.
  4. Ξεκινήστε μια εργασία.

Ας τα δούμε με τη σειρά.

Αρχικοποίηση

Κατά την εκκίνηση, αφού ελέγξει τα ορίσματα διεργασίας, το cron εγκαθιστά τους χειριστές σήματος SIGCHLD και SIGHUP. Ο πρώτος κάνει μια καταχώριση καταγραφής σχετικά με τον τερματισμό της θυγατρικής διαδικασίας, ο δεύτερος κλείνει τον περιγραφέα αρχείου του αρχείου καταγραφής:

signal(SIGCHLD, sigchld_handler);
signal(SIGHUP, sighup_handler);

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

acquire_daemonlock(0);
set_cron_uid();
set_cron_cwd();

Έχει οριστεί η προεπιλεγμένη διαδρομή, η οποία θα χρησιμοποιηθεί κατά την εκκίνηση διεργασιών:

setenv("PATH", _PATH_DEFPATH, 1);

Στη συνέχεια, η διαδικασία "δαιμονοποιείται": δημιουργεί ένα θυγατρικό αντίγραφο της διαδικασίας καλώντας το fork και μια νέα συνεδρία στη θυγατρική διαδικασία (κλήση setsid). Η γονική διαδικασία δεν χρειάζεται πλέον και εξέρχεται:

switch (fork()) {
case -1:
    /* критическая ошибка и завершение работы */
    exit(0);
break;
case 0:
    /* дочерний процесс */
    (void) setsid();
break;
default:
    /* родительский процесс завершает работу */
    _exit(0);
}

Ο τερματισμός της γονικής διαδικασίας απελευθερώνει το κλείδωμα στο αρχείο κλειδώματος. Επιπλέον, απαιτείται ενημέρωση του PID του αρχείου στο παιδί. Μετά από αυτό, συμπληρώνεται η βάση δεδομένων εργασιών:

/* повторный захват лока */
acquire_daemonlock(0);

/* Заполнение БД  */
database.head = NULL;
database.tail = NULL;
database.mtime = (time_t) 0;
load_database(&database);

Στη συνέχεια, το cron προχωρά στον κύριο κύκλο εργασίας. Αλλά πριν από αυτό, αξίζει να ρίξετε μια ματιά στη φόρτωση της λίστας εργασιών.

Συλλογή και ενημέρωση της λίστας εργασιών

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

Φόρτωση ενός αρχείου συστήματος με ειδικά ονόματα αρχείων και πινάκων:

/* если файл системной таблицы изменился, перечитываем */
if (syscron_stat.st_mtime) {
    process_crontab("root", "*system*",
    SYSCRONTAB, &syscron_stat,
    &new_db, old_db);
}

Φόρτωση πινάκων χρηστών σε βρόχο:

while (NULL != (dp = readdir(dir))) {
    char    fname[MAXNAMLEN+1],
            tabname[MAXNAMLEN+1];
    /* читать файлы с точкой не надо*/
    if (dp->d_name[0] == '.')
            continue;
    (void) strcpy(fname, dp->d_name);
    sprintf(tabname, CRON_TAB(fname));
    process_crontab(fname, fname, tabname,
                    &statbuf, &new_db, old_db);
}

Μετά από αυτό η παλιά βάση δεδομένων αντικαθίσταται με μια νέα.

Στα παραπάνω παραδείγματα, η κλήση της συνάρτησης process_crontab επαληθεύει ότι υπάρχει ένας χρήστης που ταιριάζει με το όνομα του αρχείου του πίνακα (εκτός εάν είναι υπερχρήστης) και στη συνέχεια καλεί το load_user. Το τελευταίο διαβάζει ήδη το ίδιο το αρχείο γραμμή προς γραμμή:

while ((status = load_env(envstr, file)) >= OK) {
    switch (status) {
    case ERR:
        free_user(u);
        u = NULL;
        goto done;
    case FALSE:
        e = load_entry(file, NULL, pw, envp);
        if (e) {
            e->next = u->crontab;
            u->crontab = e;
        }
        break;
    case TRUE:
        envp = env_set(envp, envstr);
        break;
    }
}

Εδώ, είτε ορίζεται η μεταβλητή περιβάλλοντος (γραμμές της μορφής VAR=value) χρησιμοποιώντας τις συναρτήσεις load_env / env_set, είτε διαβάζεται η περιγραφή της εργασίας (* * * * * /path/to/exec) χρησιμοποιώντας τη συνάρτηση load_entry.

Η οντότητα καταχώρισης που επιστρέφει το load_entry είναι η εργασία μας, η οποία τοποθετείται στη γενική λίστα εργασιών. Η ίδια η συνάρτηση πραγματοποιεί μια λεπτομερή ανάλυση της μορφής χρόνου, αλλά μας ενδιαφέρει περισσότερο ο σχηματισμός μεταβλητών περιβάλλοντος και παραμέτρων εκκίνησης εργασιών:

/* пользователь и группа для запуска задачи берутся из passwd*/
e->uid = pw->pw_uid;
e->gid = pw->pw_gid;

/* шелл по умолчанию (/bin/sh), если пользователь не указал другое */
e->envp = env_copy(envp);
if (!env_get("SHELL", e->envp)) {
    sprintf(envstr, "SHELL=%s", _PATH_BSHELL);
    e->envp = env_set(e->envp, envstr);
}
/* домашняя директория */
if (!env_get("HOME", e->envp)) {
    sprintf(envstr, "HOME=%s", pw->pw_dir);
    e->envp = env_set(e->envp, envstr);
}
/* путь для поиска программ */
if (!env_get("PATH", e->envp)) {
    sprintf(envstr, "PATH=%s", _PATH_DEFPATH);
    e->envp = env_set(e->envp, envstr);
}
/* имя пользовтеля всегда из passwd */
sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name);
e->envp = env_set(e->envp, envstr);

Ο κύριος βρόχος λειτουργεί με την τρέχουσα λίστα εργασιών.

Κύριος βρόχος

Το αρχικό cron από την έκδοση 7 Unix λειτούργησε πολύ απλά: διάβασε ξανά τη διαμόρφωση σε ένα βρόχο, ξεκίνησε τις εργασίες του τρέχοντος λεπτού ως υπερχρήστης και κοιμήθηκε μέχρι την έναρξη του επόμενου λεπτού. Αυτή η απλή προσέγγιση σε παλαιότερα μηχανήματα απαιτούσε πάρα πολλούς πόρους.

Μια εναλλακτική έκδοση προτάθηκε στο SysV, στην οποία ο δαίμονας κοιμόταν είτε μέχρι το πλησιέστερο λεπτό για το οποίο είχε καθοριστεί η εργασία είτε για 30 λεπτά. Λιγότεροι πόροι καταναλώθηκαν για την εκ νέου ανάγνωση της διαμόρφωσης και τον έλεγχο των εργασιών σε αυτήν τη λειτουργία, αλλά η γρήγορη ενημέρωση της λίστας εργασιών έγινε άβολη.

Το Vixie cron επέστρεφε στον έλεγχο των λιστών εργασιών μία φορά το λεπτό, ευτυχώς μέχρι τα τέλη της δεκαετίας του '80 υπήρχαν πολύ περισσότεροι πόροι σε τυπικές μηχανές Unix:

/* первичная загрузка задач */
load_database(&database);
/* запустить задачи, поставленные к выполнению после перезагрузки системы */
run_reboot_jobs(&database);
/* сделать TargetTime началом ближайшей минуты */
cron_sync();
while (TRUE) {
    /* выполнить задачи, после чего спать до TargetTime с поправкой на время, потраченное на задачи */
    cron_sleep();

    /* перечитать конфигурацию */
    load_database(&database);

    /* собрать задачи для данной минуты */
    cron_tick(&database);

    /* перевести TargetTime на начало следующей минуты */
    TargetTime += 60;
}

Η συνάρτηση cron_sleep εμπλέκεται άμεσα στην εκτέλεση εργασιών, καλώντας τις συναρτήσεις job_runqueue (απαρίθμηση και εκτέλεση εργασιών) και do_command (εκτέλεση κάθε μεμονωμένης εργασίας). Η τελευταία συνάρτηση αξίζει να εξεταστεί λεπτομερέστερα.

Εκτέλεση μιας εργασίας

Η συνάρτηση do_command εκτελείται σε καλό στυλ Unix, δηλαδή, κάνει ένα fork για να εκτελέσει την εργασία ασύγχρονα. Η γονική διαδικασία συνεχίζει να εκκινεί εργασίες, η θυγατρική διαδικασία προετοιμάζει τη διαδικασία εργασιών:

switch (fork()) {
case -1:
    /*не смогли выполнить fork */
    break;
case 0:
    /* дочерний процесс: на всякий случай еще раз пробуем захватить главный лок */
    acquire_daemonlock(1);
    /* переходим к формированию процесса задачи */
    child_process(e, u);
    /* по завершению дочерний процесс заканчивает работу */
    _exit(OK_EXIT);
    break;
default:
    /* родительский процесс продолжает работу */
    break;
}

Υπάρχει αρκετή λογική στο child_process: παίρνει τυπική έξοδο και ροές σφαλμάτων στον εαυτό του, για να τα στείλει στη συνέχεια στο mail (εάν η μεταβλητή περιβάλλοντος MAILTO καθορίζεται στον πίνακα εργασιών) και, τέλος, περιμένει την κύρια διαδικασία για την ολοκλήρωση της εργασίας.

Η διαδικασία της εργασίας σχηματίζεται από μια άλλη διχάλα:

switch (vfork()) {
case -1:
    /* при ошибки сразу завершается работа */
    exit(ERROR_EXIT);
case 0:
    /* процесс-внук формирует новую сессию, терминал и т.д.
     */
    (void) setsid();

    /*
     * дальше многословная настройка вывода процесса, опустим для краткости
     */

    /* смена директории, пользователя и группы пользователя,
     * то есть процесс больше не суперпользовательский
     */
    setgid(e->gid);
    setuid(e->uid);
    chdir(env_get("HOME", e->envp));

    /* запуск самой команды
     */
    {
        /* переменная окружения SHELL указывает на интерпретатор для запуска */
        char    *shell = env_get("SHELL", e->envp);

        /* процесс запускается без передачи окружения родительского процесса,
         * то есть именно так, как описано в таблице задач пользователя  */
        execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);

        /* ошибка — и процесс на запустился? завершение работы */
        perror("execl");
        _exit(ERROR_EXIT);
    }
    break;
default:
    /* сам процесс продолжает работу: ждет завершения работы и вывода */
    break;
}

Αυτό είναι βασικά όλο το cron. Παρέλειψα ορισμένες ενδιαφέρουσες λεπτομέρειες, για παράδειγμα, τη λογιστική για απομακρυσμένους χρήστες, αλλά περιέγραψα το κύριο πράγμα.

Επίλογος

Το Cron είναι ένα εκπληκτικά απλό και χρήσιμο πρόγραμμα, κατασκευασμένο σύμφωνα με τις καλύτερες παραδόσεις του κόσμου του Unix. Δεν κάνει τίποτα επιπλέον, αλλά κάνει θαυμάσια τη δουλειά της εδώ και αρκετές δεκαετίες. Η απόκτηση του κώδικα για την έκδοση που συνοδεύει το Ubuntu δεν χρειάστηκε περισσότερο από μία ώρα και το διασκέδασα πολύ! Ελπίζω να μπόρεσα να το μοιραστώ μαζί σας.

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

Υπάρχουν πολλές σύγχρονες εναλλακτικές λύσεις για το cron: τα systemd-timers σάς επιτρέπουν να οργανώνετε πολύπλοκα συστήματα με εξαρτήσεις, το fcron σας επιτρέπει να ρυθμίζετε πιο ευέλικτα την κατανάλωση πόρων ανά εργασίες. Προσωπικά, όμως, μου έφτανε πάντα το πιο απλό κροστάνι.

Με λίγα λόγια, αγαπήστε το Unix, χρησιμοποιήστε απλά προγράμματα και μην ξεχάσετε να διαβάσετε το mana για την πλατφόρμα σας!

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο