簡體   English   中英

從BEGIN塊中調用Perl fork

[英]Calling a Perl fork from within a BEGIN block

在從BEGIN塊中調用Perl時,我無法理解Perl中的fork行為。 perlfork中 ,我讀到了這個

BEGIN塊

BEGIN塊中調用時, fork()仿真將無法完全正常工作。 分叉副本將運行BEGIN塊的內容,但不會在BEGIN塊之后繼續解析源流。 例如,請考慮以下代碼:

 BEGIN { fork and exit; # fork child and exit the parent print "inner\\n"; } print "outer\\n"; 

這將打印:

 inner 

而不是預期的:

 inner outer 

但是,正如我所讀到的,這僅適用於模擬fork平台。 既然我關心(並測試代碼)Linux,那應該不是問題,不是嗎?

實際上,如果我從該文檔中復制示例代碼

BEGIN {
    fork and exit;
    print "inner\n";
}
print "outer\n";

這就是我運行它時會發生的事情

jirka@debian:~/xpath$ perl /tmp/test.pl
jirka@debian:~/xpath$ inner
outer

這似乎是一致的。

但是,當我刪除exit我希望同時擁有父進程和子進程。 那不符合我的預期。

這是我的新代碼

BEGIN {
    fork;
    print "inner\n";
}
print "outer\n";

這是奔跑

jirka@debian:~/xpath$ perl /tmp/test.pl
inner
outer
jirka@debian:~/xpath$ inner

我期待兩個inner和兩個outer 第二個outer缺失。

我的問題是,是什么導致了這種奇怪的行為,甚至如何描述它。

在我看來,孩子不再打開源文件(或者它只在父母中緩沖?)

通過-e嘗試代碼成功。

我的第一個猜測是,在孩子完成運行之前父母退出,導致它死亡(SIGPIPE?),但等待孩子產生相同的輸出:

BEGIN {
   $pid = fork;
   print "inner\n";
}
print "outer\n";
waitpid $pid, 0 if $pid;

輸出:

inner
outer
inner

事實上,似乎無法實現。 問題的原因是父和子共享源文件的相同文件指針。 當從源文件中讀取時,它會為兩者提前文件指針。

例如,如果我阻止其中一個進程使用__DATA__進一步讀取文件,則另一個進程將繼續讀取__DATA__並在那里執行代碼。 如果我將以下內容添加到上述程序中:

__DATA__
...8KB of newlines...
die("boo!");

我明白了:

inner
outer
inner
boo! at a.pl line 90.

好吧,問題實際上似乎是孩子和父母踩在彼此的源文件描述符上。 Strace給出:

read(3, "BEGIN {\n        fork;\n\tprint \"in"..., 8192) = 67
_llseek(3, 46, [46], SEEK_SET)          = 0
_llseek(3, 0, [46], SEEK_CUR)           = 0
clone(Process 29716 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb75329a8) = 29716
[pid 29715] write(1, "inner\n", 6inner
)      = 6
[pid 29715] read(3, "    print \"outer\\n\";\n", 8192) = 21
[pid 29715] read(3, "", 8192)           = 0
[pid 29715] close(3)                    = 0
...
write(1, "inner\n", 6inner
)                  = 6
read(3, "", 8192)                       = 0
close(3)                                = 0

這似乎是由父子共享單個文件讀指針的事實引起的。 man fork

  • 子進程繼承父進程打開文件描述符的副本。 子節點中的每個文件描述符引用相同的打開文件描述(請參閱open(2))作為父節點中的相應文件描述符。 這意味着兩個描述符共享打開文件狀態標志, 當前文件偏移量 ,...

現在,這引出了一個問題:如何分離這些文件描述符的偏移量?

我想知道你在最后的提示后怎么會有inner印刷品?

如果您仔細閱讀文檔

從BEGIN塊中調用時,fork()仿真將無法完全正常工作。 分叉副本將運行BEGIN塊的內容,但不會在BEGIN塊之后繼續解析源流

它表示子進程將僅解析(並因此運行) BEGIN塊的其余部分。 所以孩子打印inner ,不再打印。

沒有exit調用,父進程繼續打印inner ,然后是outer ,所以你應該有

inner
inner
outer

我希望我有一個Unix盒可以試試這個,但是當我回到家的時候會這樣做

我的問題是,是什么導致了這種奇怪的行為,甚至如何描述它。

實際上,當檢查語法時(粗略地)執行BEGIN塊。 有許多階段,如BEGINUNITCHECKCHECKINITEND 所以當你在BEGIN執行fork()時,程序實際上還沒有運行。

在模擬fork()系統上,這是由內部Perl解釋器狀態引起的,因為程序是早期啟動狀態(您的代碼甚至沒有編譯!)。 因此,在仿真環境中,我認為Perl在編譯后會丟棄模擬的分支。

我想要修復你必須將你的代碼放入INIT塊。 有關這些階段的更多詳細信息,請閱讀perlmod手冊頁。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM