2016-08-04 112 views
3

使用check_parent_select時,在閱讀器側關閉後,未填寫例外列表。選擇不適用於python管道?

但是使用check_parent_poll,在閱讀器側關閉後,它可以檢測到管道斷開。

有人知道根本原因嗎?

#!/usr/bin/python2.7 
import select 
import sys 
import os 

log=open("./test.log","w") 
(reader, writer) = os.pipe() 
def check_parent_select(fh): 
    (rlist, wlist, xlist) = select.select([], [], [fh], 1) 
    if fh in xlist: 
     print "parent exit" 
    else: 
     print "parent OK" 

def check_parent_poll(fh): 
    poller = select.poll() 
    EVENTS = select.POLLERR 
    poller.register(fh) 
    events = poller.poll() 
    for fd, flag in events: 
     if flag & select.POLLERR: 
      print "parent exit" 
     else: 
      print "parent OK" 

open_file = os.fdopen(writer, "w") 
check_parent_select(open_file) 
os.close(reader) 
check_parent_select(open_file) 

使用strace追蹤選擇功能,選擇無法檢測管道關閉。

管([4,5])= 0

選擇(6,[],[],[5],{1,0})= 0(超時)

寫(1, 「父行\ n」 個,10parent行

接近(4)= 0

選擇(6,[],[],[5],{1,0})= 0(超時)

回答

1

它稍微隱藏的,但是如果按照the documentation,它變得更清晰:select()檢查對於待處理的錯誤條件,即使文件描述符不可用,但僅在發生錯誤後纔出現的錯誤條件。

關閉讀取結束後,尚未對管道執行任何操作,導致出現錯誤情況。 writer仍然有效操作:例如,您可以關閉fd。因此管道尚未處於錯誤狀態。

關閉寫入程序端時,更容易發現問題:即使在關閉之後,管道緩衝區中仍然可以讀取尚未消耗的數據。在這種情況下,您希望read()返回0EOF,而不是-1的錯誤。儘管你真的不能寫入一個讀取端已經關閉的管道,另一方的行爲也是類似的。

行爲與socket.socketpair()(或實際套接字)相同:只要沒有做任何無效的事情,就沒有錯誤條件。

log=open("./test.log","w")  
(reader, writer) = socket.socketpair()  
def check_parent_select(fh):  
    (rlist, wlist, xlist) = select.select([], [], [fh], 1)  
    if fh in xlist:  
     print "parent exit"  
    else:  
     print "parent OK"  

def check_parent_poll(fh):  
    poller = select.poll()  
    EVENTS = select.POLLERR  
    poller.register(fh)  
    events = poller.poll()  
    for fd, flag in events:  
     if flag & select.POLLERR:  
      print "parent exit"  
     else:  
      print "parent OK"  

check_parent_select(writer)  
reader.close()  
check_parent_select(writer) 
0

緩解此意外結果的快速修復方法是在編寫器中偵聽讀取事件。更好的解決方法是使用pselect()並偵聽SIG_PIPE,但afaik python沒有pselect()

我稱之爲「意外」,因爲首先想到的是關閉讀取結束的信號將作爲作者的例外。從作者的角度來看,只要它還有什麼可寫的東西,它可能確實是一個特例。但是,從OS的角度來看,這只是一個簡單的close()文件描述符。

如果您閱讀了poll()系統調用的聯機幫助頁,您會發現通過標記讀取事件列表中的POLLHUP位可以發出關閉文件描述符的信號。select()具有相同的行爲,只是它沒有爲識別close()調用而設置的特定位。

#!/usr/bin/python2.7 
import select 
import sys 
import os 
import time 

log=open("./test.log","w") 
(reader, writer) = os.pipe() 
def check_parent_select(fh): 
    (rlist, wlist, xlist) = select.select([fh], [fh], [fh], 1) 
    print(rlist, wlist, xlist) 
    if fh in rlist: 
     print "oh i'm just writing. error"                                  
    if fh in xlist: 
     print "parent exit" 
    else: 
     print "parent OK" 

def check_parent_poll(fh): 
    poller = select.poll() 
    EVENTS = select.POLLERR 
    poller.register(fh) 
    events = poller.poll() 
    for fd, flag in events: 
     if flag & select.POLLERR: 
      print "parent exit" 
     else: 
      print "parent OK" 

#open_file = os.fdopen(writer, "w") 
check_parent_select(writer) 
os.close(reader) 
#time.sleep(3) 
check_parent_select(writer) 

因此當管道被關閉,你會得到作家讀事件:

python2 t1.py 
([], [5], []) 
parent OK 
([5], [5], []) 
oh i'm just writing. error 
parent OK 
+0

這是從經驗中也可以指向規範呢?因爲關閉另一端觸發讀事件似乎有點奇怪。 – dhke

+1

關閉管道的讀取結束不是錯誤,因此在「x」列表中將不會顯示等待的通知。例如,通過輪詢系統調用,管道末端的關閉通過返回讀取事件列表中的POLLHUP來發出信號。 – user237419

+0

但我想知道套接字行爲是否與此一致? – jaslip