File append có phải là nguyên tử trong UNIX không?


106

Nói chung, chúng ta có thể coi thường điều gì khi nối vào một tệp trong UNIX từ nhiều quy trình? Có thể mất dữ liệu (một quy trình ghi đè lên các thay đổi của quy trình khác) không? Có thể dữ liệu bị xáo trộn không? (Ví dụ: mỗi quy trình đang nối một dòng trên mỗi phần nối vào tệp nhật ký, liệu hai dòng có bị xáo trộn không?) Nếu phần nối thêm không phải là nguyên tử theo nghĩa trên, thì cách tốt nhất để đảm bảo loại trừ lẫn nhau là gì?

Câu trả lời:


65

Một chữ viết dưới kích thước 'PIPE_BUF' được cho là nguyên tử. Đó phải là ít nhất 512 byte, mặc dù nó có thể dễ dàng lớn hơn (linux dường như đã đặt nó thành 4096).

Điều này giả sử rằng bạn đang nói về tất cả các thành phần hoàn toàn tương thích với POSIX. Ví dụ, điều này không đúng trên NFS.

Nhưng giả sử bạn ghi vào tệp nhật ký mà bạn đã mở ở chế độ 'O_APPEND' và giữ cho các dòng của mình (bao gồm cả dòng mới) dài dưới byte 'PIPE_BUF', bạn sẽ có thể có nhiều người ghi vào một tệp nhật ký mà không gặp bất kỳ vấn đề hỏng hóc nào. Mọi sự gián đoạn sẽ đến trước hoặc sau khi ghi, không phải ở giữa. Nếu bạn muốn tính toàn vẹn của tệp để tồn tại fsync(2)sau khi khởi động lại, bạn cũng cần phải gọi sau mỗi lần ghi, nhưng điều đó thật tồi tệ đối với hiệu suất.

Làm rõ : đọc các bình luận và câu trả lời của Oz Solomon . Tôi không chắc rằng nó O_APPENDđược cho là có PIPE_BUFkích thước nguyên tử. Hoàn toàn có thể đó chỉ là cách Linux triển khai write()hoặc có thể do kích thước khối của hệ thống tệp bên dưới.


11
Trên các hệ thống tệp lành mạnh, fsync(2)đảm bảo nhiều như sync(2)vậy và không có nhiều tác động lớn đến hiệu suất.
ephemient

4
Bạn có chắc chắn về điều đó không? Bạn có thể cung cấp một số liên kết về hành vi đó? Tôi thấy nó được xác nhận nếu bộ mô tả là một đường ống, nhưng tôi không thể tìm thấy bằng chứng cho thấy nó hoạt động với bất kỳ tệp nào . bao gồm các đối tượng tệp bình thường, không phải NFS.
Alan Franzoni

6
Chính xác thì ở đâu trong ... / write.html? Đối với O_APPEND, tôi không thấy đề cập đến PIPE_BUF và tôi thấy lời hứa rằng "sẽ không có thao tác sửa đổi tệp can thiệp nào xảy ra giữa việc thay đổi độ lệch tệp và thao tác ghi" , nhưng tôi không chắc liệu điều này có nghĩa là chính hoạt động ghi là không bị gián đoạn ...
akavel

6
Như câu trả lời này chỉ ra, tuyên bố về PIPE_BUFtrang đó chỉ áp dụng cho các đường ống và FIFO, không áp dụng cho các tệp thông thường.
Greg Inozemtsev

3
Với các tín hiệu đến, điều này có thể trở nên tồi tệ hơn: bugzilla.kernel.org/show_bug.cgi?id=55651 . Tại sao điều này thậm chí được đánh dấu là một câu trả lời? PIPE_BUF không liên quan gì đến tệp.
thinred

35

Chỉnh sửa: Cập nhật tháng 8 năm 2017 với kết quả Windows mới nhất.

Tôi sẽ cung cấp cho bạn câu trả lời với các liên kết đến mã kiểm tra và kết quả với tư cách là tác giả của Boost.AFIO được đề xuất triển khai hệ thống tệp không đồng bộ và thư viện tệp i / o C ++.

Thứ nhất, O_APPEND hoặc FILE_APPEND_DATA tương đương trên Windows có nghĩa là số gia tăng của phạm vi tệp tối đa ("độ dài" của tệp) là nguyên tử đối với người viết đồng thời. Điều này được đảm bảo bởi POSIX và Linux, FreeBSD, OS X và Windows đều triển khai nó một cách chính xác. Samba cũng thực hiện nó một cách chính xác, NFS trước v5 không có vì nó thiếu khả năng định dạng dây để nối nguyên tử. Vì vậy, nếu bạn mở tệp của mình bằng append-only, việc ghi đồng thời sẽ không bị xé lẫn nhau trên bất kỳ hệ điều hành chính nào trừ khi có NFS.

