我在Windows上运行tcl8.5 / tk8.5应用程序(在XP,7和8上发生问题),在BitBash模式下使用FTDI D2xx驱动程序。还使用ftd2xx c扩展来访问FTDI dll。
我有一个用户的报告该应用程序最初工作正常,但在一天不使用后,生活在后台,它突然从5M增长到100M并开始消耗99%的CPU。 (这不好!)
在此之前我遇到过USB问题,特别是如果USB是“热插拔”的话。它可能导致应用程序阻止,Windows无法杀死它。我的应用程序永远不需要读取USB,只是写入它(控制它),但我发现在BitBash模式下,FTDI芯片会发送连续的数据流。处理读取会清除readbuffer,因此如果USB已拔下,则没有挂起的读取阻止,我可以正常退出。
但现在,我认为readhandler给了我一些问题。我编写了一小段测试代码,我模仿了我认为可以解决问题根源的实际应用程序。无论如何,我不理解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芯片。
我应该使用固定长度的读取而不是获取此读取吗?
答案 0 :(得分:1)
我使用read而不是gets来实现实际代码。使用我的客户作为测试平台,他报告问题已得到解决。从现在开始,我将使用read而不是获取二进制通道。
答案 1 :(得分:0)
我没有具体了解ftd2xx包,但我看到一些奇怪的东西。通常,当eof返回1时,您最好关闭处理程序中的通道。只要数据准备就绪,或者当通道在外部关闭时(可能是驱动程序或操作系统关闭它),就会调用fileevent处理程序,如果你不告诉TCL关闭处理程序中的通道,TCL会一直反复调用它永远(告诉你做某事)。 来自fileevent手册:
&#34;如果基础文件或设备上存在文件结尾或错误情况,则通道也被视为可读。脚本检查这些条件并对其进行适当处理非常重要;例如,如果没有对文件结尾进行特殊检查,则可能会发生无限循环,其中脚本不读取数据,返回并立即再次调用。&#34;