套接字读取设备的 ioctl 不合适

Posted

技术标签:

【中文标题】套接字读取设备的 ioctl 不合适【英文标题】:Inappropriate ioctl for device on socket read 【发布时间】:2020-10-28 14:42:17 【问题描述】:
    my $waitlist = IO::Select->new($self->sock);
    while($datalen < 9)
    
            ##timeout set as 50
            if($waitlist->can_read($timeout || 0)) 
                    print_message(LOGLEVEL_TRACE, "INSIDE IF....", LOG_TAG);
                    $templen = $self->sock->sysread($tempdata, 9 - $datalen);
             else 
                    print_message(LOGLEVEL_TRACE, "INSIDE ELSE....", LOG_TAG);
                    print_message(LOGLEVEL_TRACE, "B4 ERROR $!", LOG_TAG);
                    $templen = 0;
                    $! = EWOULDBLOCK;
            
           
            print_message(LOGLEVEL_TRACE, "ERROR $!", LOG_TAG);
    

在上面的代码中,“can_read”作为不适当的 ioctl for device 出现错误。想知道原因和解决需要做的事情。

【问题讨论】:

sock 是否已正确初始化,它是否引用了处于有效状态(打开,无错误)的套接字? 【参考方案1】:

$! 只有在系统调用指示它设置了$! 后才有意义。

问题:

在使用系统调用之前不要检查是否设置了$!。 在你感兴趣的系统调用和$!的使用之间调用print_messageprint_message肯定可以进行系统调用。

需要注意的一点是检查can_read是否返回错误的方式真的很奇怪。

返回一个可供读取的句柄数组。 TIMEOUT 是在返回空列表($! 不变)之前等待的最长时间,以秒为单位,可能是小数。如果没有给出TIMEOUT 并且注册了任何句柄,那么调用将无限期地阻塞。发生错误时,将返回一个空列表,并设置$! 以指示错误。为了区分超时和错误,在调用该方法之前将$!设置为零,并在返回空列表后检查。


替换

my $waitlist = IO::Select->new($self->sock);
while($datalen < 9)

     ##timeout set as 50
    if($waitlist->can_read($timeout || 0)) 
        print_message(LOGLEVEL_TRACE, "INSIDE IF....", LOG_TAG);
        $templen = $self->sock->sysread($tempdata, 9 - $datalen);
     else 
        print_message(LOGLEVEL_TRACE, "INSIDE ELSE....", LOG_TAG);
        print_message(LOGLEVEL_TRACE, "B4 ERROR $!", LOG_TAG);
        $templen = 0;
        $! = EWOULDBLOCK;
    
       
    print_message(LOGLEVEL_TRACE, "ERROR $!", LOG_TAG);

my $waitlist = IO::Select->new($self->sock);
while (length($tempdata) < 9) 
    $! = 0;
    my @handles = $waitlist->can_read($timeout || 0);
    if ($!)  # Yuck, just about anywhere else, this would be wrong.
        print_message(LOGLEVEL_TRACE, "SELECT FAILED: $!", LOG_TAG);
        ...abort...
    

    if (!@handles) 
        print_message(LOGLEVEL_TRACE, "SELECT FAILED: Timeout", LOG_TAG);
        ...abort...
    

    my $bytes_read = $self->sock->sysread($tempdata, 9 - $datalen, length($tempdata));
    if (!defined($bytes_read)) 
        print_message(LOGLEVEL_TRACE, "SYSREAD FAILED: $!", LOG_TAG);
        ...abort...
    

    if (!$bytes_read) 
        if (length($tempdata)) 
           print_message(LOGLEVEL_TRACE, "SYSREAD FAILED: Premature EOF", LOG_TAG);
           ...abort...
        

        # Reached EOF without reading anything.
        last;
    

在您执行这些更改之前,我们不知道您是否遇到了错误,更不用说是什么错误了。


评论:

您遗漏了sysread 的最后一个参数,这是必要的。

如果没有超时,像上面那样使用select 是完全没用的(因为只有一个句柄)。 sysread 已经执行了必要的等待。

如果有超时,则不是绝对超时。例如,假设您有 5 秒的超时,但您始终每 4 秒获得 1 个字节,上述将运行 36 秒而不会超时。 (好吧,这不是一个实际的例子,只是因为我们只读取 9 个字节。)对于绝对超时,您需要跟踪时间已经过去了。 (我会先使用my $wait_til = time() + timeout();,然后在检查它是否为阳性后将$wait_til - time() 传递给can_read。)

【讨论】:

您好,感谢您的支持。我已经按照您上面的建议修改了我的代码。我注意到 2 件事 1. 即使在设置 $!=0 之后,它总是返回为“不适合设备的 ioctl”。但这不会引起问题。我可以看到成功和失败的传输都会出现此错误消息。 2. 我认为导致的问题是在添加以下行后,我可以看到“SELECT FAILED: Timeout”消息出现在传输失败案例中。而成功传输文件则不会发生。所以希望这次超时会引起问题。你能建议我需要做什么吗? 在没有错误的情况下,Perl 系统调用将$! 设置为该值是很常见的。我一看到Inappropriate ioctl for device(即ENOTTY),就知道你在使用$!,而没有检查最后一次系统调用是否指示错误。现在你说你做了这个并且仍然得到错误,所以你说 Perl 中有一个错误。坦率地说,我不相信你。 ENOTTY 甚至不是 select 在 Linux 中可以返回的错误。 想知道您对第 2 点的评论。我认为导致的问题是在添加以下行之后,我可以看到“SELECT FAILED: Timeout”消息出现在传输失败案例中。因为它不会发生在成功的工作中。所以希望这次超时会引起问题。你能建议我需要做什么吗?美元! = 0;我的@handles = $waitlist->can_read($timeout || 0); if ($!) # 糟糕,几乎在任何地方 print_message(LOGLEVEL_TRACE, "SELECT FAILED: $!", LOG_TAG); if (!@handles) print_message(LOGLEVEL_TRACE, "SELECT FAILED: Timeout", LOG_TAG) 你没有中止。你继续前进......你需要一个diereturnexit 如果我添加 die(中止)传输停止。我需要知道为什么超时来了?我需要检查或升级服务器设置中的内容吗?我的预期结果是需要克服超时问题并传输文件。那么是否有可能是服务器设置或服务器配置或网络问题导致我们需要检查?

以上是关于套接字读取设备的 ioctl 不合适的主要内容,如果未能解决你的问题,请参考以下文章

ioctl() 用于 C 中的套接字编程

Linux - 带有 FIONREAD 的 ioctl 始终为 0

c ++设备不合适的ioctl

(10)Linux 网络编程之ioctl函数

在 perl 中更改 egid 时设备的 ioctl 不合适

opencv 无法停止流:设备的 ioctl 不合适