SysV IPC

shmget#

Δημιουργεί ή αναζητά τμήμα κοινόχρηστης μνήμης System V και επιστρέφει το αναγνωριστικό του.

Η shmget είναι το βήμα εκχώρησης για κοινόχρηστη μνήμη SysV. Δοθέντος αριθμητικού KEY (είτε IPC_PRIVATE για φρέσκο ανώνυμο τμήμα είτε ενός γνωστού αριθμού που συμφωνείται με άλλες διεργασίες), του αιτούμενου SIZE σε bytes, και μάσκας bits FLAGS (σημαίες δημιουργίας OR-αρισμένες με 9-bit mode), επιστρέφει ακέραιο αναγνωριστικό που έχει εκχωρηθεί από τον πυρήνα. Αυτό το αναγνωριστικό - το shmid - είναι ό,τι χρησιμοποιεί κάθε επόμενη κλήση (shmctl, shmread, shmwrite) για να αναφερθεί στο τμήμα. Το τμήμα παραμένει στον πυρήνα μέχρι να αφαιρεθεί ρητά με shmctl($shmid, IPC_RMID, 0) ή να επανεκκινήσει το σύστημα· το κλείσιμο της δημιουργούσας διεργασίας δεν το απελευθερώνει.

Σύνοψη#

shmget KEY, SIZE, FLAGS

Τι επιστρέφεται#

Ακέραιο αναγνωριστικό κοινόχρηστης μνήμης (shmid) σε επιτυχία, κατάλληλο για πέρασμα στις shmctl, shmread, και shmwrite. Σε αποτυχία επιστρέφει ψευδή τιμή και θέτει το $! στο υποκείμενο errno.

use IPC::SysV qw(IPC_PRIVATE IPC_CREAT S_IRUSR S_IWUSR);

my $shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT | S_IRUSR | S_IWUSR)
    or die "shmget: $!";

Το επιστρεφόμενο αναγνωριστικό είναι αδιαφανής μικρός ακέραιος. Μην κάνετε αριθμητική σε αυτό, μην το συγκρίνετε με 0 για αλήθεια - το αναγνωριστικό 0 είναι νόμιμη τιμή σε ορισμένα συστήματα. Πάντα ελέγχετε την επιστροφή έναντι της τεκμηριωμένης φρουράς αποτυχίας (δείτε Διαφορές από το upstream).

Καθολική κατάσταση που επηρεάζει#

  • $! - τίθεται σε αποτυχία στο υποκείμενο errno: EACCES (δικαιώματα), EEXIST (τμήμα για αυτό το κλειδί υπάρχει ήδη και δόθηκαν και τα δύο IPC_CREAT και IPC_EXCL), EINVAL (SIZE κάτω από SHMMIN ή πάνω από SHMMAX, ή το SIZE διαφέρει από το υπάρχον τμήμα κατά την αναζήτησή του), ENFILE / ENOSPC (πίνακας τμημάτων πυρήνα γεμάτος), ENOENT (κανένα τμήμα για αυτό το κλειδί και δεν δόθηκε IPC_CREAT), ENOMEM (ανεπαρκής μνήμη).

Η συνάρτηση είναι κατά τα άλλα καθαρή ως προς την κατάσταση που φαίνεται στην Perl: καμία ειδική μεταβλητή πέραν του $! δεν αγγίζεται, το $_ δεν διαβάζεται.

Παραδείγματα#

Δημιουργία φρέσκου ιδιωτικού τμήματος - το συνηθισμένο μοτίβο όταν γονέας και παιδιά μοιράζονται μνήμη μέσω fork:

use IPC::SysV qw(IPC_PRIVATE IPC_CREAT S_IRUSR S_IWUSR);

my $size  = 4096;
my $shmid = shmget(IPC_PRIVATE, $size, IPC_CREAT | S_IRUSR | S_IWUSR)
    or die "shmget: $!";
# children inherit $shmid across fork and attach via shmread/shmwrite

Σύνδεση σε προϋπάρχον τμήμα μέσω του γνωστού του κλειδιού. Το SIZE και τα mode bits αγνοούνται σε αυτή τη μορφή· περάστε 0 και για τα δύο:

my $key   = 0xDEADBEEF;
my $shmid = shmget($key, 0, 0)
    or die "shmget: $!";

Δημιουργία-ή-άνοιγμα με IPC_CREAT. Αν το κλειδί υπάρχει ήδη επιστρέφεται το υπάρχον shmid· διαφορετικά δημιουργείται νέο τμήμα μεγέθους SIZE bytes με το δοθέν mode:

use IPC::SysV qw(IPC_CREAT S_IRUSR S_IWUSR S_IRGRP);

my $shmid = shmget(0x1234, 8192, IPC_CREAT | S_IRUSR | S_IWUSR | S_IRGRP)
    or die "shmget: $!";

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

use IPC::SysV qw(IPC_CREAT IPC_EXCL S_IRUSR S_IWUSR);
use Errno qw(EEXIST);

my $shmid = shmget(0x1234, 4096, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
if (!$shmid) {
    die "shmget: $!" unless $! == EEXIST;
    # another process won the race; fall back to plain lookup
    $shmid = shmget(0x1234, 0, 0) or die "shmget (lookup): $!";
}

Πλήρης κύκλος δημιουργίας-χρήσης-καταστροφής με εγγυημένο καθαρισμό μέσω END:

use IPC::SysV qw(IPC_PRIVATE IPC_CREAT IPC_RMID S_IRUSR S_IWUSR);

my $shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | S_IRUSR | S_IWUSR)
    or die "shmget: $!";

