מטפלי קובץ קבצים ספריות

fcntl#

מבצע פעולת בקרת קובץ של fcntl(2) על מטפל קובץ.

fcntl הוא הקישור הישיר של Perl לקריאת המערכת POSIX fcntl(2). הוא קורא או משנה מצב per-descriptor - מצב non-blocking, close-on-exec, נעילות רשומה מייעצות, דגלי descriptor - על־ידי הפעלת FUNCTION מול FILEHANDLE, כאשר SCALAR משמש כארגומנט קלט, כבופר פלט, או שניהם, בהתאם לאיזה FUNCTION מועבר.

קודי ה־FUNCTION אינם ליטרלים ברמת Perl; הם מאקרואים של מעבד הקדם של C שנחשפים על־ידי המודול Fcntl. יש לטעון אותו קודם, אחרת FUNCTION יהיה bareword לא־מוגדר:

use Fcntl;

תקציר#

use Fcntl;
fcntl FILEHANDLE, FUNCTION, SCALAR
my $flags = fcntl($fh, F_GETFL, 0);
fcntl($fh, F_SETFL, $flags | O_NONBLOCK);

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

בהצלחה, המספר השלם שהקרנל החזיר. כאשר אותו מספר שלם הוא 0, Perl מחליף במחרוזת דו־ערכית "0 but true" - אמת בהקשר בוליאני, מספרי 0 בהקשר מספרי, ופטור מהאזהרה Argument "..." isn't numeric. הדבר מאפשר לבדוק את התוצאה עם or die אפילו עבור פעולות שערך ההצלחה שלהן הוא תרתי משמע אפס:

fcntl($fh, F_SETFD, FD_CLOEXEC)
    or die "F_SETFD: $!";

בכישלון, fcntl מחזיר undef וקובע את $! ל־errno הרלוונטי. אין צורך לעטוף את הערך המוחזר ב־defined - מוסכמת "0 but true" הופכת בדיקת אמת רגילה למספיקה.

עבור קודי FUNCTION שכותבים בחזרה דרך SCALAR (כגון F_GETLK), הבתים המעודכנים נוחתים בסקלר שהועבר; הערך המוחזר עדיין עוקב אחר הכלל לעיל.

כיצד נעשה שימוש ב־SCALAR#

SCALAR משחק שלושה תפקידים שונים בהתאם ל־FUNCTION:

  • מספר שלם של קלט. עבור קריאות שקובעות דגלים כמו F_SETFD או F_SETFL, SCALAR הוא מסיכת הביטים המספרית להתקנה.

  • מציין־מקום שמתעלמים ממנו. עבור קריאות שמשיגות דגלים כמו F_GETFD או F_GETFL, ה־SCALAR אינו בשימוש על־ידי הקרנל; יש להעביר 0 על־פי המוסכמה.

  • בופר struct לכניסה/יציאה. עבור קריאות לנעילת רשומה כמו F_GETLK, F_SETLK, ו־F_SETLKW, SCALAR חייב להכיל struct flock ארוז - יש לבנות אותו עם pack לפני הקריאה, ועבור F_GETLK יש לפענח את הבתים המוחזרים עם unpack לאחר מכן.

כאשר SCALAR משמש כבופר פלט, יש לקבוע את גודלו מראש לפחות לאורך ה־struct שהקרנל יכתוב; סקלר בגודל לא־מספיק מורחב אוטומטית, אך קביעת גודל מראש הופכת את הכוונה לברורה.

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

  • $! - נקבע בכישלון ל־errno מ־fcntl(2).

  • דגלים ברמת ה־descriptor על FILEHANDLE (למשל O_NONBLOCK, FD_CLOEXEC) מתמשכים לאורך חיי ה־descriptor, שורדים dup ו־fork על־פי כללי POSIX, ואינם נראים ל־Perl דרך כל משתנה אחר. הדרך היחידה לקרוא אותם בחזרה היא קריאת fcntl נוספת עם F_GETFL / F_GETFD.

דוגמאות#

הפיכת סוקט ל־non-blocking - השימוש הקנוני:

use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);

my $flags = fcntl($remote, F_GETFL, 0)
    or die "F_GETFL: $!";
fcntl($remote, F_SETFL, $flags | O_NONBLOCK)
    or die "F_SETFL: $!";

קביעת close-on-exec כך שה־descriptor לא יורש על־ידי תהליכי ילד דרך exec:

use Fcntl qw(F_SETFD FD_CLOEXEC);

fcntl($fh, F_SETFD, FD_CLOEXEC)
    or die "F_SETFD: $!";

קריאת דגלי ה־descriptor הנוכחיים ובדיקת ביט:

use Fcntl qw(F_GETFD FD_CLOEXEC);

