我在BitBash模式下使用FTDI D2xx驅動程序在Windows上運行tcl8.5/tk8.5應用程序(問題發生在XP,7和8)。還使用ftd2xx c擴展來訪問FTDI dll。在非阻塞的情況下在tcl讀取處理程序中處理非持久性eof
我有一個來自用戶的報告,最初的應用程序工作正常,但經過一天的不使用,居住在後臺,它突然從5M增長到100M,並開始消耗99%的CPU。 (這是不好的!)
在此之前,我曾遇到USB問題,特別是如果USB「拔下電源」。它可能會導致應用程序阻止,而Windows無法將其殺死。我的應用程序永遠不需要讀取USB,它只是寫入(控制它),但我發現在BitBash模式下,FTDI芯片會發送連續的數據流。處理讀操作會清除讀緩衝區,所以如果USB被拔出,沒有待處理的讀取被阻塞,我可以正常退出。
但現在,我認爲閱讀處理程序給我的問題。我寫了一小段測試代碼,它模仿了我認爲可以解決問題根源的實際應用程序。無論如何,我不明白tcl的行爲。
下面是代碼:
package require -exact ftd2xx 1.2.1
# define a read handler
# gets called by a fileevent readable
#
proc readchan {} {
variable cnt
set len [gets $::handle buf]
# buffer is non zero
if {$len > 0} {
incr cnt
set end [eof $::handle]
puts "$len ($cnt) eof $end";
# if it wasn't an eof output the buffer
if {!$end} {
puts $buf
}
} elseif {$len == 0} {
# was a zero length read
puts -nonewline "0"
set end [eof $::handle]
if { !$end } {
# eof makes the $len invalid?
puts "-0"
#puts "\nlen is 0 and eof is $end - exit!";
#exit
}
} else {
# len was negative (-1) so data in buffer but no end of line (in binary mode)
if { [eof $::handle] } {
puts "EOF w/len 0"
} else {
puts -nonewline "."
}
}
}
# main code
#
# find the usb device and open it
set usb [ftd2xx list]
lassign $usb d
lassign $d d id e loc f serial
puts "found USB $serial"
set handle [ftd2xx open -serial $serial]
puts "Opened USB $handle"
#
# configure it for bitbash mode and for binary, non blocking
#
set bitmode 0xFF01
chan configure $handle -tranlation binary -bitmode $bitmode -blocking 0
fileevent $handle readable readchan
# output something to the usb every 100ms or so required to cause failure
#
while {1} {
set continue 0
after 100 {set continue 1}
# putting:
# "a" made it crash after 245 "timeouts" - no flush
# null made it past 255 - no flush
# "aa" made it crash after 164 "timeouts - no flush
puts $handle "aa"
#flushing $handle makes it crash after first flush
#flush $handle
puts -nonewline "w"
vwait continue
}
這裏是從上面的代碼的輸出:
$ tclsh85 usbfailtest.tcl
found USB AH009L40
Opened USB ftd2xx0
w...w....w....w....w....w....w...w....w.126976 (1) eof 1 <<< 126K length with eof true
..w....w....w....w...w....w....w....w..126976 (2) eof 1 <<< "w" output for usb write
.w....w....w...w....w....w....w....w...126976 (3) eof 1 <<< this is 3rd non-zero read
w...w....w....w....w....w...w....w....w.126976 (4) eof 1 <<< "." output when len -1
..w....w...w....w....w....w....w...w...126976 (5) eof 1 <<< line takes about 1 sec
w....w....w....w....w...w....w....w....w126976 (6) eof 1
.
. (output skipped)
.
.w....w....w....w...w....w....w....w...126976 (156) eof 1
w....w....w...w....w....w....w....w....w126976 (157) eof 1
..w....w....w....w....w....w...w....w..126976 (158) eof 1
.w....w....w...w....w....w....w....w...126976 (159) eof 1
w....w...w....w....w....w....w....w...w.126976 (160) eof 1
..w....w....w....w....w...w....w....w..126976 (161) eof 1
.w....w....w....w...w....w....w....w...126976 (162) eof 1
w....w...w....w....w....w....w....w...w.126976 (163) eof 1
..w....w....w....w....w...w....w102695 (164) eof 0 << a 102K buffer with EOF false
aa << all but two of the 102K buffer were null?
0-0 << zero length buffer with no eof
2 (165) eof 0
aa << two length buffer, no eof, and we got two chars
3 (166) eof 0
aaa << three length bufffer, no eof, and we got three chars
2 (167) eof 0
aa << etc.
0-0 << another zero zero these scroll out *very* fast.
0-0
0-0
0-0
2 (168) eof 0
aa << an so forth
0-0
0-0
.
. (output skipped)
.
43 (268) eof 0
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
..w...22192 (269) eof 1
w...w....w....w....w....w...w....w....w.126976 (270) eof 1 << revert to "slow" mode
..w....w....w....w...w....w....w....w..126976 (271) eof 1
.w....w....w...w....w....w....w....w...126976 (272) eof 1
w....w...w....w....w....w....w....w...w.126976 (273) eof 1
..w....w....w....w....w...w....w....w..126976 (274) eof 1
輸出的解釋:
首先,讀出處理程序是被調用多次(每個「。」)與一個不完整的緩衝區(長度-1)和沒有eof條件。最終,某些內容超時或者內部緩衝區限制被觸發,強制讀取完成。同時EOF提出。 「... 126987」的每一行需要大約一秒鐘的時間來編寫。
經過一定數量的EOF(即...... 126987的行 - 在本例中爲164 - 非常可重複),取決於寫入通道的數據量以及通道是否刷新,讀取完成後沒有EOF(第164行)。
到目前爲止,讀事件中斷率是可以接受的,但是,它會突然間升高並消耗大量處理它的週期。在較慢的機器上,沒有時間做任何有用的事情。
我有很多關於這個難題的問題。但開始於:
我不明白爲什麼讀取處理程序將被調用與0字節掛起和非eof條件。緩衝區中是否必須至少有一個字節是「可讀的」?
我不明白爲什麼EOF是暫時的。我期望在USB端口上的EOF意味着它被拔掉,但事實並非如此。
如果我在寫入USB之後刷新頻道,我所得到的全部長度都是0,即使殺掉應用程序並重新啓動它,這仍然存在。清除此(到慢速模式)的唯一方法是從USB拔下設備並重新開始(不沖洗)。我不知道該怎麼做。我必須刷新頻道才能寫入實際的FTDI芯片。
我應該使用一個固定長度的讀取而不是獲取這個?
如果在刷新頻道之後發生了一些奇怪的事情,或者在沒有刷新頻道的情況下將很多內容寫入頻道之後,我會認爲編寫任何內容都不起作用。如果寫入時沒有刷新,那麼在填充緩衝區之前數據可能永遠不會發送。 –
在我的實際應用當然,我做了一個刷新發布寫入。作爲實驗代碼的一部分,我會/不會沖洗寫入。我認爲奇怪的是,沖洗「渠道」(寫作)改變了人們認爲獨立閱讀的行爲。 「puts」也必須在二進制模式下具有-nonewline選項。 – user1967890
只要您不知道要接收的字節,它就很難。該通道處於二進制模式。在這種情況下,使用gets可能不太好。獲取無法檢測到行結束?改爲使用read。我不知道FTDI芯片 - 它是5V芯片嗎?一些232系列在5V芯片(RS232標準爲+ -12V)方面存在問題。可能會收到錯誤的數據。 – tue