Tcl fileevent 在 64 位版本的 Tcl 上挂起
Tcl fileevent hangs on 64 bit version of Tcl
我在 Linux 上使用 64 位 8.4.3 Tcl 非线程,我遇到了一个奇怪的行为。
我的 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 上试过了,没有任何区别。
所以,我想通了这个问题。
这个问题基本上是由于 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
:
- struct
SelectMasks
已创建,它使用 fd_set
在 types.h
中定义的类型
- ThreadSpecificData
checkMasks
和 readyMasks
字段从 fd_mask
数组更改为 SelectMasks
在 Tcl_CreateFileHandler
、Tcl_DeleteFileHandler
、Tcl_WaitForEvent
、NotifierThreadProc
中用于处理掩码的 index
和 bit
变量被删除并FD_CLR
、FD_SET
、FD_ISSET
、FD_ZERO
被代替。
Tcl_WaitForEvent
将 readyMasks
分配给 checkMasks
而不是在调用 select
之前使用 memcpy
- 现在删除了调用 select 时将
readyMasks
转换为 SELECT_MASK *
,&
运算符现在用于 SelectMasks
类型的结构元素 fd_set
直接。
版本 8.4.20
中引入的修复的另一部分在 tclUnixChan.c
:
index
和 bit
在 TclUnixWaitForFile
中用于处理掩码的变量已被删除,并且 FD_CLR
、FD_SET
、FD_ISSET
、 FD_ZERO
被改用了。
在 TclUnixWaitForFile
. 中使用 fd_set
类型而不是创建 fd_mask
数组
- 在调用 select 时将掩码转换为
SELECT_MASK *
现在已被删除,&
运算符现在直接用于创建的 fd_set
变量。
我已经根据 8.4.9
和 8.4.20
中所做的更改对当前版本的 8.4.3
进行了修补,因为我没有升级整个 Tcl
的灵活性]版本。
我关于为什么要打开一个新的 shell window 让工具起作用的理论:
由于在错误大小的容器中移动并使用具有奇怪长度 3*MASK_SIZE
的 memcpy
导致内存损坏,我最终指向的 fd
是 window 管理相关因此为什么打开一个新的 shell 得到 select 到 return 非零值,因为在我指向的那些错误的 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, "X6140=[=12=][=12=]7[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]"..., 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, "X1/40=[=12=][=12=]7[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]"..., 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
输出显示通道 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)
通道 4
和 6
是将我的机器连接到 NX 服务器的通道。
参考文献:
我在 Linux 上使用 64 位 8.4.3 Tcl 非线程,我遇到了一个奇怪的行为。
我的 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 上试过了,没有任何区别。
所以,我想通了这个问题。
这个问题基本上是由于 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
:
- struct
SelectMasks
已创建,它使用fd_set
在types.h
中定义的类型
- ThreadSpecificData
checkMasks
和readyMasks
字段从fd_mask
数组更改为SelectMasks
在 index
和bit
变量被删除并FD_CLR
、FD_SET
、FD_ISSET
、FD_ZERO
被代替。Tcl_WaitForEvent
将readyMasks
分配给checkMasks
而不是在调用select
之前使用 - 现在删除了调用 select 时将
readyMasks
转换为SELECT_MASK *
,&
运算符现在用于SelectMasks
类型的结构元素fd_set
直接。
Tcl_CreateFileHandler
、Tcl_DeleteFileHandler
、Tcl_WaitForEvent
、NotifierThreadProc
中用于处理掩码的 memcpy
版本 8.4.20
中引入的修复的另一部分在 tclUnixChan.c
:
index
和bit
在TclUnixWaitForFile
中用于处理掩码的变量已被删除,并且FD_CLR
、FD_SET
、FD_ISSET
、FD_ZERO
被改用了。
在 fd_set
类型而不是创建fd_mask
数组- 在调用 select 时将掩码转换为
SELECT_MASK *
现在已被删除,&
运算符现在直接用于创建的fd_set
变量。
TclUnixWaitForFile
. 中使用 我已经根据 8.4.9
和 8.4.20
中所做的更改对当前版本的 8.4.3
进行了修补,因为我没有升级整个 Tcl
的灵活性]版本。
我关于为什么要打开一个新的 shell window 让工具起作用的理论:
由于在错误大小的容器中移动并使用具有奇怪长度 3*MASK_SIZE
的 memcpy
导致内存损坏,我最终指向的 fd
是 window 管理相关因此为什么打开一个新的 shell 得到 select 到 return 非零值,因为在我指向的那些错误的 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, "X6140=[=12=][=12=]7[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]"..., 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, "X1/40=[=12=][=12=]7[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]"..., 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
输出显示通道 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)
通道 4
和 6
是将我的机器连接到 NX 服务器的通道。
参考文献: