2013-04-09 11 views
7

Posso leggere/scrivere un dispositivo a blocchi Linux con Java usando java.nio. Il seguente codice funziona:Come memorizzare la mappa (mmap) un dispositivo di blocco linux (ad esempio/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()); 
} 

Tuttavia, la mappatura della memoria non funziona. Il seguente codice fallisce:

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

Ciò non è riuscito con errore:

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) 

C'è un lavoro intorno a questo? Forse utilizzando una libreria diversa? Ho letto da qualche parte che forse usando JNI potrei farlo, ma non ho trovato alcuna fonte.

+0

È il tuo vero codice? Sicuramente truncate() viene chiamato solo in modalità di sola scrittura? – EJP

risposta

3

In base allo documentation i meccanismi di mappatura effettiva di un file vengono lasciati all'implementazione. Sembra che l'implementazione stia tentando di troncare il file (forse perché la dimensione del dispositivo di blocco non è uguale alla dimensione specificata?).

Sono curioso di sapere perché si sta leggendo direttamente dal dispositivo a blocchi (a meno che non si stia tentando di scrivere una sorta di utility per filesystem o qualcosa che deve fare l'I/O non elaborato). Se è necessario leggere direttamente dal dispositivo a blocchi come file mappato in memoria, potrebbe essere necessario scrivere del codice C/C++ per mappare il file e gestire la lettura/scrittura su di esso e utilizzare una classe bridge Java/JNI per collegare le chiamate al proprio Codice C/C++. In questo modo gestisci te stesso chiamando mmap() e puoi specificare tutte le opzioni di cui hai bisogno. Guardando il mmap() documentation potresti non essere in grado di specificare i dispositivi di blocco sulla tua piattaforma (sto indovinando Linux ma potrei sbagliarmi).

Se è assolutamente necessario farlo in Java, potrebbe essere necessario effettuare chiamate read() e chiamate write() della lunghezza e dell'offset appropriati.

+0

Ecco perché non è possibile mmap il buffer di frame Linux con le classi di magazzino. Ma può essere fatto. Devi attuare alcune cose per arrivarci. –

1

FileChannel.map tenta di troncare il file alla dimensione specificata: See the implementation

Sarà necessario per ottenere la dimensione del dispositivo a blocchi e passare tale dimensione esatta alla chiamata mappa.

Non preoccuparti se la dimensione del dispositivo reale è maggiore della memoria disponibile. Il sistema operativo gestirà lo scambio delle pagine dentro e fuori la memoria, se necessario.

2

Ho trovato che l'approccio più semplice è utilizzare JNA e un po 'di magia sun.*/com.sun.*. Prima di tutto è necessario per avvolgere libc in questo modo:

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 
} 

E poi si è quasi finito! Tutto ciò che serve è ottenere il descrittore di file. Può essere un po 'complicato:

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

Questo è tutto. Ora puoi chiamare libc da java:

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