Tuy nhiên, việc đọc đồng thời đối với các phần bổ sung nguyên tử có thể thấy các bản ghi bị rách tùy thuộc vào hệ điều hành, hệ thống lưu trữ và bạn đã mở tệp bằng cờ nào - mức tăng của mức tối đa của tệp là nguyên tử, nhưng khả năng hiển thị của các lần ghi đối với lần đọc có thể có hoặc không là nguyên tử. Đây là bản tóm tắt nhanh theo cờ, hệ điều hành và hệ thống lưu trữ:


Không O_DIRECT / FILE_FLAG_NO_BUFFERING:

Microsoft Windows 10 với NTFS: cập nhật nguyên tử = 1 byte cho đến và bao gồm 10.0.10240, từ 10.0.14393 tối thiểu 1Mb, có thể là vô hạn (*).

Linux 4.2.6 với ext4: update nguyên tử = 1 byte

FreeBSD 10.2 với ZFS: cập nhật nguyên tử = ít nhất 1Mb, có thể là vô hạn (*)

O_DIRECT / FILE_FLAG_NO_BUFFERING:

Microsoft Windows 10 với NTFS: update atomity = cho đến khi và bao gồm 10.0.10240 lên đến 4096 byte chỉ khi căn chỉnh trang, nếu không 512 byte nếu tắt FILE_FLAG_WRITE_THROUGH, còn lại 64 byte. Lưu ý rằng tính nguyên tử này có thể là một tính năng của PCIe DMA hơn là được thiết kế trong. Kể từ 10.0.14393, ít nhất 1Mb, có thể là vô hạn (*).

Linux 4.2.6 với ext4: update nguyên tử = ít nhất 1Mb, có thể là vô hạn (*). Lưu ý rằng các Linux trước đó với ext4 chắc chắn không vượt quá 4096 byte, XFS chắc chắn đã từng có khóa tùy chỉnh nhưng có vẻ như Linux gần đây cuối cùng đã khắc phục điều này.

FreeBSD 10.2 với ZFS: cập nhật nguyên tử = ít nhất 1Mb, có thể là vô hạn (*)


Bạn có thể xem kết quả thử nghiệm thực nghiệm thô tại https://github.com/ned14/afio/tree/master/programs/fs-probe . Lưu ý rằng chúng tôi kiểm tra các hiệu số bị rách chỉ trên bội số 512 byte, vì vậy tôi không thể nói liệu bản cập nhật một phần của khu vực 512 byte có bị rách trong chu kỳ đọc-sửa đổi-ghi hay không.

Vì vậy, để trả lời câu hỏi của OP, O_APPEND ghi sẽ không can thiệp vào nhau, nhưng đọc đồng thời với O_APPEND ghi có thể sẽ thấy ghi bị rách trên Linux với ext4 trừ khi O_DIRECT được bật, trong đó O_APPEND ghi của bạn sẽ cần phải là bội số kích thước khu vực.


(*) "Có thể là vô hạn" bắt nguồn từ các mệnh đề này trong thông số kỹ thuật POSIX:

Tất cả các hàm sau đây sẽ là nguyên tử đối với nhau trong các hiệu ứng được chỉ định trong POSIX.1-2008 khi chúng hoạt động trên các tệp thông thường hoặc các liên kết tượng trưng ... [nhiều hàm] ... read () ... write ( ) ... Nếu mỗi luồng gọi một trong các hàm này, thì mỗi lệnh gọi sẽ thấy tất cả các tác dụng được chỉ định của lệnh gọi kia, hoặc không có tác dụng nào trong số chúng. [Nguồn]

Các bài viết có thể được đăng nhiều kỳ liên quan đến các bài đọc và viết khác. Nếu việc đọc () dữ liệu tệp có thể được chứng minh (bằng bất kỳ cách nào) xảy ra sau khi ghi () dữ liệu, thì nó phải phản ánh việc ghi () đó, ngay cả khi các lệnh gọi được thực hiện bởi các quy trình khác nhau. [Nguồn]

nhưng ngược lại:

Tập POSIX.1-2008 này không chỉ định hành vi ghi đồng thời vào một tệp từ nhiều quy trình. Các ứng dụng nên sử dụng một số hình thức kiểm soát đồng thời. [Nguồn]

Bạn có thể đọc thêm về ý nghĩa của những điều này trong câu trả lời này


