Αρχές λειτουργίας σε λειτουργικά συστήματα παρόμοια με το UNIX που χρησιμοποιούν Linux ως παράδειγμα. Δημιουργήστε τη δική σας ελαφριά διαδικασία

Μια από τις πιο ενοχλητικές στιγμές κατά τη μετακίνηση από το περιβάλλον στο Βασισμένο στα Windowsγια χρήση γραμμή εντολών– απώλεια της εύκολης εκτέλεσης πολλαπλών εργασιών. Ακόμη και στο Linux, εάν χρησιμοποιείτε το σύστημα X Window, μπορείτε να χρησιμοποιήσετε το ποντίκι σας για να κάνετε απλώς κλικ νέο πρόγραμμακαι ανοίξτε το. Στη γραμμή εντολών, ωστόσο, είστε λίγο πολύ κολλημένοι με το monotasking. Σε αυτό το άρθρο θα σας δείξουμε πώς να κάνετε πολλαπλές εργασίες στο Linux χρησιμοποιώντας τη γραμμή εντολών.

Διαχείριση διαδικασιών ιστορικού και προτεραιότητας

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

Απροστάτευτος

ΠρώταΓια να στείλετε μια διαδικασία στο παρασκήνιο, πρέπει πρώτα να την αναστείλετε. Δεν είναι δυνατό να στείλετε ένα πρόγραμμα που ήδη εκτελείται στο παρασκήνιο και να το διατηρήσετε ταυτόχρονα.

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

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

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

Αλλά πρώτα - θα ανοίξετε μια νέα συνεδρία SSH

Μην ξεχνάτε ότι μόλις ανοίγετε μια νέα συνεδρία SSH.

Μπορεί να είναι άβολο να ανοίγετε συνεχώς νέες συνεδρίες. Και τότε είναι που χρειάζεστε "οθόνη"

Χρησιμότητα οθόνησας επιτρέπει να δημιουργείτε πολλαπλές ροές εργασίας ανοιχτές ταυτόχρονα - το πλησιέστερο ανάλογο στα "παράθυρα". Από προεπιλογή είναι διαθέσιμο σε κανονικά αποθετήρια Linux. Εγκαταστήστε το στο CentOS/RHEL χρησιμοποιώντας την ακόλουθη εντολή:

Οθόνη εγκατάστασης Sudo yum

Άνοιγμα νέας οθόνης

Τώρα ξεκινήστε τη συνεδρία σας πληκτρολογώντας "screen".

Αυτό θα δημιουργήσει άδειο παράθυροσε μια υπάρχουσα συνεδρία SSH και δώστε της έναν αριθμό που εμφανίζεται στην κεφαλίδα ως εξής:

Η οθόνη μας εδώ έχει τον αριθμό "0" όπως φαίνεται. Σε αυτό το στιγμιότυπο οθόνης, χρησιμοποιούμε μια εικονική εντολή "read" για να αποκλείσουμε το τερματικό και να το κάνουμε να περιμένει για εισαγωγή. Τώρα ας πούμε ότι θέλουμε να κάνουμε κάτι άλλο όσο περιμένουμε.

Για να ανοίξω νέα οθόνηκαι κάνουμε κάτι άλλο, εκτυπώνουμε:

Ctrl+a c

Το "ctrl+a" είναι ο προεπιλεγμένος συνδυασμός πλήκτρων για τον έλεγχο οθονών στο πρόγραμμα οθόνης. Αυτό που πληκτρολογείτε μετά καθορίζει τη δράση. Για παράδειγμα:

  • ctrl+a c – ντοενεργοποιεί μια νέα οθόνη
  • ctrl+a [αριθμός]– μεταβείτε σε έναν συγκεκριμένο αριθμό οθόνης
  • ctrl+a k – καπενεργοποιεί την τρέχουσα οθόνη
  • ctrl+a n – Μετάβαση στην οθόνη n
  • ctrl+a "- εμφανίζει όλες τις ενεργές οθόνες στη συνεδρία

Αν πατήσουμε “ctrl+a c” θα εμφανιστεί μια νέα οθόνη με νέο αριθμό.

Μπορείτε να χρησιμοποιήσετε τα πλήκτρα του δρομέα για να πλοηγηθείτε στη λίστα και να μεταβείτε στην οθόνη που θέλετε.
Οι οθόνες είναι ό,τι πιο κοντά θα φτάσετε στα «παράθυρα», όπως ένα σύστημα σε μια εντολή Συμβολοσειρά Linux. Φυσικά, δεν είναι τόσο απλό όσο το κλικ με το ποντίκι, αλλά το υποσύστημα γραφικών είναι πολύ εντατικό σε πόρους. Με τις οθόνες, μπορείτε να έχετε σχεδόν την ίδια λειτουργικότητα και να ενεργοποιήσετε το πλήρες multitasking!

Διεργασίες στο UNIX

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

Κατά την εκτέλεση ή κατά την αναμονή "στα φτερά", οι διεργασίες περιέχονται στην εικονική μνήμη με μια οργάνωση σελίδας. Μέρος αυτής της εικονικής μνήμης αντιστοιχίζεται στη φυσική μνήμη. Μέρος της φυσικής μνήμης είναι δεσμευμένο για τον πυρήνα του λειτουργικού συστήματος. Οι χρήστες έχουν πρόσβαση μόνο στη μνήμη που απομένει για διεργασίες. Εάν είναι απαραίτητο, οι σελίδες μνήμης διεργασίας ανταλλάσσονται από τη φυσική μνήμη σε δίσκο, στην περιοχή εναλλαγής. Κατά την πρόσβαση σε μια σελίδα στην εικονική μνήμη, εάν δεν βρίσκεται σε φυσική μνήμη, γίνεται εναλλαγή από το δίσκο.

Η εικονική μνήμη υλοποιείται και διατηρείται αυτόματα από τον πυρήνα του UNIX.

Τύποι διαδικασίας

Υπάρχουν τρεις τύποι διεργασιών στα λειτουργικά συστήματα UNIX: συστήματος, διεργασίες δαίμοναΚαι διαδικασίες αίτησης.

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

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

Δαίμονες- πρόκειται για μη διαδραστικές διεργασίες που εκκινούνται με τον συνήθη τρόπο - φορτώνοντας τα αντίστοιχα προγράμματα στη μνήμη και εκτελούνται στο παρασκήνιο. Συνήθως, οι δαίμονες εκκινούνται κατά την προετοιμασία του συστήματος, αλλά μετά την προετοιμασία του πυρήνα και διασφαλίζουν τη λειτουργία διαφόρων υποσυστημάτων UNIX: συστήματα πρόσβασης τερματικού, συστήματα εκτύπωσης, υπηρεσίες δικτύουκαι τα λοιπά. Οι δαίμονες δεν συνδέονται με κανέναν χρήστη. Τις περισσότερες φορές, οι δαίμονες περιμένουν τη μία ή την άλλη διαδικασία να ζητήσουν συγκεκριμένη υπηρεσία.



ΠΡΟΣ ΤΗΝ διαδικασίες εφαρμογήςπεριλαμβάνει όλες τις άλλες διεργασίες που εκτελούνται στο σύστημα. Συνήθως, αυτές είναι διεργασίες που δημιουργούνται σε μια περίοδο λειτουργίας χρήστη. Η πιο σημαντική διαδικασία χρήστη είναι αρχικός διερμηνέας εντολών, το οποίο παρέχει την εκτέλεση εντολών χρήστη σε ένα σύστημα UNIX.

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

Χαρακτηριστικά διαδικασίας

Μια διαδικασία UNIX έχει μια σειρά από χαρακτηριστικά που της επιτρέπουν λειτουργικό σύστημαδιαχειρίζεται το έργο του. Κύρια χαρακτηριστικά:

