我在 Linux 上使用 64 位 8.4.3 Tcl 非线程,我遇到了一个奇怪的行为。
我的 C++ 应用程序有一个计时器,它使用 XtAppProcessEvent
执行一些 Xt 处理程序。其中一个处理程序调用 Tcl_DoOneEvent
。
我有一个 Tcl 脚本,它打开一个空管道并将一个文件事件附加到打开的 channel 。
set jobId [open "| "]
fileevent $jobId readable
这会完成多次。一段时间后,当 channel 名称为 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 上试过了,没有任何区别。
最佳答案
所以,我想通了这个问题。
这个问题基本上是由于转移了一个 integer
而不是 long
.这在 Tcl 中是在 tclUnixChan.c
中完成的在计算需要在 tsdPtr->checkMasks
中修改的索引和位时:
index = fd/(NBBY*sizeof(fd_mask));
bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
位线需要为1L <<
而不是 1 <<
. fd_mask
本身就是一个 long
并在 sys/types.h
中定义.这只是问题的一部分。
sys/types.h
也有在设置或清除 fd_masks 中的位时应使用的宏定义(FD_CLR
、FD_SET
、FD_ISSET
、FD_ZERO
)。
此问题已在版本 8.4.9
中部分修复并在版本 8.4.20
中得到完全修复.
修复是通过使用 types.h
中定义的宏完成的而不是手动操作 fd_mask
位。
版本 8.4.9
中引入的部分修复在tclUnixNotfy.c
:
- 结构
SelectMasks
创建它使用fd_set
types.h
中定义的类型 - 线程特定数据
checkMasks
和readyMasks
字段从fd_mask
更改为数组为SelectMasks
-
index
和bit
Tcl_CreateFileHandler
中使用的变量,Tcl_DeleteFileHandler
,Tcl_WaitForEvent
,NotifierThreadProc
处理面具被移除和FD_CLR
,FD_SET
,FD_ISSET
,FD_ZERO
被改用了。 -
Tcl_WaitForEvent
分配readyMasks
至checkMasks
而不是使用memcpy
打电话前select
- 类型转换
readyMasks
至SELECT_MASK *
现在调用 select 时被删除并且&
运算符现在用于SelectMasks
fd_set
类型的结构元素直接。
修复的另一部分在版本 8.4.20
中引入在tclUnixChan.c
:
-
index
和bit
TclUnixWaitForFile
中使用的变量处理面具被移除和FD_CLR
,FD_SET
,FD_ISSET
,FD_ZERO
被改用了。 -
fd_set
使用类型而不是创建fd_mask
TclUnixWaitForFile
中的数组. - 为
SELECT_MASK *
蒙上面具现在调用 select 时被删除并且&
运算符现在用于创建的fd_set
直接变量。
我已经修补了当前版本的 8.4.3
以 8.4.9
中所做的更改为指导和 8.4.20
因为我没有升级整个 Tcl
的灵 active 版本。
我关于为什么打开一个新的 shell 窗口使该工具起作用的理论:
由于在错误大小的容器中移动并使用 memcpy
导致内存损坏奇怪的长度3*MASK_SIZE
, fd
我最后指出是窗口管理相关的,因此为什么打开一个新的 shell 得到选择返回非零值,因为这些错误的数据 fd
我指向的 channel 。
让我得出这个理论的是 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
输出显示 channel 的以下内容 4
和 6
:
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)
channel 4
和 6
是将我的机器连接到 NX 服务器的 channel 。
引用资料:
关于c++ - Tcl fileevent 在 64 位版本的 Tcl 上挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56416818/