2011-01-16 31 views
13

Bài viết của Tim Bray "Saving Data Safely" để lại cho tôi các câu hỏi mở. Hôm nay, nó đã hơn một tháng tuổi và tôi đã không thấy bất kỳ sự theo dõi nào, vì vậy tôi đã quyết định giải quyết vấn đề ở đây.tình huống ext4/fsync không rõ ràng trong Android (Java)

Một điểm của bài viết là FileDescriptor.sync() nên được gọi là an toàn khi sử dụng FileOutputStream. Lúc đầu, tôi đã rất khó chịu, bởi vì tôi chưa bao giờ thấy bất kỳ mã Java nào thực hiện đồng bộ trong suốt 12 năm tôi làm Java. Đặc biệt là khi đối phó với các tập tin là một điều khá cơ bản. Ngoài ra, JavaDoc chuẩn của FileOutputStream không bao giờ ám chỉ đồng bộ hóa (Java 1.0 - 6). Sau một số nghiên cứu, tôi đã tìm ra ext4 thực sự có thể là hệ thống tập tin chính thống đầu tiên cần đồng bộ hóa. (Có hệ thống tập tin khác, nơi đồng bộ rõ ràng được khuyên?)

Tôi đánh giá cao một vài suy nghĩ chung về vấn đề này, nhưng tôi cũng có một số câu hỏi cụ thể:

  1. Khi Android sẽ làm đồng bộ với hệ thống tập tin ? Điều này có thể định kỳ và bổ sung dựa trên các sự kiện vòng đời (ví dụ: quy trình của ứng dụng sẽ chuyển sang nền sau).
  2. FileDescriptor.sync() có chăm sóc đồng bộ hóa dữ liệu meta không? Đó là đồng bộ thư mục của tập tin đã thay đổi. So sánh với FileChannel.force().
  3. Thông thường, một người không trực tiếp ghi vào FileOutputStream. Đây là giải pháp của tôi (bạn có đồng ý không?):
     
    FileOutputStream fileOut = ctx.openFileOutput(file, Context.MODE_PRIVATE); 
    BufferedOutputStream out = new BufferedOutputStream(fileOut); 
    try { 
        out.write(something); 
        out.flush(); 
        fileOut.getFD().sync(); 
    } finally { 
        out.close(); 
    } 
    

Trả lời

10

Android sẽ làm đồng bộ hóa khi nó cần phải - chẳng hạn như khi màn hình tắt, tắt các thiết bị, vv Nếu bạn chỉ nhìn vào "bình thường" hoạt động, đồng bộ hóa rõ ràng bởi các ứng dụng là không bao giờ cần thiết.

Sự cố xảy ra khi người dùng kéo pin ra khỏi thiết bị của họ (hoặc đặt lại hạt nhân cứng) và bạn muốn đảm bảo bạn không bị mất bất kỳ dữ liệu nào. Vì vậy, điều đầu tiên cần nhận ra: vấn đề là khi mất điện đột ngột, vì vậy việc tắt máy sạch sẽ không thể xảy ra, và câu hỏi về những gì sẽ xảy ra trong lưu trữ liên tục tại thời điểm đó.

Nếu bạn chỉ đang viết một tệp mới độc lập, điều đó không quan trọng bạn làm gì. Người dùng có thể đã kéo pin trong khi bạn đang ở giữa văn bản, ngay trước khi bạn bắt đầu viết, v.v. Nếu bạn không đồng bộ hóa, điều đó có nghĩa là có một khoảng thời gian dài hơn khi bạn viết xong trong thời gian kéo pin sẽ mất dữ liệu.

Mối quan tâm lớn ở đây là khi bạn muốn cập nhật tệp. Trong trường hợp đó, khi bạn đọc tệp tiếp theo bạn muốn có hoặcnội dung trước đây, hoặc nội dung mới mới. Bạn không muốn nhận một cái gì đó nửa chiều bằng văn bản, hoặc mất dữ liệu.

Việc này thường được thực hiện bằng cách ghi dữ liệu vào một tệp mới, sau đó chuyển sang tệp đó từ tệp cũ. Trước khi ext4 bạn biết rằng, khi bạn đã viết xong một tệp, các thao tác khác trên các tệp khác sẽ không được lưu trên đĩa cho đến tệp trên tệp đó, vì vậy bạn có thể xóa an toàn tệp trước đó hoặc thực hiện các thao tác phụ thuộc vào tệp mới của mình được viết đầy đủ.

Tuy nhiên bây giờ nếu bạn ghi tệp mới, sau đó xóa tệp cũ và pin được kéo, khi bạn khởi động tiếp theo, bạn có thể thấy tệp cũ bị xóa và tệp mới được tạo nhưng nội dung của tệp mới không hoàn thành.Bằng cách thực hiện đồng bộ, bạn đảm bảo rằng tệp mới được ghi hoàn toàn tại thời điểm đó để có thể thực hiện các thay đổi khác (chẳng hạn như xóa tệp cũ) phụ thuộc vào trạng thái đó.

+0

Tôi đã dự kiến ​​lệnh flush() để đảm bảo mọi thứ được ghi vào đĩa - về cơ bản là bằng cách gọi sync(). Đó không phải là trường hợp? –

+0

@a_horse_with_no_name: Không, không phải. _flush_ và _sync_ là hai hoạt động khác nhau: flush chỉ xóa các bộ đệm trung gian; đồng bộ thực sự ghi vào bộ nhớ. Xem ví dụ http://stackoverflow.com/questions/2340610/difference-between-fflush-and-fsync – sleske

+0

@sleske: cảm ơn vì đã làm rõ! –

1

fileOut.getFD().sync(); phải ở trên mệnh đề cuối cùng, trước close().

sync() là cách quan trọng hơn close() xem xét độ bền.

Vì vậy, mỗi lần bạn muốn 'hoàn thành' làm việc trên một tệp, bạn nên sync() trước khi close() nhập vào.

posix không đảm bảo rằng các ghi đang chờ xử lý sẽ được ghi vào đĩa khi bạn phát hành close().

Các vấn đề liên quan