2016-06-04 13 views
1

Ich schreibe einen einfachen Linux-USB-Zeichentreiber, der es erlaubt, einen kurzen String vom Geräteknoten zu lesen, den er erstellt.Warum liest Files.readAllBytes zuerst mit einer Bufsize von 1?

Es funktioniert gut, aber ich bemerkte einen Unterschied zwischen dem Lesen von dem Gerät Knoten mit cat und Lesen von einem Java-Programm mit Files.readAllBytes.

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 

mit Files.readAllBytes, eine Pufferlese mit:

mit cat Lese, ein Puffer mit einer Größe von 131072 an dem ersten Aufruf die file_operations.read Funktion und der 5 Byte String kopiert gebenen Beim ersten Aufruf wird Größe 1 übergeben, dann wird ein Puffer der Größe 8191 übergeben und die restlichen 4 Byte werden kopiert:

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 

Die file_operations.read Funktion (einschließlich der Debugging printk ‚s) ist:

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; 
} 

Die Zeichenfolge in beiden Fällen lesen ist identisch, so dass ich denke, es ist okay, ich frage mich nur, warum das unterschiedliche Verhalten?

+0

Können Sie sehen, was 'size()' für Ihren Geräteknoten zurückgibt? – Arjan

Antwort

2

GNU cat

In der Quelle cat,

 insize = io_blksize (stat_buf); 

Sie, dass die Größe des Puffer sehen io_bliksize() durch coreutils' bestimmt, die eine eher interesting comment in dieser Hinsicht hat,

/* Ab Mai 2014 ist 128KiB das Minimum blksize , um den Systemaufruf-Overhead zu minimieren.

So erklären, dass würde die Ergebnisse mit cat, da 128KiB 131072 Bytes ist und die GNUrus entschieden, dass der beste Weg, Systemaufruf Overhead zu minimieren.

Files.readAllBytes

ist ein bisschen schwieriger, zumindest für eine einfache Seele wie mich zu begreifen. Die source of 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); 
    } 
} 

zeigt es einfach ruft read(InputStream, initialSize), wo die Anfangsgröße durch die Größe des Byte-Kanal bestimmt wird. Die size() Methode hat auch eine interessante Bemerkung,

Die Größe der Dateien, die nicht isRegularFile sind (Dateien) ist die Umsetzung spezifisch und daher nicht näher bezeichnet.

Schließlich read(InputStream, initialSize) Anrufe InputStream.read(byteArray, offset, length) zu tun, das Lesen (Kommentare in Quelle sind von der ursprünglichen Quelle und sind verwirrend Dinge da capacity - nread = 0, so das erste Mal der while-Schleife erreicht ist, es tut nicht zu EOF lesen) :

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); 
} 

Die Erklärung von BUFFER_SIZE für Files

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

Dokumentation/Quelle InputStream.read(byteArray, offset, length) enthält einen entsprechenden Kommentar,

Wenn Länge Null ist, werden keine Bytes gelesen und 0 zurückgegeben;

Da size() liefert 0 Bytes für Ihren Geräteknoten, hier ist was in read(InputStream source, int initialSize) passiert:

In der ersten Runde der for (;;) Schleife:

  • capacity=0 und nread=0. So die source.read in der while ((n = source.read(buf, nread, capacity - nread)) > 0) liest 0 Bytes in buf und gibt 0 zurück: Der Zustand der while Schleife ist falsch, alles, was es tut, ist n = 0 als Nebeneffekt der Bedingung.

  • Seit n = 0, source.read() in if (n < 0 || (n = source.read()) < 0) break; liest 1 Byte, wird der Ausdruck mit false: unsere for Schleife nicht beendet. Dies führt in den "Puffer: 1, lesen: 1, Offset: 1"

  • die capacity des Puffers auf BUFFER_SIZE gesetzt ist, das einzelne Byte, das gelesen wurde, wird gesetzt in buf[0] und nread erhöht.

Die zweite Runde der for (;;) Schleife

  • somit capacity=8192 und nread=1 hat, die while ((n = source.read(buf, nread, capacity - nread)) > 0) nread += n; lesen 8191 Bytes von Offset 1 bis source.read -1 zurück macht: EOF! Was nach dem Lesen der restlichen 4 Bytes passiert. Dies führt zu Ihrem "Puffer: 8191, lesen: 4, Offset: 5".

  • Da nun n = -1 der Ausdruck in if (n < 0 || (n = source.read()) < 0) break; Kurzschlüssen auf den n < 0, die ohne unseren for Schleifenausgang macht kein weiteres Bytes zu lesen.

Schließlich gibt die Methode Arrays.copyOf(buf, nread): Eine Kopie des Teils des Puffers, wo er die gelesenen Bytes gesetzt.

+2

Hmm, die 'read()' -Methode, die Sie angeben, liest Byte für Byte, es gibt kein 'es versucht, den Rest der Bytes zu lesen. Die spezielle Verarbeitung des ersten Bytes wird für verschiedene Situationen verwendet, ob * irgendein Byte * vor dem Fehler/Ausnahme gelesen wurde. Es wirkt sich auf den Rückgabewert/die Ausnahme aus, wie von der Spezifikation der Methode gefordert. – Tsyvarev

+0

@Tsyvarev Einverstanden, das ist eine bessere Erklärung für die Verarbeitung des ersten Bytes. Ich bin auch verwirrt darüber, wo 'size()' bekommt es "unspezifiziert" -Wert aus, vielleicht ist das die Wurzel davon. – Arjan

+0

'size()' gibt 0 für den Geräteknoten zurück, während es die tatsächliche Größe einer regulären Datei wie d. H. "Ls -l" zurückgibt. –