IPC System V#

Το IPC System V δίνει σε δύο μη σχετιζόμενες διεργασίες έναν κοινόχρηστο πόρο που κατέχει ο πυρήνας: ένα σύνολο σηματοφόρων για συντονισμό, ή ένα τμήμα κοινόχρηστης μνήμης για τη μεταβίβαση bytes χωρίς αντιγραφή μέσα από σωλήνα. Κάθε πόρος προσδιορίζεται από ένα ακέραιο κλειδί και επιζεί μέχρι μια διεργασία να τον αφαιρέσει ρητά.

Τα αρθρώματα ευκολίας IPC::SysV, IPC::Semaphore και IPC::SharedMem δεν είναι διαθέσιμα στην PetaPerl, οπότε οι συνταγές εδώ χρησιμοποιούν τη χαμηλού επιπέδου οικογένεια ενσωματωμένων συναρτήσεων semget / shmget με τις τιμές των σημαιών γραμμένες ως απλούς αριθμούς. Αυτές οι ενσωματωμένες συναρτήσεις είναι το επίπεδο που περιτυλίγουν τα αρθρώματα· η απευθείας χρήση τους κοστίζει μόνο μερικές ονομασμένες σταθερές που παρέχετε μόνοι σας.

Οι σταθερές σημαιών που χρειάζεστε#

Μια χούφτα μικροί ακέραιοι ελέγχουν το πώς συμπεριφέρονται αυτές οι κλήσεις. Είναι οι τυπικές τιμές του Linux:

use constant {
    IPC_PRIVATE => 0,        # "give me a fresh, private key"
    IPC_CREAT   => 0o1000,   # create the resource if absent
    IPC_EXCL    => 0o2000,   # fail if it already exists
    IPC_RMID    => 0,        # control command: remove the resource
};

Τα χαμηλά εννέα bits της λέξης σημαιών είναι συνηθισμένα bits δικαιωμάτων, τα ίδια 0600 / 0644 που θα περνούσατε στην chmod. Το IPC_CREAT | 0600 σημαίνει «δημιούργησέ το, ανάγνωση/εγγραφή για τον ιδιοκτήτη».

Μια παράδοση μέσω κοινόχρηστης μνήμης#

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

use constant {
    IPC_PRIVATE => 0,
    IPC_CREAT   => 0o1000,
    IPC_RMID    => 0,
};

my $size = 1024;

my $id = shmget(IPC_PRIVATE, $size, IPC_CREAT | 0600);
defined $id or die "shmget: $!";

my $payload = "shared-payload";
shmwrite($id, $payload, 0, length $payload) or die "shmwrite: $!";

my $kid = fork() // die "fork: $!";
if ($kid == 0) {
    # Child reads the same segment the parent wrote.
    shmread($id, my $buf, 0, length $payload) or die "shmread: $!";
    print "child read: $buf\n";          # child read: shared-payload
    exit 0;
}

waitpid($kid, 0);

# The segment outlives the processes - remove it explicitly.
shmctl($id, IPC_RMID, 0) or warn "shmctl: $!";

Δύο σημεία αποφασίζουν αν αυτός ο κώδικας διαρρέει:

  • Οι shmread / shmwrite παίρνουν ρητό μήκος. Δεν σταματούν σε ένα NUL ή σε έναν χαρακτήρα νέας γραμμής· τους λέτε ακριβώς πόσα bytes να μετακινήσουν, ξεκινώντας από μια μετατόπιση. Περάστε length $payload ώστε ο αναγνώστης και ο εγγραφέας να συμφωνούν ως προς το μέγεθος.

  • Το IPC_RMID δεν είναι αυτόματο. Ένα κοινόχρηστο τμήμα παραμένει στον πυρήνα αφού κάθε διεργασία που είχε προσαρτηθεί σε αυτό έχει τερματίσει. Μπορείτε να δείτε ορφανά τμήματα με ipcs -m στο κέλυφος. Πάντα να συνδυάζετε ένα shmget με ένα αντίστοιχο shmctl IPC_RMID.

Ένας σηματοφόρος για συντονισμό#

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

use constant {
    IPC_PRIVATE => 0,
    IPC_CREAT   => 0o1000,
    IPC_RMID    => 0,
    SETVAL      => 16,       # semctl command: set one value
    GETVAL      => 12,       # semctl command: read one value
};

# A set with one semaphore.
my $sem = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
defined $sem or die "semget: $!";

# Initialise semaphore 0 to the value 1 (one token available).
semctl($sem, 0, SETVAL, 1) or die "semctl SETVAL: $!";

my $value = semctl($sem, 0, GETVAL, 0);
print "semaphore value: $value\n";       # semaphore value: 1

# Remove the set when finished.
semctl($sem, 0, IPC_RMID, 0) or warn "semctl IPC_RMID: $!";

Οι ίδιες οι λειτουργίες αναμονής/σηματοδότησης περνούν μέσα από την semop, η οποία παίρνει μια πακεταρισμένη συμβολοσειρά τριάδων (sem_num, sem_op, sem_flg) χτισμένη με pack. Η μείωση κατά ένα περιμένει μέχρι ένα διακριτικό να είναι ελεύθερο· η αύξηση κατά ένα το απελευθερώνει. Όπως με την κοινόχρηστη μνήμη, ένα σύνολο σηματοφόρων που κατέχει ο πυρήνας πρέπει να αφαιρεθεί με IPC_RMID αλλιώς παραμένει - ορατό υπό το ipcs -s.

Επόμενο#

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