29

Tôi đã viết một tập lệnh để kiểm tra theo kinh nghiệm kích thước phần phụ nguyên tử tối đa. Tập lệnh, được viết bằng bash, tạo ra nhiều quy trình công nhân, tất cả đều ghi chữ ký dành riêng cho công nhân vào cùng một tệp. Sau đó, nó đọc tệp, tìm kiếm chữ ký chồng chéo hoặc bị hỏng. Bạn có thể xem nguồn của script tại bài đăng trên blog này .

Kích thước phần phụ nguyên tử tối đa thực tế không chỉ thay đổi theo hệ điều hành mà còn theo hệ thống tệp.

Trên Linux + ext3 có kích thước là 4096 và trên Windows + NTFS có kích thước là 1024. Xem nhận xét bên dưới để biết thêm kích thước.


Bạn đã kiểm tra hệ thống tập tin nào trên Linux? Tôi tự hỏi liệu nó có thể dựa trên kích thước khối hệ thống tệp hay không.
freiheit

@freiheit Tôi tin vào thời điểm tôi đã thử nghiệm nó trên ext3. Nếu bạn chạy nó trên FS khác và nhận được kết quả khác, vui lòng gửi bình luận.
Oz Solomon

3
@OzSolomon, tôi đã sử dụng tập lệnh của bạn trên Debian 7.8 và tôi chỉ có thể ghi nguyên tử lên đến và bao gồm 1008 byte (1024 - 16 byte tổng chi phí?) Trên cả phân vùng ext4 của tôi và tệp gắn kết tmpfs. Bất cứ điều gì vượt quá đó đều dẫn đến tham nhũng.
Eric Pruitt

6
Thử nghiệm của bạn dường như giả định rằng echo $line >> $OUTPUT_FILEsẽ dẫn đến một cuộc gọi đến writebất kể kích thước của $line.
Tomas

16

Đây là những gì tiêu chuẩn cho biết: http://www.opengroup.org/onlinepubs/009695399/functions/pwrite.html .

Nếu O_APPENDcờ của các cờ trạng thái tệp được đặt, độ lệch tệp sẽ được đặt ở cuối tệp trước mỗi lần ghi và không có thao tác sửa đổi tệp nào xảy ra giữa việc thay đổi độ lệch tệp và thao tác ghi.


20
"between" - nhưng những gì về sự can thiệp trong quá trình viết, theo hiểu biết của tôi xảy ra sau "giữa"? (Tức là: <change_offset_action> ... "the_between_period" ... <write_action>) - tôi có hiểu là không có gì đảm bảo về điều đó không?
akavel

@akavel đồng ý; không có gì đảm bảo rằng bản thân chữ viết là nguyên tử. Nhưng tôi bối rối: dựa trên đảm bảo được cung cấp trong báo giá của bạn, có vẻ như chúng tôi có thể kết luận rằng một ứng dụng đa luồng nối cùng một tệp sẽ không kết hợp các phần của các bản ghi khác nhau. Tuy nhiên, từ các thí nghiệm được báo cáo bởi OzSolomon, chúng ta thấy rằng ngay cả giả định đó cũng bị vi phạm. Tại sao?
tối đa

@max xin lỗi, tôi e rằng không nhận được câu hỏi của bạn: thứ nhất, thử nghiệm của OzSolomon là ứng dụng đa quy trình , không phải ứng dụng đa luồng (quy trình đơn); thứ hai, tôi không hiểu làm thế nào bạn đưa ra kết luận rằng "một ứng dụng đa luồng [...] sẽ không kết hợp" - đó chính xác là những gì tôi không thấy được đảm bảo bởi trích dẫn từ Bastien, như tôi đã đề cập trong nhận xét của mình. Bạn có thể làm rõ câu hỏi của bạn?
akavel

2
Hmm, tôi không thể xây dựng lại logic của riêng mình tại thời điểm tôi viết nhận xét đó ... Vâng, nếu cách diễn giải của bạn là đúng thì tất nhiên các bản ghi khác nhau có thể bị trộn lẫn. Nhưng bây giờ tôi đang đọc lại câu trích dẫn của Bastien, tôi nghĩ điều đó phải có nghĩa là không ai có thể ngắt lời "trong khi viết" - nếu không thì toàn bộ đoạn văn trong tiêu chuẩn sẽ trở nên vô dụng, thực sự không có gì đảm bảo cả (ngay cả việc viết sẽ không xảy ra ở phần cuối, vì một số người khác có thể di chuyển bù đắp khi bước "ghi" đang được thực hiện.
tối đa
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.