2013-04-09 23 views
7

Tôi có thể đọc/ghi thiết bị khối linux bằng Java sử dụng java.nio. Mã sau đây hoạt động:Làm thế nào để bộ nhớ bản đồ (mmap) một thiết bị khối linux (ví dụ/dev/sdb) trong 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()); 
} 

Tuy nhiên, ánh xạ bộ nhớ không hoạt động. Các mã sau không:

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

này không thành công với Lỗi:

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ó một công việc xung quanh để điều này? Có lẽ bằng cách sử dụng một thư viện khác? Tôi đọc ở đâu đó có thể bằng cách sử dụng JNI tôi có thể làm điều này, nhưng tôi không thể tìm thấy bất kỳ nguồn nào.

+0

Đó có phải là mã thực sự của bạn không? Chắc chắn truncate() chỉ được gọi trong chế độ chỉ ghi? – EJP

Trả lời

3

Theo số documentation, các cơ chế thực sự ánh xạ một tệp được để lại cho việc triển khai. Dường như việc triển khai đang cố gắng cắt bớt tệp (có thể do kích thước thiết bị khối không giống với kích thước bạn chỉ định?).

Tôi tò mò tại sao bạn đang đọc trực tiếp từ thiết bị khối (trừ khi bạn đang cố gắng viết một số loại tiện ích hệ thống tệp hoặc thứ gì đó cần làm I/O thô). Nếu bạn cần đọc từ thiết bị khối dưới dạng tệp ánh xạ bộ nhớ trực tiếp, bạn có thể cần viết một số mã C/C++ để ánh xạ tệp và xử lý việc đọc/ghi và sử dụng lớp cầu nối Java/JNI để kết nối cuộc gọi đến Mã C/C++. Bằng cách đó bạn xử lý gọi mmap() cho mình và có thể chỉ định bất kỳ tùy chọn nào bạn cần. Nhìn vào mmap() documentation bạn có thể không thể xác định các thiết bị khối trên nền tảng của bạn (tôi đoán Linux nhưng tôi có thể sai).

Nếu bạn hoàn toàn cần thực hiện việc này trong Java, bạn có thể cần phải thực hiện các cuộc gọi read() và write() với độ dài và độ dài thích hợp.

+0

Đây là lý do tại sao bạn không thể mmap bộ đệm khung Linux với các lớp chứng khoán. Nhưng nó có thể được thực hiện. Bạn phải thực hiện một số công cụ để đạt được điều đó. –

1

FileChannel.map cố gắng cắt các tập tin với kích thước quy định: See the implementation

Bạn sẽ cần phải nhận được kích thước của thiết bị khối và thông qua đó kích thước chính xác để gọi đồ.

Đừng lo lắng nếu kích thước của thiết bị thực tế lớn hơn bộ nhớ có sẵn của bạn. Hệ điều hành sẽ xử lý trao đổi các trang vào và ra khỏi bộ nhớ khi cần thiết.

2

Tôi thấy rằng cách tiếp cận dễ nhất là sử dụng JNA và một chút phép thuật sun.*/com.sun.*. Trước hết, bạn cần phải bọc libc như thế này:

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 
} 

Và sau đó bạn sắp hoàn tất! Tất cả những gì bạn cần là lấy bộ mô tả tập tin. Có thể hơi phức tạp một chút:

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

Vậy đó. Bây giờ bạn có thể gọi libc từ java:

Pointer result = LibC.instance.mmap(
    /*pass your desired parameters along with obtained fd*/ 
); 
Các vấn đề liên quan