END {
    shmctl($shmid, IPC_RMID, 0) if defined $shmid;
}

shmwrite($shmid, "hello", 0, 5)  or die "shmwrite: $!";
shmread($shmid, my $buf, 0, 5)   or die "shmread: $!";

Οριακές περιπτώσεις#

  • Το use IPC::SysV είναι ουσιαστικά υποχρεωτικό. Οι σταθερές σημαιών (IPC_PRIVATE, IPC_CREAT, IPC_EXCL, IPC_RMID) και τα mode bits (S_IRUSR, S_IWUSR, S_IRGRP, …) δεν είναι ενσωματωμένα. Χωρίς την εισαγωγή καταλήγετε να περνάτε σκέτες barewords που μετατρέπονται σε 0 και κάνουν σιωπηρά το λάθος πράγμα.

  • Το κλειδί 0 δεν είναι IPC_PRIVATE. Το IPC_PRIVATE ορίζεται ως 0 στο Linux, αλλά το πέρασμα κυριολεκτικού 0 χωρίς IPC_CREAT ζητά από τον πυρήνα το τμήμα στο κλειδί 0, που είναι η φρουρά IPC_PRIVATE και αποδίδει EINVAL. Εισάγετε τη σταθερά βάσει ονόματος και χρησιμοποιήστε την.

  • Το SIZE στρογγυλοποιείται προς τα πάνω σε όριο σελίδας από τον πυρήνα· το επιστρεφόμενο τμήμα μπορεί να είναι μεγαλύτερο από αυτό που ζητήθηκε. Ζητήστε από τον πυρήνα το ενεργό μέγεθος με shmctl($shmid, IPC_STAT, $ds) και αποπακετάρετε τη δομή shmid_ds αν χρειάζεστε τον πραγματικό αριθμό.

  • Το τμήμα παραμένει πέραν της εξόδου της διεργασίας. Ένας δημιουργός που έχει καταρρεύσει ή σκοτωθεί αφήνει το τμήμα στον πυρήνα. Χρησιμοποιήστε ipcs -m για να καταγράψετε ορφανά τμήματα και ipcrm -m <shmid> για να τα αφαιρέσετε. Εγκαταστήστε μπλοκ END ή χειριστή σήματος για να καλέσετε shmctl IPC_RMID στον τερματισμό.

  • Το IPC_RMID είναι αναβληθέν, όχι άμεσο. Σημαίνει το τμήμα για καταστροφή· η πραγματική αφαίρεση συμβαίνει μόλις πέσει στο μηδέν ο μετρητής συνδέσεων. Ασφαλές να καλεστεί αμέσως μετά την shmget αν γνωρίζετε ότι κανείς άλλος δεν θα συνδεθεί.

  • Όρια πυρήνα. Το SHMMNI περιορίζει τον συνολικό αριθμό τμημάτων στο σύστημα (/proc/sys/kernel/shmmni). Το SHMMAX περιορίζει το μέγεθος ενός τμήματος (/proc/sys/kernel/shmmax). Σε κοινόχρηστο υπολογιστή είναι εύκολο να τα φτάσετε· αντιμετωπίστε τα ENOSPC και EINVAL-από-μεγάλο-μέγεθος ως ζητήματα διαμόρφωσης, όχι ως bugs.

  • Το SysV IPC δεν είναι διαθέσιμο σε κάθε πλατφόρμα. Η upstream Perl χτισμένη σε σύστημα χωρίς HAS_SHM πεθαίνει με "System V IPC is not implemented on this machine". Η pperl στοχεύει μόνο το Linux, όπου το SysV IPC είναι πάντα παρόν, οπότε η κλήση αποστέλλεται πάντα.

Διαφορές από το upstream#

  • Επιστρέφει -1 αντί για undef σε αποτυχία. Και οι δύο τιμές είναι ψευδείς υπό τους κανόνες αλήθειας της Perl, οπότε το ιδιωματικό shmget(...) or die "$!" λειτουργεί αμετάβλητο, αλλά κώδικας που διακρίνει το «όχι αναγνωριστικό» μέσω defined $shmid θα διαβάσει το -1 ως ορισμένο. Προτιμήστε τον έλεγχο για αλήθεια, ή συγκρίνετε ρητά έναντι του -1 όταν θέλετε να διακλαδώσετε βάσει της φρουράς αποτυχίας. Το -1 είναι η ωμή τιμή επιστροφής της shmget(2), που είναι η τεκμηριωμένη φρουρά σφάλματος για αυτή την κλήση συστήματος.

Δείτε επίσης#

  • shmctl - λειτουργίες ελέγχου στο τμήμα που επέστρεψε η shmget· χρησιμοποιήστε IPC_RMID για να το διαγράψετε, IPC_STAT για να εξετάσετε το μέγεθος και τα δικαιώματα

  • shmread - διαβάζει SIZE bytes από το τμήμα σε βαθμωτό της Perl

  • shmwrite - γράφει βαθμωτό της Perl στο τμήμα σε ένα offset

  • msgget - το ισοδύναμο βήμα εκχώρησης για ουρές μηνυμάτων SysV όταν θέλετε ουρά, όχι επίπεδο ενταμιευτή

  • semget - το ισοδύναμο για σηματοφόρους SysV όταν ο στόχος είναι συγχρονισμός παρά κοινόχρηστα δεδομένα

  • IPC::SysV - πηγή των σταθερών σημαιών IPC_* και S_* που απαιτεί η shmget· δεν υπάρχει τοπική σελίδα αναφοράς, δείτε το upstream POD