SysV IPC

msgsnd#

שליחת הודעה לתור הודעות של System V IPC.

msgsnd הוא עטיפה דקה סביב ה־syscall msgsnd(2). הוא מוסיף את MSG לתור המזוהה על־ידי ID (מזהה התור שהוחזר קודם לכן על־ידי msgget), ומחזיר אמת בהצלחה או שקר בשגיאה כאשר $! מוגדר. MSG אינו חופשי בצורתו: עליו להתחיל בסוג הודעת long מקומי, ולאחריו בתי המטען. FLAGS שולטים בהתנהגות חסימה ושגיאה־בעת־הצפה, בדרך כלל 0 או IPC_NOWAIT.

תקציר#

msgsnd ID, MSG, FLAGS

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

אמת בהצלחה, שקר בכישלון. בכישלון $! מוגדר ל־errno מ־msgsnd(2) - ערכים נפוצים הם EAGAIN (תור מלא, IPC_NOWAIT הוגדר), EIDRM (התור הוסר), EINTR (אות הפסיק קריאה חוסמת), ו־EACCES (אין הרשאת כתיבה על התור).

מאחר וערך ההחזרה הוא בוליאני פשוט, יש לבדוק אותו תמיד לפני שמניחים שההודעה הוכנסה לתור:

msgsnd($qid, $msg, 0)
    or die "msgsnd($qid) failed: $!";

תבנית ההודעה#

הקרנל שומר את sizeof(long) הבתים הראשונים של כל הודעה בתור עבור סוג ההודעה, מספר שלם חיובי מסוג long שקוראי msgrcv משתמשים בו לבחירת ההודעות שיוצאות מהתור. המטען בא אחריו, ללא פירוש.

בנו את ה־buffer באמצעות pack עם מציינים l! (long מקומי) ו־a* (בתים גולמיים):

my $type    = 1;
my $payload = "hello queue";
my $msg     = pack("l! a*", $type, $payload);
msgsnd($qid, $msg, 0) or die "msgsnd: $!";

סוג ההודעה חייב להיות גדול מאפס. אפס וערכים שליליים שמורים לסמנטיקת הברירה של msgrcv וגורמים ל־msgsnd(2) להיכשל עם EINVAL.

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

  • $! - מוגדר ל־errno מה־syscall שנכשל כאשר msgsnd מחזיר שקר. לא נוגעים בו בהצלחה.

דוגמאות#

שליחת הודעה ממוינת פשוטה עם התנהגות החסימה כברירת מחדל. אם התור מלא, הקריאה ממתינה עד שיהיה מקום פנוי:

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

my $qid = msgget(IPC_PRIVATE, IPC_CREAT | S_IRUSR | S_IWUSR)
    // die "msgget: $!";

my $msg = pack("l! a*", 1, "job-42");
msgsnd($qid, $msg, 0) or die "msgsnd: $!";

שליחה לא־חוסמת - החזרה מיידית עם EAGAIN ב־$! אם התור מלא, במקום שינה:

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

unless (msgsnd($qid, $msg, IPC_NOWAIT)) {
    if ($! == EAGAIN) {
        warn "queue full, dropping message\n";
    } else {
        die "msgsnd: $!";
    }
}

שימוש בשדה הסוג כתגית עדיפות / ערוץ. מקבלים בוחרים לפי סוג באמצעות msgrcv:

my %CHANNEL = (log => 1, metric => 2, alert => 3);

sub send_event {
    my ($kind, $body) = @_;
    my $msg = pack("l! a*", $CHANNEL{$kind}, $body);
    msgsnd($qid, $msg, 0) or die "msgsnd[$kind]: $!";
}

send_event(alert  => "disk > 90%");
send_event(metric => "rps=1240");

שליחת מטען מובנה - בצעו pack לשדות הכותרת אחרי קידומת הסוג החובה:

my $type = 1;
my $body = pack("N N a*", $job_id, $retry_count, $payload);
msgsnd($qid, pack("l! a*", $type, $body), 0)
    or die "msgsnd: $!";

מקרי קצה#

  • סוג ההודעה חייב להיות חיובי. העברת 0 או סוג שלילי נכשלת עם EINVAL. סמנטיקת בחירת־הסוג ב־msgrcv שומרת ערכים אלה לוגיקת ברירה.

  • ההודעה חורגת מ־msg_qbytes. אם MSG גדול מהגבול לכל־הודעה המוגדר של התור, msgsnd נכשל עם EINVAL ללא תלות ב־FLAGS. בדקו והעלו את הגבול עם msgctl IPC_SET אם אתם שולטים בתור.

  • תור מלא ללא IPC_NOWAIT. הקריאה חוסמת עד שתהליך אחר מוציא הודעה מהתור, התור מוסר (EIDRM), או אות מפסיק אותה (EINTR). נסו שוב ב־EINTR אם האות צפוי ולא־מזיק.

  • תור מלא עם IPC_NOWAIT. נכשל מיד עם EAGAIN. זו הדרך היחידה לכפות חביון תחום בצד השליחה.

  • קידומת סוג חסרה. שכחה לבצע pack לקידומת הסוג l! מעבירה את המטען הגולמי כפי שהוא; sizeof(long) הבתים הראשונים מפורשים אז כסוג - בדרך כלל מספר חיובי או שלילי גדול - והשאר כגוף. מקבלים שמסננים לפי סוג לא רואים דבר.

  • ID הוא מזהה תור, לא מפתח. העבירו את המספר השלם שהוחזר על־ידי msgget, לא את ה־KEY שניתן ל־msgget. בלבול בין השניים מניב EINVAL או, גרוע יותר, שליחה מוצלחת לתור לא־קשור שבמקרה חולק את הערך המספרי.

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

  • Tainting. MSG נקרא כמחרוזת בתים; אין מבצעים בדיקת taint עליו כאן. אם MSG הגיע ממקור לא־מהימן, צד המקבל (msgrcv, שכן מכתים את הפלט שלו) הוא המקום למסנן.

הבדלים מ־upstream#

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

ראו גם#

  • msgget - השגת מזהה התור המועבר כ־ID; קראו לכך תחילה, או קבלו את המזהה ממי שיצר את התור

  • msgrcv - פרימיטיב הקבלה המתאים; הארגומנט TYPE שלו בורר מול קידומת ה־l! הנכתבת כאן

  • msgctl - בחנו את גבול ה־msg_qbytes של התור או הסירו את התור בסיום

  • pack - בניית ה־buffer l! a* ש־msgsnd דורש; גם הכלי לכל מבנה נוסף בתוך המטען

  • $! - נושא את ה־errno מ־msgsnd שנכשל; השוו מול קבועי Errno כדי להסתעף על כשלים ספציפיים