2013-04-09 21 views
7

Ich kann lesen/schreiben ein Linux-Block-Gerät mit Java mit java.nio. Der folgende Code funktioniert:Wie Speicherabbild (mmap) ein Linux-Block-Gerät (z. B./dev/sdb) in Java?

Path fp = FileSystems.getDefault().getPath("/dev", "sdb"); 
FileChannel fc = null; 
try { 
    fc = FileChannel.open(fp, EnumSet.of(StandardOpenOption.READ, StandardOpenOption.WRITE)); 
} catch (Exception e) { 
    System.out.println("Error opening file: " + e.getMessage()); 
} 
ByteBuffer buf = ByteBuffer.allocate(50); 
try { 
    if(fc != null) 
    fc.write(buf); 
} catch (Exception e) { 
    System.out.println("Error writing to file: " + e.getMessage()); 
} 

Speicherzuordnung funktioniert jedoch nicht. Der folgende Code nicht:

MappedByteBuffer mbb = null; 
try { 
    mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 100); 
} catch (IOException e) { 
    System.out.println("Error mapping file: " + e.getMessage()); 
} 

Dieses mit Fehler fehlschlägt:

java.io.IOException: Invalid argument 
    at sun.nio.ch.FileDispatcherImpl.truncate0(Native Method) 
    at sun.nio.ch.FileDispatcherImpl.truncate(FileDispatcherImpl.java:79) 
    at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:817) 

Gibt es eine Arbeit um zu diesem? Vielleicht mit einer anderen Bibliothek? Ich habe irgendwo gelesen, dass ich das vielleicht mit JNI machen könnte, aber ich konnte keine Quellen finden.

+0

Ist das Ihr richtiger Code? Sicher wird truncate() nur im schreibgeschützten Modus aufgerufen? – EJP

Antwort

3

Gemäß der documentation sind die Mechanismen der tatsächlichen Zuordnung einer Datei der Implementierung überlassen. Es scheint, dass die Implementierung versucht, die Datei zu kürzen (möglicherweise, weil die Größe des Blockgeräts nicht der von Ihnen angegebenen Größe entspricht).

Ich bin neugierig, warum Sie direkt aus dem Block-Gerät lesen (es sei denn, Sie versuchen, eine Art Dateisystem-Dienstprogramm oder etwas zu schreiben, die rohe I/O tun müssen). Wenn Sie direkt aus dem Blockgerät als Memory-Mapped-Datei lesen müssen, müssen Sie möglicherweise C/C++ - Code schreiben, um die Datei zuzuordnen und Lesen/Schreiben zu verarbeiten und eine Java/JNI-Bridge-Klasse zum Überbrücken von Anrufen zu verwenden C/C++ - Code. Auf diese Weise behandeln Sie selbst den Aufruf von mmap() und können alle Optionen angeben, die Sie benötigen. Mit Blick auf die mmap() documentation können Sie möglicherweise keine Blockgeräte auf Ihrer Plattform angeben (ich vermute Linux, aber ich könnte falsch liegen).

Wenn Sie dies unbedingt innerhalb von Java tun müssen, müssen Sie möglicherweise read() Aufrufe und write() Aufrufe der entsprechenden Länge und des Offsets durchführen.

+0

Deshalb können Sie den Linux-Framebuffer nicht mit den Aktienklassen mappen. Aber es kann getan werden. Sie müssen einige Dinge implementieren, um dorthin zu gelangen. –

1

FileChannel.map versucht, die Datei auf die angegebene Größe zu kürzen: See the implementation

Sie müssen die Größe des Blocks Gerät erhalten und diese genaue Größe der Karte Anruf übergeben.

Machen Sie sich keine Sorgen, wenn die Größe des tatsächlichen Geräts größer ist als Ihr verfügbarer Speicher. Das Betriebssystem wird nach Bedarf die Seiten im und aus dem Speicher austauschen.

2

Ich fand, dass der einfachste Ansatz ist, JNA und ein bisschen sun.*/com.sun.* Magie zu verwenden. Zunächst einmal müssen Sie libc so wickeln:

import com.sun.jna.LastErrorException; 
import com.sun.jna.Library; 
import com.sun.jna.Native; 
import com.sun.jna.Pointer; 

public interface LibC extends Library { 
    LibC instance = (LibC) Native.loadLibrary("c", LibC.class); 

    Pointer mmap(Pointer address, long length, 
       int protect, int flags, int fd, 
       long offset) throws LastErrorException; 
    // implement more methods if you like 
} 

Und dann sind Sie fast fertig! Sie benötigen lediglich einen Dateideskriptor. Es kann ein bisschen knifflig sein:

RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); 
int fd = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess() 
           .get(randomAccessFile.getFD()); 

Das ist es. Jetzt können Sie libc von Java aufrufen:

Pointer result = LibC.instance.mmap(
    /*pass your desired parameters along with obtained fd*/ 
);