Perl脚本sysread()读取ADAM模块时无限阻塞的排查与解决
研华ADAM-6000/6200模块Perl通信脚本sysread阻塞问题排查求助
我用Perl编写了与研华ADAM-6000/6200系列以太网IO模块通信的脚本,通过Socket循环读取数据。此前运行正常,但现在脚本会卡在sysread()处无限阻塞;重启脚本后可短暂恢复,随后又会卡在相同位置。以下是相关代码、执行输出及strace日志,求排查与解决方法:
代码片段
socket($socket, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or die "Unable to Connect"; connect($socket, sockaddr_in('1000', inet_aton($ip_address)) .... print "\n\ndo_command() begin:\n"; print "Send: " . Data::Dumper::qquote($str) . "\n"; syswrite($socket, $str); usleep(600000); print "Wrote to socket\n"; print "Waiting on sysread to finish...\n"; my $bytes = sysread($socket, $data_line, 200); print "Data Read: " . Data::Dumper::qquote($data_line) . "\n"; print "Bytes Read: $bytes\n";
执行输出
do_command() begin: Send: "#01\r" Wrote to socket Waiting on sysread to finish...
strace日志
write(4, "#01\r", 4) = 4 clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=0, tv_nsec=600000000}, NULL) = 0 write(1, "Wrote to socket\n", 16) = 16 write(1, "Waiting on sysread to finish...\n", 32) = 32 read(4, ">+00.018+00.016+00.020+00.008-00"..., 200) = 58 write(1, "Data Read: \"\>+00.018+00.016+00.0"..., 73) = 73 write(1, "Bytes Read: 58\n", 15) = 15 write(1, "\n", 1) = 1 write(1, "Sleeping for .1 seconds\n", 24) = 24 clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=0, tv_nsec=100000000}, NULL) = 0 write(1, "Read Cycle\n", 11) = 11 write(1, "\n\ndo_command() begin:\n", 22) = 22 write(1, "Send: \"#01\\r\"\n", 14) = 14 write(4, "#01\r", 4) = 4 clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=0, tv_nsec=600000000}, NULL) = 0 write(1, "Wrote to socket\n", 16) = 16 write(1, "Waiting on sysread to finish...\n", 32) = 32 read(4,
排查与解决建议
- 替换固定延时为Socket超时设置:不要依赖
usleep的固定等待,给Socket设置读写超时。可以改用IO::Socket::INET创建连接(支持Timeout参数),或者用setsockopt设置SO_RCVTIMEO和SO_SNDTIMEO,避免sysread无限阻塞。 - 检查模块连接状态:若
sysread返回0,说明模块已断开连接,需重新建立Socket。同时查看模块的状态页面或日志,确认是否存在连接数限制、缓存溢出等问题。 - 优化响应完整性校验:ADAM模块的响应有固定格式(如以
>开头),不要固定读取200字节,应循环读取直到收到完整响应(比如读到特定结束标记),同时配合超时机制。 - 抓包排查网络问题:用
tcpdump或Wireshark抓包,确认发送#01\r后模块是否返回响应。若模块未发送,排查模块或网络链路;若模块已发送但脚本未收到,检查是否有丢包、防火墙拦截等情况。 - 用select做可读检查:在调用
sysread前,用select检查Socket是否有可读数据并设置超时,避免直接阻塞。示例代码:my $rin = ''; vec($rin, fileno($socket), 1) = 1; # 等待5秒,检查是否有可读数据 my $nfound = select($rin, undef, undef, 5); if ($nfound > 0) { my $bytes = sysread($socket, $data_line, 200); # 处理读取到的数据 } else { # 超时处理:关闭旧连接,重新建立连接 close($socket); # 重新执行socket创建和连接逻辑 }
内容的提问来源于stack exchange,提问作者Dennis
相关产品推荐
相关产品推荐

