semop#
מבצע פעולה אחת או יותר על סמפורי System V באופן אטומי.
semop הוא סוס העבודה של השימוש בסמפורי SysV: הוא מקבל מזהה של מערכת סמפורים (מ־semget) ורשימה ארוזה של פעולות, ואז מבקש מהקרנל להחיל את כולן או אף אחת. כל פעולה מציינת סמפור בודד בתוך המערכת, כמות להוסיף לו (בדרך כלל -1 להמתנה, +1 לסיגנל), ומילת דגלים. הקרנל חוסם את הקורא עד שכל פעולה שבוקשה יכולה להתקדם, ואז מבצע אותן כיחידה אחת. זה מה שהופך את semop לשמיש כפרימיטיב הדרה הדדית או משאב־ספירה בין תהליכים בלתי־קשורים.
תקציר#
semop $semid, $opstring
מה מקבלים בחזרה#
אמת בהצלחה, שקר בשגיאה כאשר $! נקבע. ערך האמת הוא התוצאה היחידה - semop מתקשר את התוצאה דרך תופעת הלוואי על מערכת הסמפורים, לא דרך ההחזרה שלו. בדקו אותו בכל קריאה:
semop($semid, $ops)
or die "semop failed: $!";
החזרת שקר משמעה ששום פעולה ב־OPSTRING לא הוחלה; סמנטיקת SysV מבטיחה הכל־או־כלום.
מצב גלובלי שהוא נוגע בו#
קובע את $! בכשל, עם ערכי ה־errno הרגילים של semop(2): EAGAIN כאשר IPC_NOWAIT נקבע והפעולה היתה חוסמת, EIDRM כאשר מערכת הסמפורים הוסרה בעת ההמתנה, EINTR כאשר אות הפסיק את ההמתנה, EINVAL עבור מספר או מזהה סמפור שגויים, EACCES עבור אי־התאמת הרשאות מול mode של המערכת.
מבנה הפעולה#
OPSTRING הוא שרשור של רשומות ברוחב קבוע. כל רשומה היא שלושה מספרים שלמים מסוג short ברוחב מקומי ומיוצרת באמצעות pack:
pack("s!3", $semnum, $semop, $semflag)
$semnum- האינדקס של סמפור המטרה בתוך המערכת (0עדnsems-1, כאשרnsemsהוא הגודל שהועבר ל־semget).$semop- הכמות להוסיף לערך הסמפור. ערכים שליליים ממתינים עד שהסמפור הוא לפחות|$semop|לפני החיסור; ערכים חיוביים מוסיפים ללא תנאי ומעירים ממתינים; אפס ממתין עד שהסמפור מגיע לאפס.$semflag- OR ביטי שלIPC_NOWAIT(להיכשל עםEAGAINבמקום לחסום) ושלSEM_UNDO(לגרום לקרנל להפוך את הפעולה אם התהליך יוצא מבלי לבטלה - חיוני לשימושים דמויי־מנעול שבהם קריסה אסור שתשאיר את הסמפור מוחזק).
האורך של OPSTRING מרמז על מספר הפעולות: הקרנל מעבד length($opstring) / sizeof(struct sembuf) רשומות. העברת שתי רשומות הארוזות סוף־אל־סוף מבקשת שתי פעולות שיוחלו אטומית.
ה־s! בתבנית הוא ה־short המקומי, התואם את ה־layout של sembuf בלינוקס. s רגיל הוא 16־ביט חתום בסדר־רשת ויארוז שגויות בשקט בכל פלטפורמה שבה sizeof(short) != 2 או שבה ה־endianness שונה מסדר־רשת.
דוגמאות#
המתנה (החסרה ב־1) על סמפור 0 של המערכת ב־$semid:
my $op = pack("s!3", 0, -1, 0);
semop($semid, $op)
or die "wait on semaphore failed: $!";
סיגנל (הגדלה ב־1) של אותו סמפור:
my $op = pack("s!3", 0, 1, 0);
semop($semid, $op)
or die "signal failed: $!";
נסו לרכוש ללא חסימה - היכשלו מהר אם מישהו אחר מחזיק אותו:
use POSIX qw(EAGAIN);
my $op = pack("s!3", 0, -1, IPC_NOWAIT);
unless (semop($semid, $op)) {
die "semop: $!" unless $! == EAGAIN;
# contended; caller decides what to do
}
רכישת סמפור 0 עם שחרור אוטומטי ביציאת התהליך - התבנית להדרה הדדית בטוחה־בקריסה:
my $op = pack("s!3", 0, -1, SEM_UNDO);
semop($semid, $op) or die "lock: $!";
# critical section
my $release = pack("s!3", 0, 1, SEM_UNDO);
semop($semid, $release) or die "unlock: $!";
שתי פעולות המוחלות אטומית - המתנה לסמפור 0 ולסמפור 1 ביחד, ללא שחרור אף אחד מהם אם אחד היה חוסם:
my $ops = pack("s!3", 0, -1, 0)
. pack("s!3", 1, -1, 0);
semop($semid, $ops) or die "paired wait: $!";
המתינו לסמפור 0 להגיע לאפס - צורת ה־״barrier״, שבה $semop הוא אפס במקום שלילי:
my $op = pack("s!3", 0, 0, 0);
semop($semid, $op) or die "barrier: $!";
מקרי קצה#
OPSTRINGקצר: האורך חייב להיות כפולה מדויקת של גודל רשומתsembuf. מחרוזת קטומה או מרופדת נותנתEINVAL. תמיד בנו את המחרוזת מקריאותpack, אף פעם לא ביד.רוחב מקומי חשוב:
s!3ולאs3. סקריפט שנכתב עםsרגיל עובד על מארח עם short של 16־ביט במקרה ונשבר במקומות אחרים.OPSTRINGבאורך אפס: מבקש אפס פעולות. הקרנל מקבל זאת ומחזיר הצלחה ללא תופעות לוואי - לעיתים נדירות מה שאתם רוצים. הגנו על הקריאה עםreturn unless length $ops.הפסקת אות:
semopחסום שחוזר עם$!שנקבע ל־EINTRאינו באג - נסו שוב אלא אם התקנתם מטפל שרוצה להפסיק. הטיפול ברירת המחדל של$SIG{*}ב־Perl אינו מפעיל מחדש את ה־syscall אוטומטית.SEM_UNDOעם זוגות תואמים: הקרנל עוקב אחר ההתאמה המצטברת לכל סמפור לכל תהליך. רכישה פעמיים עםSEM_UNDOואז שחרור פעם אחת מותירים-1תלוי ועומד שייעקר ביציאה. צמדו כל-1עם+1תואם בזרימה רגילה;SEM_UNDOהוא ביטוח נגד קריסות, לא תחליף לשחרור.מערכת שהוסרה תוך כדי המתנה: אם תהליך אחר קורא ל־
semctlעםIPC_RMID, כלsemopחסום מחזיר שקר כאשר$!נקבע ל־EIDRM. מזהה הסמפור אינו תקף מנקודה זו והלאה.הרשאות:
semopבודק הרשאת שינוי על המערכת. תהליך שיכול לבצעsemgetעל handle לקריאה־בלבד עדיין לא יכול לבצעsemopעליו.ספירות פעולות גדולות: הקרנל מגביל את מספר הפעולות לכל קריאה ל־
SEMOPM(בדרך כלל 32). בנייתOPSTRINGעם יותר רשומות נכשלת עםE2BIG.
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.
ראו גם#
semget- משיג את המזהה שאתם מעבירים כארגומנט הראשון; השניים תמיד בשימוש יחדsemctl- קובע או מתשאל ערכי סמפור, ומסיר את המערכת כשסיימתם איתהpack- בונה כל רשומתsembuf;s!3היא התבנית שאתם רוציםmsgsnd- המקבילה של תור ההודעות כאשר צריך להעביר נתונים ולא רק לאותת על זמינותshmget- המקבילה של זיכרון משותף; סמפורים משמשים בדרך כלל לשמירה על גישה למקטע זיכרון משותף של SysVIPC::SysV- מייצא את הקבועיםIPC_NOWAIT,SEM_UNDO, ו־IPC_RMIDהמשמשים ב־$semflagובפקודותsemctlIPC::Semaphore- עטיפת עצם מעלsemget/semop/semctlכאשר ממשק פרוצדורלי מרגיש חשוף מדי