תהליכים

pipe#

פותח זוג מטפלי קובץ מחוברים - אחד לקריאה, אחד לכתיבה.

pipe עוטף את קריאת המערכת pipe(2). הוא יוצר ערוץ בתים חד־כיווני חסר שם בקרנל ומוסר לכם שני מטפלי קובץ: READHANDLE, שממנו ניתן לקרוא בתים, ו־WRITEHANDLE, שלתוכו ניתן לכתוב בתים. כל מה שנכתב ל־WRITEHANDLE הופך לזמין ב־READHANDLE באותו סדר. השימוש הקלאסי הוא תקשורת בין תהליך הורה לילד שהופק על ידי fork: צד אחד כותב, השני קורא.

תקציר#

pipe READHANDLE, WRITEHANDLE

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

אמת בהצלחה, שקר בכישלון (כאשר $! מוגדר לסיבה - בדרך כלל EMFILE או ENFILE כאשר טבלת הקבצים של התהליך או המערכת מלאה). שני המטפלים נוצרים כתופעות לוואי על הארגומנטים; אין ערך החזרה של ״עצם pipe״.

בהצלחה אתם מחזיקים כעת שני descriptorים של קובץ פתוחים. כל descriptor שאתם יוצרים הוא descriptor שעליכם close - במיוחד את הקצה שכל תהליך אינו משתמש בו לאחר fork (ראו מקרי קצה).

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

  • $! - מוגדר בכישלון ל־errno מקריאת pipe(2) הבסיסית.

  • $| - autoflush על המטפל בצד הכתיבה. print עושה buffering דרך PerlIO, אז ללא autoflush הקורא עשוי לא לראות דבר עד שהכותב יוצא או ה־buffer מתמלא. הגדירו את $| על מטפל הכתיבה (בדרך כלל דרך select) לפני הכתיבה, או שהפרוטוקול שלכם יקרוס ב־deadlock.

  • $^F - מטפל הקובץ המקסימלי של המערכת. descriptorים חדשים עם fileno גדול מ־$^F (ברירת מחדל 2, כלומר מעל STDERR) מקבלים את דגל close-on-exec באופן אוטומטי, כך שהם אינם דולפים לתוך תוכניות שהופעלו על ידי exec. הורידו את $^F כדי לשמור את ה־pipe פתוח לאורך exec; העלו אותו, או בצעו dup על fd עם מספר נמוך, כדי לאלץ ירושה.

דוגמאות#

הורה כותב שורה; ילד מ־fork קורא אותה:

pipe my $reader, my $writer or die "pipe: $!";

my $pid = fork // die "fork: $!";
if ($pid == 0) {
    # child: reads only
    close $writer;
    my $line = <$reader>;
    print "child saw: $line";
    close $reader;
    exit 0;
}

# parent: writes only
close $reader;
$writer->autoflush(1);
print $writer "hello from parent\n";
close $writer;
waitpid $pid, 0;

הילד מדווח בכיוון השני - ילד מפיק נתונים, ההורה צורך אותם:

pipe my $reader, my $writer or die "pipe: $!";

my $pid = fork // die "fork: $!";
if ($pid == 0) {
    close $reader;
    $writer->autoflush(1);
    print $writer "$_\n" for 1 .. 5;
    close $writer;
    exit 0;
}

close $writer;
while (my $line = <$reader>) {
    print "got: $line";
}
close $reader;
waitpid $pid, 0;

Autoflush מצד מטפל־נבחר, עבור קוד שקדם לקריאות מתודה של IO::Handle:

pipe my $reader, my $writer or die "pipe: $!";
my $old = select $writer; $| = 1; select $old;

לרוב השימושים, open עם מצב pipe פשוט יותר - הוא עושה fork, pipe, ו־exec בשבילכם:

open my $fh, '|-', 'gzip', '-c' or die $!;   # write to gzip's stdin
print $fh "data\n";
close $fh;

open my $fh, '-|', 'ls', '-l'   or die $!;   # read from ls's stdout
while (my $line = <$fh>) { print $line }
close $fh;

פנו ל־pipe חשוף כאשר אתם זקוקים לשני הכיוונים, צריכים לשמר את הילד שלא בוצע לו exec, או בונים משהו ששתי צורות ה־pipe של open אינן מכסות - אז IPC::Open2 / IPC::Open3 עוטפים בשבילכם את הריקוד pipe + fork + exec.

