ioctl#
לבצע קריאת מערכת ioctl(2) של בקרת התקן על מטפל קובץ.
ioctl הוא הקישור הישיר של Perl ל־syscall של POSIX ioctl(2) - ממשק הקרנל הכוללני לפעולות ספציפיות־להתקן שאינן מתאימות למודל ה־read/write הרגיל: גודל חלון טרמינל, פרמטרים של קו טורי, שאילתות התקן סוקט, מיקום סרט, וכל מה שדרייבר בוחר לחשוף דרך קוד בקשה. הוא מבצע דיספאץ« של FUNCTION מול FILEHANDLE, ומעביר את SCALAR כארגומנט השלישי - או מספר שלם קטן או מצביע למאגר בתים, תלוי במה ש־FUNCTION הספציפי מצפה לו.
שלא כמו fcntl, שקודי הבקשה שלו חיים במודול Fcntl, קודי הבקשה של ioctl כמעט אף פעם אינם מיוצאים כקבועים מוכנים מראש. המסלול הרגיל הוא:
require "sys/ioctl.ph";
שמושך את ה־header המתורגם <sys/ioctl.h>. אם הקובץ הזה אינו קיים, או אינו מגדיר את הקוד שדרוש, יש לבנות אותו מ־headers של C עם הכלי h2ph שמשולח עם Perl, או להגדיר את הקבוע בעצמך מ־header של הקרנל.
תקציר#
require "sys/ioctl.ph";
ioctl FILEHANDLE, FUNCTION, SCALAR
ioctl($fh, $request, $buf)
my $rv = ioctl($fh, $request, $arg) || -1;
מה מקבלים בחזרה#
בהצלחה, המספר השלם שהקרנל החזיר. כאשר המספר השלם הזה הוא 0, Perl מחליף את המחרוזת דו־ערכית "0 but true" - אמת בהקשר בוליאני, 0 מספרי בהקשר מספרי, ופטור מהאזהרה Argument "..." isn't numeric. זה אומר שבדיקה בוליאנית פשוטה מבחינה באופן אמין בין הצלחה לכישלון אפילו עבור בקשות שערך ההצלחה שלהן הוא ממש אפס:
ioctl($fh, $request, $buf) or die "ioctl: $!";
בכישלון, ioctl מחזיר undef ומגדיר את $! ל־errno הרלוונטי. המיפוי המלא:
מערכת ההפעלה מחזירה | Perl מחזיר |
|---|---|
| |
| מחרוזת |
כל ערך אחר | המספר השלם הזה |
כאשר נדרשת החזרה גולמית של הקרנל - עבור קודי בקשה שמקודדים מידע בערך ההחזרה עצמו - יש להשתמש במבנה מ־perlfunc:
my $retval = ioctl($fh, $request, $arg) || -1;
printf "System returned %d\n", $retval;
איך SCALAR משמש#
SCALAR מועבר לקרנל כארגומנט השלישי של הקריאה ioctl של C, ותפקידו תלוי בקוד הבקשה:
מצביע למאגר. המקרה הרגיל. רוב בקשות ה־
ioctlקוראות מתוך או כותבות אל struct של C שהפריסה שלו מוגדרת על־ידי הדרייבר. יש לבנות את המאגר עםpackלפני הקריאה, ועבור בקשות שכותבות חזרה, לפענח עםunpackלאחר מכן. יש לקבוע מראש את גודל הסקלר לפחות לאורך ה־struct - Perl יגדיל סקלר קטן מדי אוטומטית, אך קביעת גודל מראש מבהירה את הכוונה ומונעת הפתעות אם הדרייבר כותב יותר ממה שציפית.מספר שלם קטן. מיעוט הבקשות לוקח מספר שלם חשוף במקום מצביע. אם ל־
SCALARאין ערך מחרוזת אך יש לו ערך מספרי, Perl מעביר את המספר במקום מצביע. כדי להבטיח דיספאץ« זה - אפילו עבור סקלר שאולי הומר למחרוזת קודם לכן - יש להוסיף לו0קודם:my $n = 0 + $n; # force numeric representation ioctl($fh, $request, $n);
מוזנח. עבור קודי בקשה שאינם לוקחים ארגומנט שלישי, יש להעביר
0כ־placeholder. הקרנל מתעלם ממנו; המוסכמה שומרת על הקריאה קריאה.
מצב גלובלי שהוא נוגע בו#
$!- מוגדר בכישלון ל־errnoשמוחזר על־ידיioctl(2).מצב התקן או descriptor ב־
FILEHANDLE-ioctlבדרך כלל משנה מצב קרנל המשויך ל־file descriptor הבסיסי או להתקן שלו (תכונות טרמינל, אפשרויות סוקט, מיקום סרט). מצב זה אינו נראה ל־Perl דרך משתנה אחר כלשהו; יש לקרוא אותו חזרה עם קריאתioctlנוספת באמצעות בקשתTIOCG.../SIOCG...המתאימה.
דוגמאות#
לתשאל את גודל חלון הטרמינל ב־Linux. TIOCGWINSZ כותב struct winsize (שני unsigned short rows/cols בתוספת שני שדות שמתעלמים מהם) לתוך המאגר:
require "sys/ioctl.ph";
my $winsize = "\0" x 8;
ioctl(STDOUT, TIOCGWINSZ(), $winsize)
or die "TIOCGWINSZ: $!";
my ($rows, $cols) = unpack('S S', $winsize);
print "terminal is $rows rows by $cols cols\n";
לספור בתים זמינים לקריאה על סוקט מבלי לצרוך אותם (FIONREAD נתמך באופן רחב על פני Unixes):
require "sys/ioctl.ph";
my $pending = pack('L', 0);
ioctl($sock, FIONREAD(), $pending)
or die "FIONREAD: $!";
my $n = unpack('L', $pending);
print "$n bytes pending\n";
להכניס סוקט למצב non-blocking דרך בקשת FIONBIO - חלופה לגישה של fcntl / F_SETFL, והצורה ניידת היסטורית לחלק ממערכות ישנות יותר:
require "sys/ioctl.ph";
my $on = pack('L', 1);
ioctl($sock, FIONBIO(), $on)
or die "FIONBIO: $!";
הבחנה בין ״syscall החזיר 0״ ל־״syscall נכשל״ באמצעות המוסכמה "0 but true":
my $rv = ioctl($fh, $request, $buf);
if (!defined $rv) {
die "ioctl failed: $!";
} elsif ($rv eq "0 but true") {
# kernel returned 0 - success with a zero value
} else {
# kernel returned $rv
}
ייצור קוד בקשה משלך כאשר sys/ioctl.ph חסר או לא־שלם. הקידוד (_IOR / _IOW / _IOWR) ספציפי לקרנל; ב־Linux המאקרו מתרחב לערך בן 32 ביט שניתן לחשב מראש ולקבוע בקוד:
use constant TIOCGWINSZ => 0x5413; # Linux <asm-generic/ioctls.h>
my $winsize = "\0" x 8;
ioctl(STDOUT, TIOCGWINSZ, $winsize) or die $!;
מקרי קצה#
שכחת ה־
.phrequire: קבועים כמוTIOCGWINSZמנותחים כ־barewords ללא ערך מוגדר, מפעיליםEINVALמהקרנל. יש אוrequire "sys/ioctl.ph"או להגדיר את הקבוע בעצמך עםuse constant.החזרת
"0 but true": אין להשוות את ההחזרה ל־== 0כבדיקת כישלון. יש להשתמש בבוליאני פשוט (or die) אוdefined. השוואה לשוויון מול המחרוזת המילולית גם עובדת ($rv eq "0 but true") אך לעיתים רחוקות נדרשת.הפתעת דיספאץ« סקלר/מספר שלם: סקלר שהיה מספרי אך הומר למחרוזת - לדוגמה דרך אינטרפולציה להודעה - מתהפך מסמנטיקת ארגומנט־מספר שלם לסמנטיקת מצביע־למאגר בקריאה הבאה. יש לאלץ ייצוג מספרי עם
0 + $xכאשר קוד הבקשה מצפה למספר שלם.פריסת struct ספציפית לפלטפורמה.
struct winsize,struct termios,struct ifreq, וחבריהם שונים בין קרנלים ואפילו בין ארכיטקטורות באותו קרנל. אין לקבוע תבניותpackבקוד מהזיכרון; יש לאמת מול ה־header של מערכת היעד.מאגר פלט קטן מדי. אם הדרייבר כותב יותר בתים ממה ש־
SCALARמחזיק כעת, הסקלר מוגדל, אך אין רמז ב־Perl שזה קרה. יש לקבוע גודל מראש לפחות לאורך ה־struct עם"\0" x Nכך שקריאות מעבר לסוף המאגר יהיו באגי דרייבר, לא באגי Perl.לא כל מערכת מממשת כל בקשה.
ioctlעצמו הוא POSIX, אך קודי בקשה ספציפיים מוגדרים על־ידי דרייבר. בקשה לא־נתמכת נכשלת עםENOTTYאוEINVAL; קריאתioctlעל מערכת ללאioctl(2)בכלל מעלה חריגה.ioctlמולfcntl: עיבוד ארגומנטים ומוסכמת ערך החזרה זהים, ממשק קרנל שונה. יש להשתמש ב־fcntlלדגלי descriptor ולנעילות POSIX; יש להשתמש ב־ioctlלקודי בקרת התקן.
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.
ראו גם#
fcntl- אותה מוסכמת קריאה וערך החזרה; משמש לדגלים ברמת descriptor ולנעילות מייעצות במקום לקודי בקרה ספציפיים להתקןpack/unpack- נדרשים לבניית ופענוח מאגרי ה־struct שרוב בקשות ה־ioctlמחליפות עם הקרנלopen/sysopen- לפתוח את ההתקן או הקובץ המיוחד שעם הדרייבר שלו בקשת ה־ioctlתדברbinmode- חלק מבקשותioctl(לדוגמה שינויי מצב טרמינל) מקיימות אינטראקציה עם buffering של PerlIO; יש לעבור למצב raw כאשר הדרייבר ו־PerlIO חולקים על משמעת השורהselect/sysread- מסלול ה־I/O הרגיל; יש לפנות ל־ioctlרק כאשר הפעולה הדרושה אינה ניתנת להבעה כ־read או write