SysV IPC

shmget#

יוצר או מחפש מקטע זיכרון משותף של System V ומחזיר את המזהה שלו.

shmget הוא צעד ההקצאה לזיכרון משותף SysV. בהינתן KEY מספרי (או IPC_PRIVATE למקטע אנונימי רענן או מספר ידוע־מראש שהוסכם עם תהליכים אחרים), SIZE מבוקש בבתים, ומסיכת ביטים של FLAGS (דגלי יצירה OR’ed עם מצב 9־ביטי), הוא מחזיר מזהה מספר שלם שהוקצה על־ידי הקרנל. המזהה הזה - ה־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 הוא ערך לגיטימי בחלק מהמערכות. תמיד בדוק את ההחזרה מול sentinel הכישלון המתועד (ראה הבדלים מהמקור).

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

  • $! - מוגדר בכישלון ל־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 וביטי המצב מתעלמים מהם בצורה זו; העבר 0 עבור שניהם:

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

יצירה־או־פתיחה עם IPC_CREAT. אם המפתח כבר קיים ה־shmid הקיים מוחזר; אחרת מקטע חדש של SIZE בתים נוצר עם המצב הנתון:

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) וביטי המצב (S_IRUSR, S_IWUSR, S_IRGRP, …) אינם מובנים. ללא הייבוא אתה מסיים בהעברת barewords חשופים שמומרים למחרוזת כ־0 ועושים בשקט את הדבר הלא־נכון.

  • מפתח 0 אינו IPC_PRIVATE. IPC_PRIVATE מוגדר כ־0 ב־Linux, אך העברת 0 ליטרלי ללא IPC_CREAT מבקשת מהקרנל את המקטע במפתח 0, שהוא ה־sentinel של 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־מ־גודל־גדול כסוגיות תצורה, לא באגים.

  • SysV IPC אינו זמין בכל פלטפורמה. Perl המקורי שנבנה על מערכת ללא HAS_SHM מת עם "System V IPC is not implemented on this machine". pperl מכוון רק ל־Linux, שבו SysV IPC נוכח תמיד, כך שהקריאה תמיד מופנית.

הבדלים מהמקור#

  • מחזיר -1 במקום undef בכישלון. שני הערכים שקריים תחת כללי האמת של Perl, כך שה־shmget(...) or die "$!" האידיומטי עובד ללא שינוי, אך קוד שמבחין בין ״לא מזהה״ דרך defined $shmid יקרא את -1 כמוגדר. העדף בדיקת אמת, או השווה מול -1 במפורש כשאתה רוצה להסתעף על sentinel הכישלון. ה־-1 הוא ערך ההחזרה הגולמי של shmget(2), שהוא sentinel השגיאה המתועד לקריאת המערכת הזו.

ראו גם#

  • shmctl - פעולות בקרה על המקטע ש־shmget החזיר; השתמש ב־IPC_RMID כדי למחוק אותו, ב־IPC_STAT כדי לבדוק גודל והרשאות

  • shmread - קורא SIZE בתים מהמקטע לסקלר Perl

  • shmwrite - כותב סקלר Perl לתוך המקטע ב־offset

  • msgget - צעד ההקצאה המקביל לתורי הודעות SysV כשאתה רוצה תור, לא buffer שטוח

  • semget - המקבילה לסמפורי SysV כשהמטרה היא סנכרון במקום נתונים משותפים

  • IPC::SysV - מקור הקבועים IPC_* ו־S_* של דגלים ש־shmget דורש; אין דף עיון מקומי, ראה POD מקורי