מקרי קצה#

  • סגרו את הקצה שאינכם משתמשים בו. לאחר fork, שני התהליכים מחזיקים את שני ה־descriptorים. הקורא חייב לסגור את קצה הכתיבה, והכותב חייב לסגור את קצה הקריאה. דלגו על זה ותקבלו את באג ה־pipe הקלאסי: ה־<$reader> של הקורא לעולם אינו מחזיר EOF, מכיוון שהקרנל עדיין רואה כותב פתוח (עותק $writer סרק של הקורא עצמו). התוכנית נתקעת.

  • כתיבה ל־pipe ללא קורא מעלה SIGPIPE. כשכל עותק של קצה הקריאה נסגר, ה־print הבא בצד הכתיבה מספק SIGPIPE, שפעולת ברירת המחדל שלו היא להרוג את התהליך. התקינו $SIG{PIPE} = 'IGNORE' (או מטפל) כדי להפוך אותו לכישלון כתיבה רגיל כאשר $! מוגדר ל־EPIPE, שאז תוכלו לבדוק.

  • Deadlock בלולאות pipe. שני תהליכים שכל אחד מהם כותב לשני דרך pipeים יחסמו לנצח אם שני buffer הכתיבה מתמלאים לפני שאף תהליך קורא. buffer pipe של הקרנל קטנים (בדרך כלל 64 KiB). נקזו צד אחד, השתמשו בקלט/פלט לא־חוסם, או השתמשו ב־select / IO::Select למיקסור.

  • Buffering מסתיר כתיבות. ללא $| (או $writer->autoflush(1)) בצד הכתיבה, נתונים יושבים ב־buffer של PerlIO עד שה־buffer מתמלא או שהמטפל נסגר. קורא שחסום על <$reader> יחכה - לעתים קרובות לזמן בלתי מוגבל - לנתונים שכבר ״נכתבו״ מנקודת המבט של הכותב.

  • חד־כיווני בלבד. pipe מפיק ערוץ חד־כיווני: כתבו ל־WRITEHANDLE, קראו מ־READHANDLE, לא להפך. לתקשורת דו־כיוונית השתמשו ב־socketpair או בשני pipeים.

  • נתונים בינאריים. המטפלים שמחזיר pipe מתחילים במחסנית שכבת PerlIO של ברירת המחדל. אם אתם מעבירים בתים גולמיים דרך ה־pipe, החילו binmode על שני הקצוות; אחרת שכבות CRLF או קידוד שנירשו מברירות המחדל של התהליך יכולות לכתוב מחדש את הנתונים שלכם.

  • Close-on-exec ו־fileno. על מערכות עם FD_CLOEXEC, כל descriptor חדש שה־fileno שלו עולה על $^F נוצר עם דגל close-on-exec מוגדר. descriptorים של pipe נופלים בדרך כלל הרבה מעל 2, אז כברירת מחדל הם אינם שורדים exec - מה שזה בדרך כלל מה שאתם רוצים. הורידו את $^F לפני קריאה ל־pipe אם בכוונתכם להעביר את ה־descriptorים לתוכנית שעברה exec.

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

תאימות מלאה עם Perl 5.42 המקורי.

ראו גם#

  • fork - המחצית השנייה של ניב ה־pipe; pipe נקרא כמעט תמיד ממש לפני fork

  • open - המצבים '|-' ו־'-|' עושים fork, pipe, ו־exec לילד בקריאה אחת; העדיפו אותם כשאתם זקוקים בדיוק לכיוון אחד לפקודה חיצונית אחת

  • close - סגירת הקצה שאינו בשימוש בכל תהליך היא חובה, לא אופציונלית

  • read - קריאות מוכוונות־בתים מ־READHANDLE כשצורת <$fh> המוכוונת־שורות היא הצורה הלא נכונה

  • print - הדרך הרגילה לדחוף נתונים לתוך WRITEHANDLE; זכרו את $|

  • fileno - שחזרו את ה־descriptor הבסיסי של הקובץ, לדוגמה כדי למסור ל־select / IO::Select או לקריאת מערכת ברמה נמוכה יותר