为啥来自 /dev/null 的 ioctl FIONREAD 在 Mac OS X 上返回 0 而在 Linux 上返回随机数?

Posted

技术标签:

【中文标题】为啥来自 /dev/null 的 ioctl FIONREAD 在 Mac OS X 上返回 0 而在 Linux 上返回随机数?【英文标题】:Why does ioctl FIONREAD from /dev/null return 0 on Mac OS X and a random number on Linux?为什么来自 /dev/null 的 ioctl FIONREAD 在 Mac OS X 上返回 0 而在 Linux 上返回随机数? 【发布时间】:2018-09-25 03:22:44 【问题描述】:

在我正在处理的项目中添加测试时,我遇到了一些看起来很奇怪的事情 - 我一直在使用 /dev/null 作为串行端口,并且不希望有任何数据可供读取。

然而,在 LINUX 上总是有可用的数据,而在 Mac OS X 上,调用 srand() 后就有可用的数据。

有人可以帮助解释这种行为吗?

这是一个最小可行的 C++ 测试

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>

int open_serial(const char *device) 
    speed_t bd = B115200;
    int fd;
    int state;
    struct termios config;

    if ((fd = open(device, O_NDELAY | O_NOCTTY | O_NONBLOCK | O_RDWR)) == -1)
        return -1;

    fcntl(fd, F_SETFL, O_RDWR);
    tcgetattr(fd, &config);
    cfmakeraw(&config);
    cfsetispeed(&config, bd);
    cfsetospeed(&config, bd);

    config.c_cflag |= (CLOCAL | CREAD);
    config.c_cflag &= ~(CSTOPB | CSIZE | PARENB);
    config.c_cflag |= CS8;
    config.c_lflag &= ~(ECHO | ECHOE | ICANON | ISIG);
    config.c_oflag &= ~OPOST;
    config.c_cc[VMIN] = 0;
    config.c_cc[VTIME] = 50; // 5 seconds reception timeout

    tcsetattr(fd, TCSANOW, &config);
    ioctl(fd, TIOCMGET, &state);
    state |= (TIOCM_DTR | TIOCM_RTS);
    ioctl(fd, TIOCMSET, &state);

    usleep(10000);    // Sleep for 10 milliseconds
    return fd;
;

int serial_data_available(const int fd) 
    int result;
    ioctl(fd, FIONREAD, &result);
    return result;
;

int main() 
    int fd = open_serial("/dev/null");
    printf("Opened /dev/null - FD: %d\n", fd);
    printf("Serial data available : %d\n", serial_data_available(fd));
    printf("Serial data available : %d\n", serial_data_available(fd));
    printf("Calling srand()\n");
    srand(1234);
    printf("Serial data available : %d\n", serial_data_available(fd));
    printf("Serial data available : %d\n", serial_data_available(fd));
    return 0;

在 Mac OS X 下输出如下:-

Opened /dev/null - FD: 3
Serial data available : 0
Serial data available : 0
Calling srand()
Serial data available : 148561936
Serial data available : 0

在 Linux 上,我得到以下信息:-

Opened /dev/null - FD: 3
Serial data available : 32720
Serial data available : 32720
Calling srand()
Serial data available : 32720
Serial data available : 32720

两个问题-

    /dev/null 不应该总是有 0 个字节可供读取吗? 为什么在 Mac OS X 上调用 srand() 会导致可从 /dev/null 读取的字节发生变化?

【问题讨论】:

【参考方案1】:

问题很明显(事后看来!) - 结果 int 未初始化,因此当 ioctl 出现错误时,该函数返回一个非零整数,即使数据可能不可用。

int serial_data_available(const int fd) 
    int result;
    ioctl(fd, FIONREAD, &result);
    return result;
;

正确的代码应该是

int serial_data_available(const int fd) 
    int result = 0;
    ioctl(fd, FIONREAD, &result);
    return result;
;

【讨论】:

以上是关于为啥来自 /dev/null 的 ioctl FIONREAD 在 Mac OS X 上返回 0 而在 Linux 上返回随机数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 ioctl 调用没有传递给 sys_ioctl?

为啥 ioctl 返回“错误地址”

为啥有些 ioctl 案例总是失败?

为啥 ioctl 调用的原型使用 unsigned long 作为第三个参数?

ioctl(sock, SIOCETHTOOL, &ifr) 为啥它总是返回-1

语法错误:bash 脚本中的“fi”意外(预期“then”)