【问题标题】:Tcl fileevent hangs on 64 bit version of TclTcl fileevent 在 64 位版本的 Tcl 上挂起
【发布时间】:2019-06-02 16:23:11
【问题描述】:

我在 Linux 上使用 64 位 8.4.3 Tcl Non-Threaded,我遇到了一个奇怪的行为。

我的 C++ 应用程序有一个计时器,它使用 XtAppProcessEvent 执行一些 Xt 处理程序。其中一个处理程序调用Tcl_DoOneEvent

我有一个 Tcl 脚本,它打开一个空管道并将一个文件事件附加到打开的通道。

set jobId [open "| "]
fileevent $jobId readable

这需要多次完成。在频道名称为file35 的一段时间后,该工具会挂起。使用 Tcl 库的调试版本。显示readyMasks[0]在执行完以下部分后始终为0:

// tclUnixNotfy.c:772
numFound = select(tsdPtr->numFdBits,
    (SELECT_MASK *) &tsdPtr->readyMasks[0],
    (SELECT_MASK *) &tsdPtr->readyMasks[MASK_SIZE],
    (SELECT_MASK *) &tsdPtr->readyMasks[2*MASK_SIZE], timeoutPtr);

输入检查掩码为 72 (1001000)。


这是奇怪的部分:

当我到达工具挂起的部分时,如果我打开一个新的 shell 选项卡,该工具将不再挂起并按预期继续执行。当打开一个新的 shell 选项卡时,readyMasks 变为 72。

该工具在32位时运行正常。我无法将正在发生的事情与 64 位联系起来。

我在 redhat 5、6 和 7 上试过这个,没有任何区别。

【问题讨论】:

  • 你可以试试 8.6.9 吗? 8.6.3 现在是一个相当老的版本了。
  • 另外,打开一个空管子?! 这到底是怎么回事?我不知道它曾经有效……但它不应该有效。当我尝试时,我得到一个“illegal use of | or |& in command”。
  • @DonalFellows 我尝试升级到8.6.6,但一切都失败了。应用程序很大,这样的升级并不是那么简单。它确实有效我 100% 我的应用程序经常使用它。至于使用它的原因,它是用来安排在下一个Tcl_DoOneEvent上处理一个事件。这样会执行一些Xt 事件以保持 UI 响应。
  • 我知道最近对 8.6.x 中频道的更改,其中使用了 poll 而不是 select。虽然我的工具严重依赖于 Tcl,但我无法进行如此大规模的升级。
  • OP 说的是 8.4.3,而不是 8.6.3(但这似乎已经改变)?

标签: c++ linux tcl 64-bit x11


【解决方案1】:

所以,我发现了问题。

这个问题基本上是由于转移到integer 而不是long。这在 Tcl 中是在 tclUnixChan.c 中完成的,同时计算需要在 tsdPtr->checkMasks 中修改的索引和位:

index = fd/(NBBY*sizeof(fd_mask)); 
bit = 1 << (fd%(NBBY*sizeof(fd_mask)));

位线需要是1L &lt;&lt; 而不是1 &lt;&lt;fd_mask 本身是一个long 并在sys/types.h 中定义。这只是问题的一部分。

sys/types.h 还包含在设置或清除 fd_masks 中的位时应使用的宏定义(FD_CLRFD_SETFD_ISSETFD_ZERO)。

此问题已在版本 8.4.9 中部分修复,并在版本 8.4.20 中完全修复。

修复是通过使用types.h 中定义的宏而不是手动操作fd_mask 位来完成的。

8.4.9 版本中引入的部分修复在tclUnixNotfy.c 中:

  1. struct SelectMasks 已创建,它使用在 types.h 中定义的 fd_set 类型
  2. ThreadSpecificData checkMasksreadyMasks 字段从 fd_mask 数组更改为 SelectMasks
  3. Tcl_CreateFileHandlerTcl_DeleteFileHandlerTcl_WaitForEventNotifierThreadProc 中使用的用于处理掩码的indexbit 变量被删除,FD_CLRFD_SETFD_ISSET、@98765432 被使用而是。
  4. Tcl_WaitForEventreadyMasks 分配给checkMasks,而不是在调用select 之前使用memcpy
  5. 现在删除了在调用 select 时将 readyMasks 转换为 SELECT_MASK * 并且 &amp; 运算符现在直接用于 fd_set 类型的 SelectMasks 结构元素。

版本8.4.20 中引入的修复的另一部分是tclUnixChan.c

  1. indexbitTclUnixWaitForFile 中用于处理掩码的变量已被删除,而使用 FD_CLRFD_SETFD_ISSETFD_ZERO 代替。
  2. 使用fd_set 类型而不是在TclUnixWaitForFile 中创建fd_mask 数组。
  3. 现在删除了在调用 select 时将掩码转换为 SELECT_MASK *,并且现在可以直接对创建的 fd_set 变量使用 &amp; 运算符。

根据8.4.98.4.20 中所做的更改,我已经修补了我当前版本的8.4.3,因为我没有升级整个Tcl 版本的灵活性。


我关于为什么打开一个新的 shell 窗口可以使该工具工作的理论:

由于在错误大小的容器中移动并使用具有奇怪长度3*MASK_SIZEmemcpy 导致内存损坏,我最终指向的fd 与窗口管理相关,因此为什么要打开一个新外壳由于我指向的那些错误的fd 频道上有数据,因此选择返回非零值。

使我得出这个理论的是strace 输出以及lsof 输出:

strace 挂起时的输出重复了以下部分:

poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 2, 0) = 0 (Timeout)
recvfrom(4, 0x80e21f4, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
recvfrom(4, 0x80e21f4, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 2, 0) = 0 (Timeout)
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
select(36, [3 6], [], [], {0, 0})       = 0 (Timeout)
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
select(36, [3 6], [], [], {0, 0})       = 0 (Timeout)
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 2, 0) = 0 (Timeout)
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {0x4c3cd70, [CHLD], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x37eec0f790}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0

strace 打开新的 shell 选项卡时的输出更改为:

poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 2, 0) = 1 ([{fd=4, revents=POLLIN}])
recvfrom(4, "X\1\366\371\264\300\7=\3\0\22\0\10\377\0\0\26\1\26\1\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096, 0, NULL, NULL) = 256
recvfrom(4, 0x80e21f4, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
recvfrom(4, 0x80e21f4, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 2, 0) = 0 (Timeout)
recvfrom(6, "X\1\321/\264\300\7=\3\0\22\0\10\377\0\0\26\1\26\1\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096, 0, NULL, NULL) = 256
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
select(36, [3 6], [], [], {0, 0})       = 1 (in [3], left {0, 0})
recvfrom(6, 0x8380d64, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
read(35, "", 4096)                      = 0
close(35)                               = 0

lsof 输出显示频道46 的以下内容:

myexec 13626 aymansalah    4u  IPv4 1607796326       0t0        TCP rhe6x64:38787->nx-svr:7016 (ESTABLISHED)
myexec 13626 aymansalah    6u  IPv4 1607837231       0t0        TCP rhe6x64:38788->nx-svr:7016 (ESTABLISHED)

频道46 是将我的机器连接到NX 服务器的频道。


参考资料:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-24
    • 1970-01-01
    • 2019-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多