my $fd_flags = fcntl($fh, F_GETFD, 0);
defined $fd_flags or die "F_GETFD: $!";
if ($fd_flags & FD_CLOEXEC) {
    # descriptor will close on exec
}

הצבת נעילת כתיבה מייעצת על הקובץ כולו. פריסת struct flock היא ספציפית לפלטפורמה; בלינוקס s s l l i מכסה את l_type l_whence l_start l_len l_pid:

use Fcntl qw(F_SETLK F_WRLCK SEEK_SET);

my $lock = pack('s s l l i', F_WRLCK, SEEK_SET, 0, 0, 0);
fcntl($fh, F_SETLK, $lock)
    or die "lock refused: $!";

כיבוי דגל יחיד - מיסוך החוצה של O_NONBLOCK כדי להחזיר מטפל למצב blocking:

use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);

my $flags = fcntl($fh, F_GETFL, 0);
fcntl($fh, F_SETFL, $flags & ~O_NONBLOCK)
    or die "F_SETFL: $!";

מקרי קצה#

  • שכחו use Fcntl: F_GETFL וחבריו מנותחים כ־barewords ומוערכים כמטרד ברמת אזהרה תחת use strict / use warnings, או למחרוזת "F_GETFL" אחרת - בכל מקרה הקריאה נכשלת עם EINVAL. יש לטעון תמיד את Fcntl קודם.

  • ערך מוחזר "0 but true": אין להשוות את הערך המוחזר עם == 0 כבדיקת כישלון. יש להשתמש בבוליאני רגיל (or die) או defined - הראשון הוא המטבע.

  • F_SETLK לעומת F_SETLKW: F_SETLK מחזיר undef וקובע את $! ל־EAGAIN / EACCES כאשר תהליך אחר מחזיק נעילה מתנגשת. F_SETLKW חוסם עד שהנעילה ניתנת. יש לבחור במכוון; ערבוב ביניהם הוא המקור הרגיל לתקיעות.

  • פלט F_GETLK: בהצלחה, בתי ה־struct flock ב־SCALAR נדרסים במידע על הנעילה המתנגשת (או l_type == F_UNLCK אם האזור פנוי). יש לבצע unpack חזרה עם אותה תבנית שאיתה בוצע pack.

  • לא כל מערכת ממשת כל FUNCTION: fcntl מעלה חריגה על מכונה שאינה ממשת את fcntl(2) כלל. במערכות שכן, קודי FUNCTION שאינם נתמכים נכשלים עם EINVAL דרך $!. המודול Fcntl מייצא רק קבועים שהפלטפורמה הנוכחית בעצם מגדירה.

  • fcntl לעומת ioctl: שניהם חולקים את מוסכמת עיבוד הארגומנטים והערך המוחזר, אך הם מכוונים לממשקי קרנל שונים. יש להשתמש ב־fcntl עבור דגלי descriptor ונעילות מייעצות; יש להשתמש ב־ioctl עבור קודי בקרה ספציפיים להתקן.

  • fcntl לעומת flock: flock משתמש בנעילות flock(2) בסגנון BSD לקובץ כולו, שבלינוקס פועלות יחד עם נעילות POSIX של fcntl רק בדרכים מוגבלות. אם דרושה נעילת טווח־בתים או סמנטיקת POSIX (נעילה משוחררת בכל close של הקובץ על־ידי התהליך הבעלים), יש להשתמש ב־fcntl עם F_SETLK; אחרת flock פשוט יותר.

הבדלים מ־upstream#

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

ראו גם#

  • Fcntl - המודול שמייצא את הקבועים F_* / O_* / FD_* / SEEK_* ש־fcntl מצפה להם; יש לבצע use עליו תמיד קודם

  • ioctl - אותה מוסכמת קריאה וערך מוחזר כמו fcntl, אך עבור קודי בקרת התקן ולא דגלי בקרת קובץ

  • flock - נעילות מייעצות פשוטות יותר לקובץ כולו; יש להושיט יד אליו כאשר אין צורך בטווח־בתים או בסמנטיקת POSIX

  • open - פותח את מטפל הקובץ מלכתחילה; חלק מהדגלים ש־fcntl מתאים (למשל O_NONBLOCK, O_APPEND) גם ניתנים לקביעה בזמן הפתיחה

  • sysopen - פתיחה ברמה נמוכה שלוקחת את אותם דגלי O_* מ־Fcntl; יש להשתמש בו כאשר רוצים את הדגלים מוחלים אטומית ולא דרך fcntl(F_SETFL) עוקב

  • pack / unpack - נדרשים לבניית ופענוח של בופר ה־struct flock שבו משתמשים F_GETLK / F_SETLK