繁体   English   中英

为什么Files.readAllBytes首先读取bufsize为1?

[英]Why does Files.readAllBytes first read with a bufsize of 1?

我正在编写一个简单的Linux USB字符驱动程序,允许从它创建的设备节点读取短字符串。

它工作正常,但我注意到使用cat从设备节点读取与使用Files.readAllBytes从Java程序读取之间存在差异。

cat读取,在第一次调用file_operations.read函数时,将传递大小为131072的缓冲区,并复制5个字节的字符串:

kernel: [46863.186331] usbtherm: Device was opened
kernel: [46863.186407] usbtherm: buffer: 131072, read: 5, offset: 5
kernel: [46863.186444] usbtherm: done, returning 0
kernel: [46863.186481] usbtherm: Device was released

使用Files.readAllBytes读取时 ,在第一次调用时传入大小为1的缓冲区,然后传入大小为8191的缓冲区,并复制其余4个字节:

kernel: [51442.728879] usbtherm: Device was opened
kernel: [51442.729032] usbtherm: buffer: 1, read: 1, offset: 1
kernel: [51442.729102] usbtherm: buffer: 8191, read: 4, offset: 5
kernel: [51442.729140] usbtherm: done, returning 0
kernel: [51442.729158] usbtherm: Device was released

file_operations.read函数(包括调试printk )为:

static ssize_t device_read(struct file *filp, char *buffer, size_t length,
        loff_t *offset)
{
    int err = 0;
    size_t msg_len = 0;
    size_t len_read = 0;

    msg_len = strlen(message);

    if (*offset >= msg_len)
    {
        printk(KERN_INFO "usbtherm: done, returning 0\n");
        return 0;
    }

    len_read = msg_len - *offset;
    if (len_read > length)
    {
        len_read = length;
    }

    err = copy_to_user(buffer, message + *offset, len_read);
    if (err)
    {
        err = -EFAULT;
        goto error;
    }

    *offset += len_read;

    printk(KERN_INFO "usbtherm: buffer: %ld, read: %ld, offset: %lld\n", 
            length, len_read, *offset);

    return len_read;

error:
    return err;
}

两种情况下读取的字符串都是相同的,所以我想这还可以,我只是想知道为什么会有不同的行为吗?

GNU cat

cat的来源中,

      insize = io_blksize (stat_buf);

您会看到缓冲区的大小是由coreutils的io_bliksize()确定的,在这方面有一个相当有趣的注释

/ *自2014年5月起,最小的blksize被确定为128KiB,以最大程度地减少系统调用开销。

因此,用cat解释结果,因为128KiB为131072字节,而GNUrus认为这是最大程度地减少系统调用开销的最佳方法。

Files.readAllBytes

至少对于像我这样的简单灵魂来说,要把握起来要难一些。 readAllBytes来源

public static byte[] readAllBytes(Path path) throws IOException {
    try (SeekableByteChannel sbc = Files.newByteChannel(path);
         InputStream in = Channels.newInputStream(sbc)) {
        long size = sbc.size();
        if (size > (long)MAX_BUFFER_SIZE)
            throw new OutOfMemoryError("Required array size too large");

        return read(in, (int)size);
    }
}

显示它只是调用read(InputStream, initialSize) ,其中初始大小由字节通道的大小确定。 size()方法也有一个有趣的注释,

不是isRegularFile()文件的文件大小是特定于实现的,因此未指定。

最后, read(InputStream, initialSize)调用InputStream.read(byteArray, offset, length)进行读取(源中的注释来自原始源,并且由于capacity - nread = 0造成了混乱,因此第一次使用while循环达到,它读取到EOF):

private static byte[] read(InputStream source, int initialSize)
        throws IOException {
    int capacity = initialSize;
    byte[] buf = new byte[capacity];
    int nread = 0;
    int n;
    for (;;) {
        // read to EOF which may read more or less than initialSize (eg: file
        // is truncated while we are reading)
        while ((n = source.read(buf, nread, capacity - nread)) > 0)
            nread += n;

        // if last call to source.read() returned -1, we are done
        // otherwise, try to read one more byte; if that failed we're done too
        if (n < 0 || (n = source.read()) < 0)
            break;

        // one more byte was read; need to allocate a larger buffer
        if (capacity <= MAX_BUFFER_SIZE - capacity) {
            capacity = Math.max(capacity << 1, BUFFER_SIZE);
        } else {
            if (capacity == MAX_BUFFER_SIZE)
                throw new OutOfMemoryError("Required array size too large");
            capacity = MAX_BUFFER_SIZE;
        }
        buf = Arrays.copyOf(buf, capacity);
        buf[nread++] = (byte)n;
    }
    return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
}

Files BUFFER_SIZE的声明

    // buffer size used for reading and writing
    private static final int BUFFER_SIZE = 8192;

InputStream.read(byteArray, offset, length)文档/源包含相关注释,

如果length为零,则不读取任何字节,并返回0;否则,返回0。

由于size()为您的设备节点返回0字节,因此这是在read(InputStream source, int initialSize)发生的情况:

for (;;)循环的第一轮中:

  • capacity=0nread=0 因此while ((n = source.read(buf, nread, capacity - nread)) > 0)source.read while ((n = source.read(buf, nread, capacity - nread)) > 0)将0字节读入buf并返回0: while循环的条件为false,它所做的就是作为条件的副作用, n = 0

  • 由于n = 0source.read()if (n < 0 || (n = source.read()) < 0) break; 读取1个字节,表达式的计算结果为false :我们的for循环不会退出。 这将导致您的“ 缓冲区:1,读取:1,偏移量:1

  • 缓冲区的capacity设置为BUFFER_SIZE ,将读取的单个字节放入buf[0] ,并且nread递增。

for (;;)循环的第二轮

  • 因此具有capacity=8192nread=1 ,这使得while ((n = source.read(buf, nread, capacity - nread)) > 0) nread += n; 从偏移量1读取8191个字节,直到source.read返回-1:EOF! 读取剩余的4个字节后会发生这种情况。 这将导致您的“ 缓冲区:8191,读取:4,偏移:5 ”。

  • 从现在开始n = -1if (n < 0 || (n = source.read()) < 0) break;的表达式会if (n < 0 || (n = source.read()) < 0) break; n < 0上发生短路,这使我们的for循环退出而不再读取任何字节。

最后,该方法返回Arrays.copyOf(buf, nread) :缓冲区的该部分的副本,在该副本中放置了读取的字节。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM