SysV IPC

msgrcv#

לקבל הודעה מתור הודעות System V IPC.

msgrcv הוא הקישור של Perl ל־syscall msgrcv(2). הוא מושך הודעה אחת מהתור שמזוהה על־ידי ID, מאחסן את סוג ההודעה ואת ה־payload לתוך VAR, ומחזיר ערך אמת בהצלחה או שקר בשגיאה. SIZE הוא המספר המקסימלי של בתי payload שהקורא מוכן לקבל; TYPE בוחר איזו הודעה לקבל; FLAGS הוא ה־bitmask שמועבר לקרנל (IPC_NOWAIT, MSG_NOERROR, וכו«).

המאגר המוחזר אינו רק גוף ההודעה - sizeof(long) הבתים הראשונים הם סוג ההודעה שהשולח העביר ל־msgsnd, ואחריו ה־payload. יש להשתמש ב־unpack עם התבנית l! a* כדי לפצל ביניהם.

תקציר#

use IPC::SysV qw(IPC_NOWAIT MSG_NOERROR);

msgrcv $qid, my $buf, 1024, 0, 0            or die "msgrcv: $!";
msgrcv $qid, my $buf, 1024, 0, IPC_NOWAIT;  # non-blocking
my ($type, $payload) = unpack "l! a*", $buf;

מה מקבלים בחזרה#

אמת בהצלחה, שקר בשגיאה כאשר $! מוגדר. בהצלחה, VAR נדרס במסגרת שהתקבלה: long נטיבי (סוג ההודעה) ומיד אחריו עד SIZE בתים של payload. המשתנה מסומן tainted תחת -T.

VAR מוגדל או מוקטן כדי להתאים לאורך המסגרת בפועל - אין צורך לקבוע לו גודל מראש. התוכן לפני הקריאה מושלך.

לקבלת הסוג וה־payload כערכים נפרדים:

my ($type, $msg) = unpack "l! a*", $buf;

ארגומנטים#

  • ID - מזהה התור שהוחזר קודם לכן על־ידי msgget.

  • VAR - סקלר lvalue שמקבל את המסגרת הגולמית. הערך הקודם שלו מושלך בהצלחה.

  • SIZE - גודל ה־payload המקסימלי בבתים. אם ההודעה התואמת הבאה גדולה מזה, msgrcv נכשל עם E2BIG וההודעה נשארת בתור - אלא אם MSG_NOERROR מוגדר ב־FLAGS, ובמקרה זה ההודעה נחתכת בשקט והבתים הנוספים אובדים.

  • TYPE - בוחר איזו הודעה לקבל:

    • 0 - ההודעה הראשונה בתור, לא משנה הסוג שלה.

    • N חיובי - ההודעה הראשונה שסוגה הוא בדיוק N.

    • -N שלילי - ההודעה הראשונה של הסוג הנמוך ביותר שהוא <= abs(N), שימושי לתורי עדיפויות.

  • FLAGS - bitmask של IPC_NOWAIT (להחזיר ENOMSG מיד במקום לחסום כאשר אין הודעה תואמת זמינה), MSG_NOERROR (לחתוך במקום להיכשל על הודעות גדולות מדי), ו־MSG_EXCEPT הספציפי ל־Linux (עם TYPE חיובי, לקבל את ההודעה הראשונה שסוגה אינו TYPE).

הקבועים חיים ב־IPC::SysV; השמות החשופים אינם נראים עד שמייבאים אותם.

מצב גלובלי שהוא נוגע בו#

  • $! - מוגדר בשגיאה ל־errno הבסיסי (ENOMSG, E2BIG, EIDRM, EINVAL, EACCES, EFAULT, EINTR).

  • מצב taint - VAR שהתקבל מסומן tainted, מכיוון שתוכנו הגיע מתהליך אחר.

דוגמאות#

קבלה חוסמת של הודעה כלשהי, ואז פיצול לסוג ו־payload:

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

my $qid = msgget(IPC_PRIVATE, S_IRUSR | S_IWUSR) // die "msgget: $!";
msgrcv $qid, my $buf, 4096, 0, 0            or die "msgrcv: $!";
my ($type, $payload) = unpack "l! a*", $buf;
print "got type=$type payload=$payload\n";

לקבל רק הודעות מסוג 42:

msgrcv $qid, my $buf, 4096, 42, 0           or die "msgrcv: $!";

תשאול לא־חוסם - להחזיר מיד אם התור ריק:

use IPC::SysV qw(IPC_NOWAIT);
use Errno qw(ENOMSG);

if (msgrcv $qid, my $buf, 4096, 0, IPC_NOWAIT) {
    my ($type, $msg) = unpack "l! a*", $buf;
    handle($type, $msg);
} elsif ($! == ENOMSG) {
    # queue was empty; try again later
} else {
    die "msgrcv: $!";
}

לחתוך הודעות גדולות מדי במקום להיכשל:

use IPC::SysV qw(MSG_NOERROR);

msgrcv $qid, my $buf, 64, 0, MSG_NOERROR    or die "msgrcv: $!";
# payload is at most 64 bytes, excess discarded without error

קבלה בסגנון עדיפויות - לקחת את ההודעה עם המספר הנמוך ביותר שסוגה <= 3:

msgrcv $qid, my $buf, 4096, -3, 0           or die "msgrcv: $!";

מקרי קצה#

  • VAR חייב להיות lvalue. העברת קבוע או סקלר לקריאה־בלבד (לדוגמה אלמנט של @_ שהועבר בהפניה למילולי) מעלה Modification of a read-only value attempted.

  • פריסת המסגרת היא long נטיבי, לא ארבעה בתים. ב־Linux 64-bit הסוג תופס 8 בתים; במערכות 32-bit הוא תופס 4. ה־l! ב־unpack "l! a*" - ה־! - חובה; l פשוט מניח 4 בתים ויפגע ב־payloads על מארחי 64-bit.

  • SIZE אינו כולל את כותרת הסוג לצרכי MSG_NOERROR. הקרנל משווה את SIZE מול אורך ה־payload, לא אורך המסגרת. יש לקבוע את גודל המאגר עבור ה־payload הגדול ביותר שמצפים לו; כותרת הסוג נכתבת מעבר לכך.

  • הפסקת אות. msgrcv חוסם מחזיר שקר עם $! == EINTR כאשר אות מגיע לפני שהודעה זמינה. יש לנסות שוב את הקריאה אלא אם האות נועד לבטל את לולאת הקבלה.

  • התור הוסר בעת חסימה. אם תהליך אחר קורא ל־msgctl עם IPC_RMID בעוד תהליך זה ישן ב־msgrcv, הקריאה מחזירה שקר עם $! == EIDRM.

  • הרשאות. קבלה דורשת הרשאת קריאה על התור. EACCES משמעו שהתור קיים אך ה־uid/gid הנוכחי אינו תואם את ביטי המצב של התור.

  • TYPE = 0 רואה סדר FIFO. אין עדיפות ״כל אחד״ פחות מ־TYPE שלילי מפורש - הודעות מכל הסוגים נמשכות בסדר השליחה.

  • תמיכת קרנל. קרנלי Linux שנבנו ללא CONFIG_SYSVIPC, ו־container runtimes שלא חושפים את ה־namespace של IPC, נכשלים בכל msgrcv עם ENOSYS. יש לבדוק בעת האתחול אם יעד הפריסה אינו ודאי.

הבדלים מ־upstream#

תואם מלא ל־upstream Perl 5.42.

ראו גם#

  • msgsnd - המקבילה ששמה הודעות על התור; התבנית pack "l! a*" שלה היא ההפוכה של מה ש־unpack כאן

  • msgget - להשיג את מזהה התור ID שמעבירים פנימה

  • msgctl - לבחון או למחוק את התור, כולל ה־IPC_RMID שגורם ל־msgrcv חסום להחזיר EIDRM

  • unpack - לפצל את המסגרת המוחזרת לסוג ו־payload עם l! a*

  • IPC::SysV - מקור של IPC_NOWAIT, MSG_NOERROR, וקבועי דגלים קשורים

  • IPC::Msg - עטיפה מונחית־עצמים שמסתירה את ריקוד ה־pack/unpack מאחורי מתודת rcv