· Αναγνωριστικό διαδικασίας (PID), επιτρέποντας στον πυρήνα του συστήματος να διακρίνει μεταξύ διεργασιών. Όταν δημιουργείται νέα διαδικασία, ο πυρήνας του εκχωρεί το επόμενο δωρεάν αναγνωριστικό (δηλαδή, που δεν σχετίζεται με καμία διεργασία). Η εκχώρηση ενός αναγνωριστικού συνήθως γίνεται με αύξουσα σειρά, δηλ. Το αναγνωριστικό της νέας διαδικασίας είναι μεγαλύτερο από το αναγνωριστικό της διαδικασίας που δημιουργήθηκε πριν από αυτήν. Εάν το αναγνωριστικό φτάσει τη μέγιστη τιμή (συνήθως 65737), η επόμενη διαδικασία θα λάβει το ελάχιστο ελεύθερο PID και ο κύκλος επαναλαμβάνεται. Όταν μια διεργασία εξέρχεται, ο πυρήνας απελευθερώνει το αναγνωριστικό που χρησιμοποιούσε.

· Γονικό αναγνωριστικό διαδικασίας (PPID)– αναγνωριστικό της διαδικασίας που προκάλεσε αυτή τη διαδικασία. Όλες οι διαδικασίες στο σύστημα εκτός από διαδικασίες του συστήματοςκαι διαδικασία μέσα σε αυτό, που είναι ο προγονέας των υπόλοιπων διεργασιών, δημιουργούνται από μία από τις υπάρχουσες ή προηγούμενες διεργασίες.

· Διόρθωση προτεραιότητας (NI)– τη σχετική προτεραιότητα της διαδικασίας, που λαμβάνεται υπόψη από τον προγραμματιστή κατά τον καθορισμό της σειράς εκκίνησης. Η πραγματική κατανομή των πόρων του επεξεργαστή καθορίζεται από την προτεραιότητα εκτέλεσης (χαρακτηριστικό PRI), ανάλογα με διάφορους παράγοντες, ιδίως με τη δεδομένη σχετική προτεραιότητα. Η σχετική προτεραιότητα δεν αλλάζει από το σύστημα καθ' όλη τη διάρκεια της διαδικασίας, αν και μπορεί να αλλάξει από τον χρήστη ή τον διαχειριστή κατά την έναρξη της διαδικασίας χρησιμοποιώντας την εντολή όμορφη. Το εύρος τιμών αύξησης προτεραιότητας στα περισσότερα συστήματα είναι -20 έως 20. Εάν δεν καθορίζεται αύξηση, χρησιμοποιείται η προεπιλεγμένη τιμή 10. Μια θετική αύξηση σημαίνει μείωση της τρέχουσας προτεραιότητας. Οι απλοί χρήστες μπορούν να ορίσουν μόνο μια θετική αύξηση και, επομένως, να μειώσουν μόνο την προτεραιότητα. Χρήστης ρίζαμπορεί να ορίσει μια αρνητική προσαύξηση, η οποία αυξάνει την προτεραιότητα της διαδικασίας και, ως εκ τούτου, συμβάλλει στην υψηλότερη γρήγορη δουλειά. Σε αντίθεση με τη σχετική προτεραιότητα, η προτεραιότητα εκτέλεσης μιας διεργασίας αλλάζει δυναμικά από τον προγραμματιστή.

· Τερματική γραμμή (TTY)– ένα τερματικό ή ψευδοτερματικό που σχετίζεται με μια διεργασία. Αυτό το τερματικό σχετίζεται με το τυπικό ρέματα: εισαγωγή, ρεπόΚαι ροή μηνυμάτωνσχετικά με λάθη. Ροές ( κανάλια προγράμματος) είναι τυπικά μέσαεπικοινωνία μεταξύ διεργασιών στο UNIX OS. Οι διαδικασίες Daemon δεν σχετίζονται με ένα τερματικό.

· Πραγματικά (UID) και αποτελεσματικά (EUID) αναγνωριστικά χρήστη. Το πραγματικό αναγνωριστικό χρήστη μιας δεδομένης διαδικασίας είναι το αναγνωριστικό του χρήστη που ξεκίνησε τη διαδικασία. Το αποτελεσματικό αναγνωριστικό χρησιμοποιείται για τον προσδιορισμό των δικαιωμάτων πρόσβασης της διαδικασίας σε πόρους συστήματος (κυρίως πόρους σύστημα αρχείων). Συνήθως τα πραγματικά και τα αποτελεσματικά αναγνωριστικά είναι τα ίδια, δηλ. η διαδικασία έχει τα ίδια δικαιώματα στο σύστημα με τον χρήστη που την εκκίνησε. Ωστόσο, είναι δυνατό να δοθούν σε μια διεργασία περισσότερα δικαιώματα από τον χρήστη με τη ρύθμιση bit SUIDόταν το ενεργό αναγνωριστικό έχει οριστεί στο αναγνωριστικό του κατόχου του εκτελέσιμου αρχείου (για παράδειγμα, ο χρήστης ρίζα).

· Πραγματικά (GID) και αποτελεσματικά (EGID) αναγνωριστικά ομάδας.Το πραγματικό αναγνωριστικό ομάδας είναι ίσο με το κύριο ή τρέχον αναγνωριστικό ομάδας του χρήστη που ξεκίνησε τη διαδικασία. Το αποτελεσματικό αναγνωριστικό χρησιμοποιείται για τον προσδιορισμό των δικαιωμάτων πρόσβασης σε πόρους του συστήματος για λογαριασμό μιας ομάδας. Συνήθως το ενεργό αναγνωριστικό ομάδας είναι το ίδιο με το πραγματικό. Αλλά αν το εκτελέσιμο αρχείο έχει οριστεί σε bit SGID, ένα τέτοιο αρχείο εκτελείται με το πραγματικό αναγνωριστικό ομάδας κατόχων.

ΕΡΓΑΣΤΗΡΙΑΚΗ ΕΡΓΑΣΙΑ Νο 3

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΠΟΛΛΑΠΛΩΝ ΕΡΓΑΣΙΩΝ ΣΕLINUX

1. Στόχος της εργασίας:Εξοικειωθείτε με τον μεταγλωττιστή gcc, τις τεχνικές εντοπισμού σφαλμάτων προγραμμάτων και τις λειτουργίες για εργασία με διεργασίες.

2. Σύντομες θεωρητικές πληροφορίες.

Το ελάχιστο σύνολο διακοπτών μεταγλωττιστή gcc είναι - Wall (εμφάνιση όλων των σφαλμάτων και προειδοποιήσεων) και - o (αρχείο εξόδου):

gcc - Wall - o print_pid print_pid. ντο

Η ομάδα θα δημιουργήσει εκτελέσιμο αρχείο print_pid.

Η τυπική βιβλιοθήκη C (libc, που υλοποιείται στο Linux σε glibc) εκμεταλλεύεται τις δυνατότητες πολλαπλών εργασιών του Unix System V (εφεξής SysV). Στο libc, ο τύπος pid_t ορίζεται ως ένας ακέραιος αριθμός που μπορεί να περιέχει ένα pid. Η συνάρτηση που αναφέρει το pid της τρέχουσας διαδικασίας έχει ένα πρωτότυπο του pid_t getpid(void) και ορίζεται μαζί με το pid_t στο unistd. h και sys/τύποι. η).

Για να δημιουργήσετε μια νέα διαδικασία, χρησιμοποιήστε τη συνάρτηση fork:

pid_t πιρούνι (κενό)

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

Αυτό θα κάνει το πρόγραμμα να "αδράνει" για έναν τυχαίο αριθμό δευτερολέπτων: από 0 έως 3.

Για να καλέσετε μια συνάρτηση ως θυγατρική διεργασία, απλώς καλέστε την μετά τη διακλάδωση:

// εάν εκτελείται μια θυγατρική διεργασία, καλέστε τη συνάρτηση

pid=διαδικασία(arg);

// έξοδος από τη διαδικασία

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

// εάν εκτελείται μια θυγατρική διεργασία, τότε καλέστε το πρόγραμμα


if (execl("./αρχείο","αρχείο",arg, NULL)<0) {

printf("ΣΦΑΛΜΑ κατά την έναρξη της διαδικασίας\n");

else printf("η διαδικασία ξεκίνησε (pid=%d)\n", pid);

// έξοδος από τη διαδικασία

Συχνά, μια γονική διαδικασία χρειάζεται να ανταλλάσσει πληροφορίες με τα παιδιά της, ή τουλάχιστον να συγχρονίζει με αυτά, προκειμένου να εκτελεί λειτουργίες την κατάλληλη στιγμή. Ένας τρόπος για να συγχρονίσετε τις διαδικασίες είναι με τις λειτουργίες αναμονής και αναμονής:

#περιλαμβάνω

#περιλαμβάνω

pid_t wait(int *status) - αναστέλλει την εκτέλεση της τρέχουσας διαδικασίας μέχρι να τερματιστεί οποιαδήποτε από τις θυγατρικές διεργασίες της.

pid_t waitpid (pid_t pid, int *status, int επιλογές) - αναστέλλει την εκτέλεση της τρέχουσας διαδικασίας έως ότου ολοκληρωθεί η καθορισμένη διαδικασία ή ελέγχει την ολοκλήρωση της καθορισμένης διαδικασίας.

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

status=waitpid(pid,&status, WNOHANG);

αν (pid == κατάσταση) (

printf("PID: %d, Αποτέλεσμα = %d\n", pid, WEXITSTATUS(κατάσταση)); )

Για να αλλάξετε τις προτεραιότητες των δημιουργούμενων διεργασιών, χρησιμοποιούνται η σειρά προτεραιότητας και οι συναρτήσεις. Οι προτεραιότητες ορίζονται στην περιοχή από -20 (υψηλότερη) έως 20 (χαμηλότερη), η κανονική τιμή είναι 0. Σημειώστε ότι μόνο ένας υπερχρήστης μπορεί να αυξήσει την προτεραιότητα πάνω από το κανονικό!

#περιλαμβάνω

#περιλαμβάνω

διαδικασία int(int i) (

setpriority(PRIO_PROCESS, getpid(),i);

printf("Επεξεργασία %d Αναγνωριστικό νήματος: %d λειτουργεί με προτεραιότητα %d\n",i, getpid(),getpriority(PRIO_PROCESS, getpid()));

return(getpriority(PRIO_PROCESS, getpid()));

Για να σκοτώσετε μια διαδικασία, χρησιμοποιήστε τη συνάρτηση kill:

#περιλαμβάνω

#περιλαμβάνω

int kill(pid_t pid, int sig);

Εάν pid > 0, τότε καθορίζει το PID της διεργασίας στην οποία αποστέλλεται το σήμα. Αν pid = 0, τότε το σήμα αποστέλλεται σε όλες τις διεργασίες της ομάδας στην οποία ανήκει η τρέχουσα διεργασία.

σήμα - τύπος σήματος. Μερικοί τύποι σημάτων στο Linux:

SIGKILL Αυτό το σήμα κάνει τη διαδικασία να τερματιστεί αμέσως. Η διαδικασία δεν μπορεί να αγνοήσει αυτό το σήμα.

SIGTERM Αυτό το σήμα είναι ένα αίτημα τερματισμού της διαδικασίας.

SIGCHLD Το σύστημα στέλνει αυτό το σήμα σε μια διεργασία όταν μια από τις θυγατρικές διεργασίες τερματίζεται. Παράδειγμα:

αν (pid[i] == κατάσταση) (

printf("ThreadID: %d ολοκληρώθηκε με κατάσταση %d\n", pid[i], WEXITSTATUS(κατάσταση));

else kill(pid[i],SIGKILL);

3. Μεθοδικές οδηγίες.

3.1. Για να εξοικειωθείτε με τις επιλογές μεταγλωττιστή gcc και τις περιγραφές των συναρτήσεων της γλώσσας C, χρησιμοποιήστε τις οδηγίες man and info.

3.2. Για τον εντοπισμό σφαλμάτων προγραμμάτων είναι βολικό να χρησιμοποιήσετε τον ενσωματωμένο επεξεργαστή του διαχειριστή αρχείων Διοικητής των Μεσονυκτίων(MC), που επισημαίνει διάφορες γλωσσικές κατασκευές με χρώμα και υποδεικνύει τη θέση του δρομέα στο αρχείο (γραμμή, στήλη) στην επάνω γραμμή της οθόνης.

3.3. Ο διαχειριστής αρχείων Midnight Commander διαθέτει ένα buffer εντολών που μπορεί να κληθεί με μια συντόμευση πληκτρολογίου - H, το οποίο μπορεί να μετακινηθεί χρησιμοποιώντας τα βέλη του δρομέα (πάνω και κάτω). Για να εισαγάγετε μια εντολή από το buffer στη γραμμή εντολών, χρησιμοποιήστε το κλειδί , για να επεξεργαστείτε μια εντολή από τα πλήκτρα buffer<- и ->, Και .


3.4. Θυμηθείτε ότι ο τρέχων κατάλογος δεν περιέχεται στη διαδρομή, επομένως πρέπει να εκτελέσετε το πρόγραμμα ως "./print_pid" από τη γραμμή εντολών. Στο MC, απλώς τοποθετήστε το δείκτη του ποντικιού πάνω από το αρχείο και κάντε κλικ .

3.5. Για να δείτε το αποτέλεσμα της εκτέλεσης του προγράμματος, χρησιμοποιήστε τη συντόμευση πληκτρολογίου - O. Λειτουργούν επίσης σε λειτουργία επεξεργασίας αρχείων.

3.6. Για να καταγράψετε τα αποτελέσματα της εκτέλεσης του προγράμματος, συνιστάται να χρησιμοποιήσετε την ανακατεύθυνση της εξόδου από την κονσόλα σε ένα αρχείο: ./test > result. κείμενο

3.7. Για πρόσβαση σε αρχεία που έχουν δημιουργηθεί σε Διακομιστής Linux, χρησιμοποιήστε το πρωτόκολλο ftp, το πρόγραμμα-πελάτη για το οποίο είναι διαθέσιμο στα Windows 2000 και είναι ενσωματωμένο διαχείριση αρχείωνΜΑΚΡΙΑ. Εν λογαριασμόςκαι ο κωδικός πρόσβασης είναι ο ίδιος όπως όταν συνδέεστε μέσω ssh.

4.1. Εξοικειωθείτε με τις επιλογές και τις μεθόδους μεταγλωττιστή gcc για τον εντοπισμό σφαλμάτων προγραμμάτων.

4.2. Για παραλλαγές εργασιών από την εργαστηριακή εργασία Νο. 1, γράψτε και διορθώστε ένα πρόγραμμα που υλοποιεί τη διαδικασία που δημιουργείται.

4.3. Για επιλογές εργασιών από εργαστηριακές εργασίεςΝο 1 εγγραφή και εντοπισμός σφαλμάτων ενός προγράμματος που υλοποιεί μια γονική διαδικασία που καλεί και παρακολουθεί την κατάσταση των θυγατρικών διεργασιών - προγραμμάτων (αναμονή να ολοκληρωθούν ή καταστρέφονται, ανάλογα με την επιλογή).

4.4. Για παραλλαγές εργασιών από την εργαστηριακή εργασία Νο. 1, γράψτε και διορθώστε ένα πρόγραμμα που υλοποιεί μια γονική διαδικασία που καλεί και παρακολουθεί την κατάσταση των θυγατρικών διεργασιών - συναρτήσεων (αναμονή για την ολοκλήρωσή τους ή καταστροφή τους, ανάλογα με την παραλλαγή).

5. Επιλογές για εργασίες.Δείτε επιλογές για εργασίες από την εργαστηριακή εργασία Νο. 1

6. Περιεχόμενα της έκθεσης.

6.1. Στόχος της εργασίας.

6.2. Επιλογή εργασίας.

6.3. Λίστες προγραμμάτων.

6.4. Πρωτόκολλα εκτέλεσης προγραμμάτων.

7. Ερωτήσεις ελέγχου.

7.1. Δυνατότητες μεταγλώττισης και εκτέλεσης προγραμμάτων C σε Linux.

7.2. Τι είναι το pid, πώς να το προσδιορίσετε στο λειτουργικό σύστημα και το πρόγραμμα;

7.3. Λειτουργία πιρουνιού - σκοπός, εφαρμογή, τιμή επιστροφής.

7.4. Πώς να εκτελέσετε μια συνάρτηση σε μια διαδικασία αναπαραγωγής; Πρόγραμμα?

7.5. Τρόποι συγχρονισμού διαδικασιών γονέα και παιδιού.

7.6. Πώς να μάθετε την κατάσταση της ωοτοκίας διαδικασίας όταν τερματίζεται και την τιμή που επιστρέφει;

7.7. Πώς να διαχειριστείτε τις προτεραιότητες της διαδικασίας;

7.8. Πώς να σκοτώσετε μια διαδικασία στο λειτουργικό σύστημα και το πρόγραμμα;

Συνεχίζουμε το θέμα του multithreading στον πυρήνα του Linux. Την τελευταία φορά που μίλησα για διακοπές, την επεξεργασία και τα tasklets τους, και επειδή αρχικά προβλεπόταν ότι αυτό θα ήταν ένα άρθρο, στην ιστορία μου σχετικά με την ουρά εργασίας θα αναφερθώ σε tasklets, υποθέτοντας ότι ο αναγνώστης είναι ήδη εξοικειωμένος με αυτά.
Όπως και την προηγούμενη φορά, θα προσπαθήσω να κάνω την ιστορία μου όσο το δυνατόν πιο λεπτομερή και λεπτομερή.

Άρθρα της σειράς:

  1. Multitasking στον πυρήνα Linux: ουρά εργασίας

Ουρά εργασίας

Ουρά εργασίας- πρόκειται για πιο σύνθετες και βαρύτερες οντότητες από ό,τι τα tasklets. Δεν θα προσπαθήσω καν να περιγράψω όλες τις περιπλοκές της υλοποίησης εδώ, αλλά ελπίζω ότι θα αναλύσω τα πιο σημαντικά πράγματα με περισσότερες ή λιγότερο λεπτομέρειες.
Οι ουρές εργασίας, όπως τα tasklets, χρησιμεύουν για επεξεργασία καθυστερημένης διακοπής (αν και μπορούν να χρησιμοποιηθούν για άλλους σκοπούς), αλλά, σε αντίθεση με τα tasklets, εκτελούνται στο πλαίσιο μιας διεργασίας πυρήνα, επομένως, δεν χρειάζεται να είναι ατομικές και μπορούν να χρησιμοποιούν ύπνο () λειτουργία, διάφορα εργαλεία συγχρονισμού κ.λπ.

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

Πολλές οντότητες εμπλέκονται σε αυτή τη σκοτεινή ύλη.
Πρώτα, αντικείμενο εργασίας(απλά εργασία για συντομία) είναι μια δομή που περιγράφει τη συνάρτηση (για παράδειγμα, έναν χειριστή διακοπής) που θέλουμε να προγραμματίσουμε. Μπορεί να θεωρηθεί ως ανάλογο της δομής του tasklet. Κατά τον προγραμματισμό, τα Tasklet προστέθηκαν σε ουρές που ήταν κρυμμένες από τον χρήστη, αλλά τώρα πρέπει να χρησιμοποιήσουμε μια ειδική ουρά - ουρά εργασίας.
Τα Tasklet συλλέγονται από τη συνάρτηση χρονοπρογραμματιστή και η ουρά εργασίας επεξεργάζεται από ειδικά νήματα που ονομάζονται εργαζόμενοι.
Εργάτηςπαρέχουν ασύγχρονη εκτέλεση εργασιών από την ουρά εργασίας. Αν και ονομάζουν την εργασία με σειρά εκ περιτροπής, στη γενική περίπτωση δεν τίθεται θέμα αυστηρής, διαδοχικής εκτέλεσης: άλλωστε εδώ γίνεται η προκοπή, ο ύπνος, η αναμονή κ.λπ.

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

Για να περιγράψετε τις κύριες δυνατότητες του μηχανισμού ουράς εργασίας, προτείνω να εξερευνήσετε το API.

Σχετικά με την ουρά και τη δημιουργία της

alloc_workqueue (fmt, flags, max_active, args...)
Οι παράμετροι fmt και args είναι η μορφή printf για το όνομα και τα ορίσματα σε αυτό. Η παράμετρος max_activate είναι υπεύθυνη για τον μέγιστο αριθμό εργασιών που από αυτήν την ουρά μπορούν να εκτελεστούν παράλληλα σε μία CPU.
Μπορεί να δημιουργηθεί μια ουρά με τις ακόλουθες σημαίες:
  • WQ_HIGHPRI
  • WQ_UNBOUND
  • WQ_CPU_INTENSIVE
  • WQ_FREEZABLE
  • WQ_MEM_RECLAIM
Ιδιαίτερη προσοχή πρέπει να δοθεί στη σημαία WQ_UNBOUND. Με βάση την παρουσία αυτής της σημαίας, οι ουρές χωρίζονται σε δεσμευμένες και μη.
Σε συνδεδεμένες ουρέςΌταν προστίθενται, οι εργασίες συνδέονται με την τρέχουσα CPU, δηλαδή, σε τέτοιες ουρές, οι εργασίες εκτελούνται στον πυρήνα που την προγραμματίζει. Από αυτή την άποψη, οι δεσμευμένες ουρές μοιάζουν με tasklets.
Σε αδέσμευτες ουρέςοι εργασίες μπορούν να εκτελεστούν σε οποιονδήποτε πυρήνα.

Ένα σημαντικό χαρακτηριστικό της υλοποίησης της ουράς εργασίας στον πυρήνα του Linux είναι η πρόσθετη οργάνωση της παράλληλης εκτέλεσης που υπάρχει σε δεσμευμένες ουρές. Είναι γραμμένο με περισσότερες λεπτομέρειες παρακάτω, αλλά τώρα θα πω ότι πραγματοποιείται με τέτοιο τρόπο ώστε να χρησιμοποιείται όσο το δυνατόν λιγότερη μνήμη και έτσι ώστε ο επεξεργαστής να μην μένει αδρανής. Όλα αυτά υλοποιούνται με την υπόθεση ότι μια εργασία δεν χρησιμοποιεί πάρα πολλούς κύκλους επεξεργαστή.
Αυτό δεν ισχύει για τις μη συνδεδεμένες ουρές. Ουσιαστικά, τέτοιες ουρές απλώς παρέχουν το πλαίσιο στους εργαζόμενους και τους ξεκινούν όσο το δυνατόν νωρίτερα.
Έτσι, θα πρέπει να χρησιμοποιούνται μη συνδεδεμένες ουρές εάν αναμένεται φόρτος εργασίας έντασης CPU, καθώς σε αυτήν την περίπτωση ο προγραμματιστής θα φροντίσει για την παράλληλη εκτέλεση σε πολλούς πυρήνες.

Κατ' αναλογία με τα tasklets, τα έργα μπορούν να έχουν προτεραιότητα εκτέλεσης, κανονική ή υψηλή. Η προτεραιότητα είναι κοινή για ολόκληρη την ουρά. Από προεπιλογή, η ουρά έχει κανονική προτεραιότητα και αν ορίσετε τη σημαία WQ_HIGHPRI, τότε, ανάλογα, υψηλή.

Σημαία WQ_CPU_INTENSIVEέχει νόημα μόνο για δεσμευμένες ουρές. Αυτή η σημαία είναι μια άρνηση συμμετοχής σε μια πρόσθετη οργάνωση παράλληλης εκτέλεσης. Αυτή η σημαία θα πρέπει να χρησιμοποιείται όταν η εργασία αναμένεται να καταναλώσει πολύ χρόνο CPU, οπότε είναι προτιμότερο να μετατεθεί η ευθύνη στον προγραμματιστή. Αυτό περιγράφεται με περισσότερες λεπτομέρειες παρακάτω.

Σημαίες WQ_FREEZABLEΚαι WQ_MEM_RECLAIMείναι συγκεκριμένα και ξεφεύγουν από το αντικείμενο του θέματος, οπότε δεν θα σταθούμε αναλυτικά σε αυτά.

Μερικές φορές είναι λογικό να μην δημιουργείτε τις δικές σας ουρές, αλλά να χρησιμοποιείτε κοινές. Τα κυριότερα:

  • system_wq - δεσμευμένη ουρά για γρήγορη εργασία
  • system_long_wq - μια δεσμευμένη ουρά για εργασίες που αναμένεται να διαρκέσουν πολύ χρόνο για να εκτελεστούν
  • system_unbound_wq - μη δεσμευμένη ουρά

Σχετικά με την εργασία και τον προγραμματισμό τους

Τώρα ας ασχοληθούμε με τα έργα. Αρχικά, ας δούμε τις μακροεντολές προετοιμασίας, δήλωσης και προετοιμασίας:
DECLARE(_DELAYED)_WORK(name, void (*function)(struct work_struct *work)); /* κατά το χρόνο μεταγλώττισης */ INIT(_DELAYED)_WORK(_work, _func); /* κατά την εκτέλεση */ PREPARE(_DELAYED)_WORK(_work, _func); /* για να αλλάξετε τη συνάρτηση που εκτελείται */
Τα έργα προστίθενται στην ουρά χρησιμοποιώντας τις συναρτήσεις:
bool queue_work(struct workqueue_struct *wq, struct work_struct *work); bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay); /* η εργασία θα προστεθεί στην ουρά μόνο αφού λήξει η καθυστέρηση */
Αξίζει να σταθούμε σε αυτό με περισσότερες λεπτομέρειες. Αν και καθορίζουμε μια ουρά ως παράμετρο, στην πραγματικότητα, οι εργασίες δεν τοποθετούνται στην ίδια την ουρά εργασίας, όπως μπορεί να φαίνεται, αλλά σε μια εντελώς διαφορετική οντότητα - στη λίστα ουρών της δομής worker_pool. Δομή worker_pool, στην πραγματικότητα, είναι η πιο σημαντική οντότητα στην οργάνωση του μηχανισμού της ουράς εργασίας, αν και για τον χρήστη παραμένει στα παρασκήνια. Μαζί τους δουλεύουν οι εργαζόμενοι και σε αυτούς περιέχονται όλες οι βασικές πληροφορίες.

Τώρα ας δούμε ποιες ομάδες υπάρχουν στο σύστημα.
Αρχικά, πισίνες για δεσμευμένες ουρές (στην εικόνα). Για κάθε CPU, δύο ομάδες εργαζομένων κατανέμονται στατικά: η μία για εργασία υψηλής προτεραιότητας και η άλλη για εργασία με κανονική προτεραιότητα. Δηλαδή, αν έχουμε τέσσερις πυρήνες, τότε θα υπάρχουν μόνο οκτώ δεμένες πισίνες, παρά το γεγονός ότι μπορεί να υπάρχουν όσες ουρές εργασίας επιθυμείτε.
Όταν δημιουργούμε μια ουρά εργασίας, έχει μια υπηρεσία που εκχωρείται για κάθε CPU πισίνα_ουρά εργασίας(pwq). Κάθε τέτοιο pool_workqueue συσχετίζεται με μια ομάδα εργαζομένων, η οποία εκχωρείται στην ίδια CPU και αντιστοιχεί σε προτεραιότητα στον τύπο της ουράς. Μέσω αυτών, η ουρά εργασίας αλληλεπιδρά με την ομάδα εργαζομένων.
Οι εργαζόμενοι εκτελούν εργασία από την ομάδα των εργαζομένων αδιακρίτως, χωρίς να διακρίνουν σε ποια ουρά εργασίας ανήκαν αρχικά.

Για μη συνδεδεμένες ουρές, οι ομάδες εργαζομένων κατανέμονται δυναμικά. Όλες οι ουρές μπορούν να χωριστούν σε κλάσεις ισοδυναμίας ανάλογα με τις παραμέτρους τους και για κάθε τέτοια κατηγορία δημιουργείται το δικό της σύνολο εργαζομένων. Η πρόσβαση σε αυτά γίνεται χρησιμοποιώντας έναν ειδικό πίνακα κατακερματισμού, όπου το κλειδί είναι ένα σύνολο παραμέτρων και η τιμή, αντίστοιχα, είναι η ομάδα εργαζομένων.
Στην πραγματικότητα, για τις μη δεσμευμένες ουρές τα πάντα είναι λίγο πιο περίπλοκα: αν για δεσμευμένες ουρές δημιουργήθηκαν pwq και ουρές για κάθε CPU, εδώ δημιουργούνται για κάθε κόμβο NUMA, αλλά αυτή είναι μια πρόσθετη βελτιστοποίηση που δεν θα εξετάσουμε λεπτομερώς.

Όλα τα μικροπράγματα

Θα δώσω επίσης μερικές λειτουργίες από το API για να ολοκληρώσω την εικόνα, αλλά δεν θα μιλήσω για αυτές λεπτομερώς:
/* Αναγκαστική ολοκλήρωση */ bool flush_work(struct work_struct *work); bool flush_delayed_work(struct delayed_work *dwork); /* Ακύρωση εκτέλεσης εργασίας */ bool cancel_work_sync(struct work_struct *work); bool cancel_delayed_work(struct delayed_work *dwork); bool cancel_delayed_work_sync(struct delayed_work *dwork); /* Διαγραφή ουράς */ voidstruct_workqueue(struct workqueue_struct *wq);

Πώς οι εργαζόμενοι κάνουν τη δουλειά τους

Τώρα που εξοικειωθήκαμε με το API, ας προσπαθήσουμε να κατανοήσουμε με περισσότερες λεπτομέρειες πώς λειτουργεί και πώς γίνεται η διαχείριση του.
Κάθε πισίνα έχει ένα σύνολο εργαζομένων που χειρίζονται εργασίες. Επιπλέον, ο αριθμός των εργαζομένων αλλάζει δυναμικά, προσαρμοζόμενος στην τρέχουσα κατάσταση.
Όπως έχουμε ήδη ανακαλύψει, οι εργαζόμενοι είναι νήματα που εκτελούν εργασία στο πλαίσιο του πυρήνα. Ο εργάτης τα ανακτά με τη σειρά, το ένα μετά το άλλο, από τη δεξαμενή εργαζομένων που σχετίζεται με αυτό και οι εργάτες, όπως ήδη γνωρίζουμε, μπορούν να ανήκουν σε διαφορετικές ουρές πηγής.

Οι εργαζόμενοι μπορούν υπό όρους να βρίσκονται σε τρεις λογικές καταστάσεις: μπορεί να είναι αδρανείς, να τρέχουν ή να διαχειρίζονται.
Ο εργαζόμενος μπορεί μείνε αδρανήςκαι να μην κάνεις τίποτα. Αυτό συμβαίνει, για παράδειγμα, όταν όλες οι εργασίες εκτελούνται ήδη. Όταν ένας εργαζόμενος μπαίνει σε αυτή την κατάσταση, πηγαίνει για ύπνο και, κατά συνέπεια, δεν θα εκτελέσει μέχρι να ξυπνήσει.
Εάν δεν απαιτείται διαχείριση πισίνας και η λίστα των προγραμματισμένων εργασιών δεν είναι κενή, τότε ο εργαζόμενος ξεκινά την εκτέλεσή τους. Τέτοιους εργάτες θα ονομάζουμε συμβατικά τρέξιμο.
Εάν είναι απαραίτητο, ο εργαζόμενος αναλαμβάνει το ρόλο διευθυντήςπισίνα. Μια πισίνα μπορεί να έχει είτε μόνο έναν διαχειριστή είτε κανέναν εργαζόμενο. Το καθήκον του είναι να διατηρεί τον βέλτιστο αριθμό εργαζομένων ανά ομάδα. Πώς το κάνει; Πρώτον, διαγράφονται οι εργαζόμενοι που έχουν μείνει σε αδράνεια για μεγάλο χρονικό διάστημα. Δεύτερον, δημιουργούνται νέοι εργαζόμενοι εάν πληρούνται τρεις προϋποθέσεις ταυτόχρονα:

  • υπάρχουν ακόμη εργασίες προς ολοκλήρωση (εργασίες στην πισίνα)
  • όχι αδρανείς εργάτες
  • δεν υπάρχουν εργαζόμενοι (δηλαδή ενεργοί και δεν κοιμούνται)
Ωστόσο, η τελευταία συνθήκη έχει τις δικές της αποχρώσεις. Εάν οι ουρές της πισίνας δεν είναι συνδεδεμένες, τότε οι εργαζόμενοι που τρέχουν δεν λαμβάνονται υπόψη· γι' αυτούς αυτή η συνθήκη ισχύει πάντα. Το ίδιο ισχύει στην περίπτωση ενός εργαζόμενου που εκτελεί μια εργασία από μια συνδεδεμένη, αλλά με τη σημαία WQ_CPU_INTENSIVE, ουρές. Επιπλέον, στην περίπτωση των δεμένων ουρών, αφού οι εργαζόμενοι εργάζονται με έργα από την κοινή πισίνα (που είναι ένα από τα δύο για κάθε πυρήνα στην παραπάνω εικόνα), αποδεικνύεται ότι μερικά από αυτά υπολογίζονται ως εργαζόμενα και άλλα όχι. Επίσης προκύπτει ότι η εκτέλεση εργασιών από WQ_CPU_INTENSIVEΟι ουρές μπορεί να μην ξεκινούν αμέσως, αλλά οι ίδιες δεν παρεμβαίνουν στην εκτέλεση άλλων εργασιών. Τώρα θα πρέπει να είναι σαφές γιατί αυτή η σημαία ονομάζεται έτσι και γιατί χρησιμοποιείται όταν αναμένουμε ότι η εργασία θα χρειαστεί πολύ χρόνο για να ολοκληρωθεί.

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

Όσοι ενδιαφέρονται μπορούν να δουν τη συνάρτηση worker στον πυρήνα, που ονομάζεται worker_thread().

Όλες οι περιγραφόμενες λειτουργίες και δομές μπορείτε να τις βρείτε με περισσότερες λεπτομέρειες στα αρχεία include/linux/workqueue.h, πυρήνας/ουρά εργασίας.γΚαι kernel/workqueue_internal.h. Υπάρχει επίσης τεκμηρίωση για την ουρά εργασίας Τεκμηρίωση/ουρά εργασίας.txt.

Αξίζει επίσης να σημειωθεί ότι ο μηχανισμός ουράς εργασίας χρησιμοποιείται στον πυρήνα όχι μόνο για την επεξεργασία καθυστερημένης διακοπής (αν και αυτό είναι ένα αρκετά κοινό σενάριο).

Έτσι, εξετάσαμε τους μηχανισμούς για τον χειρισμό αναβαλλόμενης διακοπής στον πυρήνα του Linux - tasklet και workqueue, που αποτελούν μια ειδική μορφή πολλαπλών εργασιών. Μπορείτε να διαβάσετε για διακοπές, εργαστήρια και ουρές εργασίας στο βιβλίο «Linux Device Drivers» των Jonathan Corbet, Greg Kroah-Hartman, Alessandro Rubini, αν και οι πληροφορίες εκεί είναι μερικές φορές ξεπερασμένες.

Συνεχίζουμε το θέμα του multithreading στον πυρήνα του Linux. Την τελευταία φορά που μίλησα για διακοπές, την επεξεργασία και τα tasklets τους, και επειδή αρχικά προβλεπόταν ότι αυτό θα ήταν ένα άρθρο, στην ιστορία μου σχετικά με την ουρά εργασίας θα αναφερθώ σε tasklets, υποθέτοντας ότι ο αναγνώστης είναι ήδη εξοικειωμένος με αυτά.
Όπως και την προηγούμενη φορά, θα προσπαθήσω να κάνω την ιστορία μου όσο το δυνατόν πιο λεπτομερή και λεπτομερή.

Άρθρα της σειράς:

  1. Multitasking στον πυρήνα Linux: ουρά εργασίας

Ουρά εργασίας

Ουρά εργασίας- πρόκειται για πιο σύνθετες και βαρύτερες οντότητες από ό,τι τα tasklets. Δεν θα προσπαθήσω καν να περιγράψω όλες τις περιπλοκές της υλοποίησης εδώ, αλλά ελπίζω ότι θα αναλύσω τα πιο σημαντικά πράγματα με περισσότερες ή λιγότερο λεπτομέρειες.
Οι ουρές εργασίας, όπως τα tasklets, χρησιμεύουν για επεξεργασία καθυστερημένης διακοπής (αν και μπορούν να χρησιμοποιηθούν για άλλους σκοπούς), αλλά, σε αντίθεση με τα tasklets, εκτελούνται στο πλαίσιο μιας διεργασίας πυρήνα, επομένως, δεν χρειάζεται να είναι ατομικές και μπορούν να χρησιμοποιούν ύπνο () λειτουργία, διάφορα εργαλεία συγχρονισμού κ.λπ.

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

Πολλές οντότητες εμπλέκονται σε αυτή τη σκοτεινή ύλη.
Πρώτα, αντικείμενο εργασίας(απλά εργασία για συντομία) είναι μια δομή που περιγράφει τη συνάρτηση (για παράδειγμα, έναν χειριστή διακοπής) που θέλουμε να προγραμματίσουμε. Μπορεί να θεωρηθεί ως ανάλογο της δομής του tasklet. Κατά τον προγραμματισμό, τα Tasklet προστέθηκαν σε ουρές που ήταν κρυμμένες από τον χρήστη, αλλά τώρα πρέπει να χρησιμοποιήσουμε μια ειδική ουρά - ουρά εργασίας.
Τα Tasklet συλλέγονται από τη συνάρτηση χρονοπρογραμματιστή και η ουρά εργασίας επεξεργάζεται από ειδικά νήματα που ονομάζονται εργαζόμενοι.
Εργάτηςπαρέχουν ασύγχρονη εκτέλεση εργασιών από την ουρά εργασίας. Αν και ονομάζουν την εργασία με σειρά εκ περιτροπής, στη γενική περίπτωση δεν τίθεται θέμα αυστηρής, διαδοχικής εκτέλεσης: άλλωστε εδώ γίνεται η προκοπή, ο ύπνος, η αναμονή κ.λπ.

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

Για να περιγράψετε τις κύριες δυνατότητες του μηχανισμού ουράς εργασίας, προτείνω να εξερευνήσετε το API.

Σχετικά με την ουρά και τη δημιουργία της

alloc_workqueue (fmt, flags, max_active, args...)
Οι παράμετροι fmt και args είναι η μορφή printf για το όνομα και τα ορίσματα σε αυτό. Η παράμετρος max_activate είναι υπεύθυνη για τον μέγιστο αριθμό εργασιών που από αυτήν την ουρά μπορούν να εκτελεστούν παράλληλα σε μία CPU.
Μπορεί να δημιουργηθεί μια ουρά με τις ακόλουθες σημαίες:
  • WQ_HIGHPRI
  • WQ_UNBOUND
  • WQ_CPU_INTENSIVE
  • WQ_FREEZABLE
  • WQ_MEM_RECLAIM
Ιδιαίτερη προσοχή πρέπει να δοθεί στη σημαία WQ_UNBOUND. Με βάση την παρουσία αυτής της σημαίας, οι ουρές χωρίζονται σε δεσμευμένες και μη.
Σε συνδεδεμένες ουρέςΌταν προστίθενται, οι εργασίες συνδέονται με την τρέχουσα CPU, δηλαδή, σε τέτοιες ουρές, οι εργασίες εκτελούνται στον πυρήνα που την προγραμματίζει. Από αυτή την άποψη, οι δεσμευμένες ουρές μοιάζουν με tasklets.
Σε αδέσμευτες ουρέςοι εργασίες μπορούν να εκτελεστούν σε οποιονδήποτε πυρήνα.

Ένα σημαντικό χαρακτηριστικό της υλοποίησης της ουράς εργασίας στον πυρήνα του Linux είναι η πρόσθετη οργάνωση της παράλληλης εκτέλεσης που υπάρχει σε δεσμευμένες ουρές. Είναι γραμμένο με περισσότερες λεπτομέρειες παρακάτω, αλλά τώρα θα πω ότι πραγματοποιείται με τέτοιο τρόπο ώστε να χρησιμοποιείται όσο το δυνατόν λιγότερη μνήμη και έτσι ώστε ο επεξεργαστής να μην μένει αδρανής. Όλα αυτά υλοποιούνται με την υπόθεση ότι μια εργασία δεν χρησιμοποιεί πάρα πολλούς κύκλους επεξεργαστή.
Αυτό δεν ισχύει για τις μη συνδεδεμένες ουρές. Ουσιαστικά, τέτοιες ουρές απλώς παρέχουν το πλαίσιο στους εργαζόμενους και τους ξεκινούν όσο το δυνατόν νωρίτερα.
Έτσι, θα πρέπει να χρησιμοποιούνται μη συνδεδεμένες ουρές εάν αναμένεται φόρτος εργασίας έντασης CPU, καθώς σε αυτήν την περίπτωση ο προγραμματιστής θα φροντίσει για την παράλληλη εκτέλεση σε πολλούς πυρήνες.

Κατ' αναλογία με τα tasklets, τα έργα μπορούν να έχουν προτεραιότητα εκτέλεσης, κανονική ή υψηλή. Η προτεραιότητα είναι κοινή για ολόκληρη την ουρά. Από προεπιλογή, η ουρά έχει κανονική προτεραιότητα και αν ορίσετε τη σημαία WQ_HIGHPRI, τότε, ανάλογα, υψηλή.

Σημαία WQ_CPU_INTENSIVEέχει νόημα μόνο για δεσμευμένες ουρές. Αυτή η σημαία είναι μια άρνηση συμμετοχής σε μια πρόσθετη οργάνωση παράλληλης εκτέλεσης. Αυτή η σημαία θα πρέπει να χρησιμοποιείται όταν η εργασία αναμένεται να καταναλώσει πολύ χρόνο CPU, οπότε είναι προτιμότερο να μετατεθεί η ευθύνη στον προγραμματιστή. Αυτό περιγράφεται με περισσότερες λεπτομέρειες παρακάτω.

Σημαίες WQ_FREEZABLEΚαι WQ_MEM_RECLAIMείναι συγκεκριμένα και ξεφεύγουν από το αντικείμενο του θέματος, οπότε δεν θα σταθούμε αναλυτικά σε αυτά.

Μερικές φορές είναι λογικό να μην δημιουργείτε τις δικές σας ουρές, αλλά να χρησιμοποιείτε κοινές. Τα κυριότερα:

  • system_wq - δεσμευμένη ουρά για γρήγορη εργασία
  • system_long_wq - μια δεσμευμένη ουρά για εργασίες που αναμένεται να διαρκέσουν πολύ χρόνο για να εκτελεστούν
  • system_unbound_wq - μη δεσμευμένη ουρά

Σχετικά με την εργασία και τον προγραμματισμό τους

Τώρα ας ασχοληθούμε με τα έργα. Αρχικά, ας δούμε τις μακροεντολές προετοιμασίας, δήλωσης και προετοιμασίας:
DECLARE(_DELAYED)_WORK(name, void (*function)(struct work_struct *work)); /* κατά το χρόνο μεταγλώττισης */ INIT(_DELAYED)_WORK(_work, _func); /* κατά την εκτέλεση */ PREPARE(_DELAYED)_WORK(_work, _func); /* για να αλλάξετε τη συνάρτηση που εκτελείται */
Τα έργα προστίθενται στην ουρά χρησιμοποιώντας τις συναρτήσεις:
bool queue_work(struct workqueue_struct *wq, struct work_struct *work); bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay); /* η εργασία θα προστεθεί στην ουρά μόνο αφού λήξει η καθυστέρηση */
Αξίζει να σταθούμε σε αυτό με περισσότερες λεπτομέρειες. Αν και καθορίζουμε μια ουρά ως παράμετρο, στην πραγματικότητα, οι εργασίες δεν τοποθετούνται στην ίδια την ουρά εργασίας, όπως μπορεί να φαίνεται, αλλά σε μια εντελώς διαφορετική οντότητα - στη λίστα ουρών της δομής worker_pool. Δομή worker_pool, στην πραγματικότητα, είναι η πιο σημαντική οντότητα στην οργάνωση του μηχανισμού της ουράς εργασίας, αν και για τον χρήστη παραμένει στα παρασκήνια. Μαζί τους δουλεύουν οι εργαζόμενοι και σε αυτούς περιέχονται όλες οι βασικές πληροφορίες.

Τώρα ας δούμε ποιες ομάδες υπάρχουν στο σύστημα.
Αρχικά, πισίνες για δεσμευμένες ουρές (στην εικόνα). Για κάθε CPU, δύο ομάδες εργαζομένων κατανέμονται στατικά: η μία για εργασία υψηλής προτεραιότητας και η άλλη για εργασία με κανονική προτεραιότητα. Δηλαδή, αν έχουμε τέσσερις πυρήνες, τότε θα υπάρχουν μόνο οκτώ δεμένες πισίνες, παρά το γεγονός ότι μπορεί να υπάρχουν όσες ουρές εργασίας επιθυμείτε.
Όταν δημιουργούμε μια ουρά εργασίας, έχει μια υπηρεσία που εκχωρείται για κάθε CPU πισίνα_ουρά εργασίας(pwq). Κάθε τέτοιο pool_workqueue συσχετίζεται με μια ομάδα εργαζομένων, η οποία εκχωρείται στην ίδια CPU και αντιστοιχεί σε προτεραιότητα στον τύπο της ουράς. Μέσω αυτών, η ουρά εργασίας αλληλεπιδρά με την ομάδα εργαζομένων.
Οι εργαζόμενοι εκτελούν εργασία από την ομάδα των εργαζομένων αδιακρίτως, χωρίς να διακρίνουν σε ποια ουρά εργασίας ανήκαν αρχικά.

Για μη συνδεδεμένες ουρές, οι ομάδες εργαζομένων κατανέμονται δυναμικά. Όλες οι ουρές μπορούν να χωριστούν σε κλάσεις ισοδυναμίας ανάλογα με τις παραμέτρους τους και για κάθε τέτοια κατηγορία δημιουργείται το δικό της σύνολο εργαζομένων. Η πρόσβαση σε αυτά γίνεται χρησιμοποιώντας έναν ειδικό πίνακα κατακερματισμού, όπου το κλειδί είναι ένα σύνολο παραμέτρων και η τιμή, αντίστοιχα, είναι η ομάδα εργαζομένων.
Στην πραγματικότητα, για τις μη δεσμευμένες ουρές τα πάντα είναι λίγο πιο περίπλοκα: αν για δεσμευμένες ουρές δημιουργήθηκαν pwq και ουρές για κάθε CPU, εδώ δημιουργούνται για κάθε κόμβο NUMA, αλλά αυτή είναι μια πρόσθετη βελτιστοποίηση που δεν θα εξετάσουμε λεπτομερώς.

Όλα τα μικροπράγματα

Θα δώσω επίσης μερικές λειτουργίες από το API για να ολοκληρώσω την εικόνα, αλλά δεν θα μιλήσω για αυτές λεπτομερώς:
/* Αναγκαστική ολοκλήρωση */ bool flush_work(struct work_struct *work); bool flush_delayed_work(struct delayed_work *dwork); /* Ακύρωση εκτέλεσης εργασίας */ bool cancel_work_sync(struct work_struct *work); bool cancel_delayed_work(struct delayed_work *dwork); bool cancel_delayed_work_sync(struct delayed_work *dwork); /* Διαγραφή ουράς */ voidstruct_workqueue(struct workqueue_struct *wq);

Πώς οι εργαζόμενοι κάνουν τη δουλειά τους

Τώρα που εξοικειωθήκαμε με το API, ας προσπαθήσουμε να κατανοήσουμε με περισσότερες λεπτομέρειες πώς λειτουργεί και πώς γίνεται η διαχείριση του.
Κάθε πισίνα έχει ένα σύνολο εργαζομένων που χειρίζονται εργασίες. Επιπλέον, ο αριθμός των εργαζομένων αλλάζει δυναμικά, προσαρμοζόμενος στην τρέχουσα κατάσταση.
Όπως έχουμε ήδη ανακαλύψει, οι εργαζόμενοι είναι νήματα που εκτελούν εργασία στο πλαίσιο του πυρήνα. Ο εργάτης τα ανακτά με τη σειρά, το ένα μετά το άλλο, από τη δεξαμενή εργαζομένων που σχετίζεται με αυτό και οι εργάτες, όπως ήδη γνωρίζουμε, μπορούν να ανήκουν σε διαφορετικές ουρές πηγής.

Οι εργαζόμενοι μπορούν υπό όρους να βρίσκονται σε τρεις λογικές καταστάσεις: μπορεί να είναι αδρανείς, να τρέχουν ή να διαχειρίζονται.
Ο εργαζόμενος μπορεί μείνε αδρανήςκαι να μην κάνεις τίποτα. Αυτό συμβαίνει, για παράδειγμα, όταν όλες οι εργασίες εκτελούνται ήδη. Όταν ένας εργαζόμενος μπαίνει σε αυτή την κατάσταση, πηγαίνει για ύπνο και, κατά συνέπεια, δεν θα εκτελέσει μέχρι να ξυπνήσει.
Εάν δεν απαιτείται διαχείριση πισίνας και η λίστα των προγραμματισμένων εργασιών δεν είναι κενή, τότε ο εργαζόμενος ξεκινά την εκτέλεσή τους. Τέτοιους εργάτες θα ονομάζουμε συμβατικά τρέξιμο.
Εάν είναι απαραίτητο, ο εργαζόμενος αναλαμβάνει το ρόλο διευθυντήςπισίνα. Μια πισίνα μπορεί να έχει είτε μόνο έναν διαχειριστή είτε κανέναν εργαζόμενο. Το καθήκον του είναι να διατηρεί τον βέλτιστο αριθμό εργαζομένων ανά ομάδα. Πώς το κάνει; Πρώτον, διαγράφονται οι εργαζόμενοι που έχουν μείνει σε αδράνεια για μεγάλο χρονικό διάστημα. Δεύτερον, δημιουργούνται νέοι εργαζόμενοι εάν πληρούνται τρεις προϋποθέσεις ταυτόχρονα:

  • υπάρχουν ακόμη εργασίες προς ολοκλήρωση (εργασίες στην πισίνα)
  • όχι αδρανείς εργάτες
  • δεν υπάρχουν εργαζόμενοι (δηλαδή ενεργοί και δεν κοιμούνται)
Ωστόσο, η τελευταία συνθήκη έχει τις δικές της αποχρώσεις. Εάν οι ουρές της πισίνας δεν είναι συνδεδεμένες, τότε οι εργαζόμενοι που τρέχουν δεν λαμβάνονται υπόψη· γι' αυτούς αυτή η συνθήκη ισχύει πάντα. Το ίδιο ισχύει στην περίπτωση ενός εργαζόμενου που εκτελεί μια εργασία από μια συνδεδεμένη, αλλά με τη σημαία WQ_CPU_INTENSIVE, ουρές. Επιπλέον, στην περίπτωση των δεμένων ουρών, αφού οι εργαζόμενοι εργάζονται με έργα από την κοινή πισίνα (που είναι ένα από τα δύο για κάθε πυρήνα στην παραπάνω εικόνα), αποδεικνύεται ότι μερικά από αυτά υπολογίζονται ως εργαζόμενα και άλλα όχι. Επίσης προκύπτει ότι η εκτέλεση εργασιών από WQ_CPU_INTENSIVEΟι ουρές μπορεί να μην ξεκινούν αμέσως, αλλά οι ίδιες δεν παρεμβαίνουν στην εκτέλεση άλλων εργασιών. Τώρα θα πρέπει να είναι σαφές γιατί αυτή η σημαία ονομάζεται έτσι και γιατί χρησιμοποιείται όταν αναμένουμε ότι η εργασία θα χρειαστεί πολύ χρόνο για να ολοκληρωθεί.

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

Όσοι ενδιαφέρονται μπορούν να δουν τη συνάρτηση worker στον πυρήνα, που ονομάζεται worker_thread().

Όλες οι περιγραφόμενες λειτουργίες και δομές μπορείτε να τις βρείτε με περισσότερες λεπτομέρειες στα αρχεία include/linux/workqueue.h, πυρήνας/ουρά εργασίας.γΚαι kernel/workqueue_internal.h. Υπάρχει επίσης τεκμηρίωση για την ουρά εργασίας Τεκμηρίωση/ουρά εργασίας.txt.

Αξίζει επίσης να σημειωθεί ότι ο μηχανισμός ουράς εργασίας χρησιμοποιείται στον πυρήνα όχι μόνο για την επεξεργασία καθυστερημένης διακοπής (αν και αυτό είναι ένα αρκετά κοινό σενάριο).

Έτσι, εξετάσαμε τους μηχανισμούς για τον χειρισμό αναβαλλόμενης διακοπής στον πυρήνα του Linux - tasklet και workqueue, που αποτελούν μια ειδική μορφή πολλαπλών εργασιών. Μπορείτε να διαβάσετε για διακοπές, εργαστήρια και ουρές εργασίας στο βιβλίο «Linux Device Drivers» των Jonathan Corbet, Greg Kroah-Hartman, Alessandro Rubini, αν και οι πληροφορίες εκεί είναι μερικές φορές ξεπερασμένες.