Làm thế nào để các chương trình có thể tiếp tục chuyển tập tin thất bại biết nơi bắt đầu nối thêm dữ liệu?


23

Một số chương trình sao chép tệp như rsynccurlcó khả năng tiếp tục chuyển / sao chép không thành công.

Lưu ý rằng có thể có nhiều nguyên nhân của những thất bại này, trong một số trường hợp, chương trình có thể "dọn dẹp" một số trường hợp chương trình không thể.

Khi các chương trình này tiếp tục, chúng dường như chỉ tính kích thước của tệp / dữ liệu được truyền thành công và chỉ bắt đầu đọc byte tiếp theo từ nguồn và nối vào đoạn tệp.

ví dụ: kích thước của đoạn tệp "đã tạo ra" đến đích là 1378 byte, vì vậy họ chỉ bắt đầu đọc từ byte 1379 trên bản gốc và thêm vào đoạn đó.

Câu hỏi của tôi là, biết rằng các byte được tạo thành từ các bit và không phải tất cả các tệp có dữ liệu được phân đoạn theo các đoạn có kích thước byte sạch, làm thế nào để các chương trình này biết chúng là điểm mà chúng đã chọn để bắt đầu thêm dữ liệu là chính xác?

Khi viết tệp đích là một loại bộ đệm hoặc "giao dịch" tương tự như cơ sở dữ liệu SQL xảy ra, ở cấp độ chương trình, kernel hoặc hệ thống tệp để đảm bảo rằng chỉ các byte được tạo sạch, được tạo thành tốt cho thiết bị khối bên dưới?
Hoặc các chương trình giả định rằng byte mới nhất sẽ có khả năng không đầy đủ, vì vậy họ xóa nó với giả định là xấu, lấy lại byte và bắt đầu nối thêm từ đó?

biết rằng không phải tất cả dữ liệu được biểu diễn dưới dạng byte, những dự đoán này có vẻ không chính xác.

Khi các chương trình này "tiếp tục" làm sao họ biết họ đang bắt đầu đúng nơi?


21
"không phải tất cả các tệp có dữ liệu được phân đoạn theo các đoạn có kích thước byte sạch" chúng không? Làm thế nào để bạn viết bất cứ điều gì ít hơn một byte vào một tập tin?
muru

17
Tôi biết rằng không có cuộc gọi hệ thống nào có thể ghi bất cứ điều gì ít hơn một byte và đối với bản thân đĩa, tôi nghĩ rằng không có đĩa nào hôm nay ghi ít hơn 512 byte khối (hoặc khối 4096 byte).
muru

8
Không, tôi đang nói mức tối thiểu là một byte. Các ứng dụng Sane sẽ sử dụng các khối 4KB hoặc 8KB: head -c 20480 /dev/zero | strace -e write tee foo >/dev/nullvà sau đó, HĐH sẽ đệm chúng lên và gửi chúng vào đĩa trong các khối lớn hơn.
muru

9
@the_velour_fog: Làm thế nào để bạn viết chỉ một bit với fwrite()?
psmears

9
Đối với tất cả các mục đích thực tế, dữ liệu được tạo thành từ các byte và mọi thứ hoạt động với chúng là đơn vị nhỏ nhất. Một số hệ thống (chủ yếu liên quan đến nén, ví dụ như gzip, h264) giải nén các bit riêng lẻ ra khỏi byte, nhưng hoạt động của hệ điều hành và bộ nhớ nằm ở mức byte.
pjc50

Câu trả lời:


40

Để rõ ràng - cơ học thực sự phức tạp hơn để bảo mật tốt hơn - bạn có thể tưởng tượng hoạt động ghi vào đĩa như thế này:

  • ứng dụng ghi byte (1)
  • kernel (và / hoặc hệ thống tệp IOSS) đệm chúng
  • khi bộ đệm đầy, nó được đỏ mặt với hệ thống tập tin:
    • khối được phân bổ (2)
    • khối được viết (3)
    • thông tin về tập tin và khối được cập nhật (4)

Nếu quá trình bị gián đoạn tại (1), bạn không nhận được bất cứ thứ gì trên đĩa, tệp vẫn còn nguyên vẹn và bị cắt ở khối trước. Bạn đã gửi 5000 byte, chỉ có 4096 trên đĩa, bạn khởi động lại chuyển ở offset 4096.

Nếu tại (2), không có gì xảy ra ngoại trừ trong bộ nhớ. Tương tự như (1). Nếu tại (3), dữ liệu được ghi nhưng không ai nhớ về nó . Bạn đã gửi 9000 byte, 4096 đã được viết, 4096 đã bị ghi và bị mất , phần còn lại chỉ bị mất. Chuyển hồ sơ tại offset 4096.

Nếu tại (4), dữ liệu sẽ được cam kết trên đĩa. Các byte tiếp theo trong luồng có thể bị mất. Bạn đã gửi 9000 byte, 8192 được ghi, phần còn lại bị mất, chuyển lại sơ yếu lý lịch ở offset 8192.

Đây là một đơn giản hóa mất. Ví dụ: mỗi lần ghi "logic" trong các giai đoạn 3-4 không phải là "nguyên tử", nhưng tạo ra một chuỗi khác (hãy đánh số thứ 5), theo đó, khối được chia thành các khối con phù hợp với thiết bị đích (ví dụ: đĩa cứng ) được gửi đến bộ điều khiển máy chủ của thiết bị, cũng có cơ chế lưu trữ và cuối cùng được lưu trữ trên đĩa từ. Chuỗi con này không phải lúc nào cũng hoàn toàn nằm dưới sự kiểm soát của hệ thống, do đó, việc gửi dữ liệu vào đĩa cứng không phải là một đảm bảo rằng nó đã thực sự được ghi và sẽ có thể đọc lại được.

Một số hệ thống tệp thực hiện ghi nhật ký , để đảm bảo rằng điểm dễ bị tổn thương nhất, (4), không thực sự dễ bị tổn thương, bằng cách ghi dữ liệu meta vào, bạn đoán nó, các giao dịch sẽ hoạt động ổn định bất cứ điều gì xảy ra trong giai đoạn (5).

Nếu hệ thống được đặt lại ở giữa một giao dịch, nó có thể tiếp tục đường đến điểm kiểm tra nguyên vẹn gần nhất. Dữ liệu được viết vẫn bị mất, giống như trường hợp (1), nhưng việc nối lại sẽ giải quyết vấn đề đó. Không có thông tin thực sự bị mất.


1
Giải thích tuyệt vời. tất cả làm cho rất nhiều ý nghĩa. Vì vậy, nếu một quá trình thực hiện tất cả các cách để (4) thông tin khối tệp được cập nhật, bạn biết tất cả các byte đó đều tốt. sau đó, bất kỳ byte nào ở bất kỳ giai đoạn nào trước đó đều không được đưa vào đĩa hoặc - nếu có - chúng sẽ là "không nhớ" (không có tham chiếu đến chúng)
the_velour_fog 6/2/18

4
@the_velour_fog Và chỉ để bổ sung cho đoạn áp chót - nếu bạn đang sử dụng một hệ thống tệp không thực hiện ghi nhật ký, bạn thực sự có thể nhận được dữ liệu "bị hỏng", khiến sơ yếu lý lịch bị lỗi và tạo ra một tệp bị cắt xén mà không gây ra lỗi cho bạn. Điều này từng xảy ra mọi lúc trong quá khứ, đặc biệt là với các hệ thống tệp được thiết kế cho các thiết bị có độ trễ cao (như đĩa mềm). Vẫn còn một số thủ thuật để tránh điều này ngay cả khi hệ thống tệp không đáng tin cậy theo cách này, nhưng nó cần một ứng dụng thông minh hơn để bù đắp và một số giả định có thể đã sai trên một số hệ thống.
Luaan

Câu trả lời này nói quá mức hữu ích của việc ghi nhật ký trong các hệ thống tệp. Nó không hoạt động đáng tin cậy trừ khi mọi thứ thực hiện ngữ nghĩa giao dịch, bao gồm các ứng dụng không gian người dùng (thông qua fsync) và bộ điều khiển ổ cứng (thường bị hỏng, ngay cả trong các ổ đĩa được cho là "doanh nghiệp"). Không có fsyncnhiều thao tác tệp, được sắp xếp theo trực giác và nguyên tử không được đảm bảo như vậy bởi POSIX: các tệp, được mở có O_APPENDthể hoạt động khác với các tệp không có, v.v. Trong thực tế, các khóa quan trọng nhất để thống nhất tệp là hệ thống VFS kernel và bộ đệm đĩa. Mọi thứ khác chủ yếu là lông tơ.
dùng1643723

11

Lưu ý: Tôi chưa xem các nguồn của rsynchoặc bất kỳ tiện ích chuyển tập tin nào khác.

Thật là tầm thường khi viết một chương trình C nhảy phần cuối của tệp và có được vị trí của vị trí đó theo byte.

Cả hai thao tác được thực hiện với một lệnh gọi đến hàm thư viện C tiêu chuẩn lseek()( lseek(fd, 0, SEEK_END)trả về độ dài của tệp được mở cho mô tả tệp fd, được đo bằng byte).

Khi đã xong cho tệp đích, một lệnh gọi tương tự lseek()có thể được thực hiện trên tệp nguồn để chuyển đến vị trí thích hợp : lseek(fd, pos, SEEK_SET). Việc chuyển giao sau đó có thể tiếp tục tại thời điểm đó, giả sử phần trước của tệp nguồn đã được xác định là không thay đổi (các tiện ích khác nhau có thể thực hiện việc này theo các cách khác nhau).

Một tệp có thể bị phân mảnh trên đĩa, nhưng hệ thống tệp sẽ đảm bảo rằng một ứng dụng nhận biết tệp là một chuỗi tuần tự của các byte.


Về thảo luận trong các nhận xét về bit và byte: Đơn vị dữ liệu nhỏ nhất có thể được ghi vào đĩa là một byte . Một byte đơn yêu cầu ít nhất một khối dữ liệu được phân bổ trên đĩa. Kích thước của một khối phụ thuộc vào loại hệ thống tệp và cũng có thể phụ thuộc vào các tham số được quản trị viên sử dụng khi khởi tạo hệ thống tệp, nhưng nó thường nằm trong khoảng từ 512 byte đến 4 KiB. Các thao tác ghi có thể được đệm bởi kernel, thư viện C bên dưới hoặc bởi chính ứng dụng và việc ghi thực tế vào đĩa có thể xảy ra trong bội số của kích thước khối thích hợp dưới dạng tối ưu hóa.

Không thể ghi các bit đơn vào tệp và nếu thao tác ghi không thành công, nó sẽ không để lại "byte nửa viết" trong tệp.


cảm ơn, vậy điều gì đảm bảo nếu một thao tác ghi không thành công - nó sẽ không để lại một nửa byte đã ghi? Có phải đó là muru đệm nhân đã mô tả? - tức là nếu một quá trình bị gián đoạn ở giữa việc gửi một đoạn 8KB đến kernel và bị chấm dứt bất ngờ - đoạn 8KB đó sẽ không bao giờ đến được kernel - nhưng bất kỳ quá trình nào trước đó đạt được kernel và hệ thống tập tin có thể được coi là tốt?
the_velour_fog 6/2/18

6
@the_velour_fog rằng loại chấm dứt bất ngờ không thể xảy ra, bởi vì quá trình này sẽ liên tục ở giữa một cuộc gọi hệ thống I / O (Đó là lý do nó không phải là bất thường để xem quá trình unkillable mắc kẹt trên hệ thống tập tin cuộc gọi truy cập cho một tập tin NFS). Xem thêm: unix.stackexchange.com/q/62697/70524
muru

2
Có thể có vấn đề nếu hệ thống mất điện vào đúng thời điểm. Điều này đôi khi có thể dẫn đến rác tại điểm ghi cuối cùng của tệp. Đó là một vấn đề rất khó khăn trong thiết kế cơ sở dữ liệu. Nhưng vẫn là đơn vị nhỏ nhất bình thường "hợp lệ" hoặc "không hợp lệ" là một khối đĩa.
pjc50

1
@the_velour_fog Không quá nhiều vì bạn không thể nhận được " một nửa byte được viết " (hay chính xác hơn là một khối byte được viết một nửa) dưới dạng một nửa được viết sẽ không được ghi là đã được viết (toàn bộ ) - xem các bước (3) và (4) câu trả lời của LSerni .
TripeHound

5

Về cơ bản, đây là hai câu hỏi, bởi vì các chương trình như curl và rsync rất khác nhau.

Đối với các máy khách HTTP như curl, họ kiểm tra kích thước của tệp hiện tại và sau đó gửi Content-Rangetiêu đề với yêu cầu của họ. Máy chủ sẽ tiếp tục gửi phạm vi của tệp bằng mã trạng thái 206(nội dung một phần) thay vì 200(thành công) và quá trình tải xuống được tiếp tục hoặc nó bỏ qua tiêu đề và bắt đầu từ đầu và máy khách HTTP không có lựa chọn nào khác ngoài tải xuống lại mọi thứ lần nữa.

Hơn nữa máy chủ có thể hoặc không thể gửi Content-Lengthtiêu đề. Bạn có thể nhận thấy rằng một số tải xuống không hiển thị phần trăm và kích thước tệp. Đây là các bản tải xuống mà máy chủ không cho khách hàng biết chiều dài, vì vậy khách hàng chỉ biết số lượng đã tải xuống chứ không phải số lượng byte sẽ theo.

Một số trình quản lý tải xuống sử dụng một Content-Rangetiêu đề với vị trí bắt đầu dừng được sử dụng để tải xuống một tệp từ các nguồn khác nhau cùng một lúc, giúp tăng tốc độ truyền nếu mỗi máy nhân bản của nó chậm hơn kết nối mạng của bạn.

Mặt khác, rsync là một giao thức nâng cao để truyền tệp gia tăng. Nó tạo tổng kiểm tra các phần của tệp trên máy chủ và phía máy khách để phát hiện byte nào giống nhau. Sau đó, nó chỉ gửi sự khác biệt. Điều này có nghĩa là nó không thể chỉ tiếp tục tải xuống, mà thậm chí nó có thể tải xuống các byte đã thay đổi nếu bạn thay đổi một vài byte ở giữa một tệp rất lớn mà không cần tải lại tệp.

Một giao thức khác được thực hiện để tiếp tục chuyển tiền là bittorrent, trong đó .torrenttệp chứa danh sách tổng kiểm tra cho các khối từ tệp, do đó, các khối có thể được tải xuống và xác minh theo thứ tự tùy ý và song song từ các nguồn khác nhau.

Lưu ý rằng rsync và bittorent sẽ xác minh dữ liệu một phần trên đĩa của bạn, trong khi tiếp tục tải xuống HTTP thì không. Vì vậy, nếu bạn nghi ngờ dữ liệu một phần bị hỏng, bạn cần kiểm tra tính toàn vẹn nếu không, tức là sử dụng tổng kiểm tra của tệp cuối cùng. Nhưng chỉ làm gián đoạn quá trình tải xuống hoặc mất kết nối mạng thường không làm hỏng tệp một phần trong khi mất điện trong quá trình chuyển có thể xảy ra.


4

TL; DR: Họ không thể, trừ khi giao thức mà họ sử dụng cho phép.

Các chương trình không thể luôn tiếp tục từ một vị trí tùy ý: ví dụ: các yêu cầu HTTP chỉ có thể khởi động lại nếu máy chủ hỗ trợ và máy khách thực hiện nó: đây không phải là phổ biến, vì vậy hãy kiểm tra tài liệu của chương trình của bạn. Nếu máy chủ hỗ trợ nó, các chương trình có thể tiếp tục chuyển bằng cách yêu cầu như một phần của giao thức. Bạn thường sẽ thấy chuyển một phần trong thư mục tải xuống của mình (chúng thường được đánh dấu bằng tiện ích mở rộng ".partial" hoặc một cái gì đó tương tự.)

Nếu quá trình tải xuống tệp bị tạm dừng hoặc tạm dừng, khách hàng có thể ghi tệp vào đĩa và có một ý tưởng nhất định về nơi tiếp tục. Mặt khác, nếu máy khách gặp sự cố hoặc có lỗi ghi vào tệp, máy khách phải cho rằng tệp bị hỏng và bắt đầu lại. BitTorrent phần nào giảm thiểu điều này bằng cách chia nhỏ các tệp thành "khối" và theo dõi những tệp nào đã được tải xuống thành công; hầu hết những gì nó sẽ phải làm lại là một vài khối. Rsync làm một cái gì đó tương tự.

Làm thế nào để các chương trình biết rằng nội dung là giống nhau? Một phương pháp là xác minh rằng một số định danh giống nhau giữa máy khách và máy chủ. Một số ví dụ về điều này sẽ là dấu thời gian và kích thước, nhưng có những cơ chế có thể dành riêng cho một giao thức. Nếu các định danh khớp, thì khách hàng có thể cho rằng việc tiếp tục sẽ hoạt động.

Nếu bạn muốn xác minh rõ ràng hơn, HTTP và bạn bè không nên là lựa chọn đầu tiên của bạn. Bạn sẽ muốn sử dụng một giao thức cũng có tổng kiểm tra hoặc hàm băm cho toàn bộ tệp và từng đoạn được chuyển đổi để bạn có thể so sánh tổng kiểm tra tải xuống với tổng kiểm tra máy tính của máy chủ: mọi thứ không khớp sẽ được tải xuống lại. Một lần nữa, BitTorrent là một ví dụ về loại giao thức này; rsync có thể tùy chọn làm điều này quá.


đối với ví dụ rsync, nó sẽ đơn giản vì chỉ có một giao thức rsync. để tải xuống http, có một yêu cầu phạm vi như là một tiêu chuẩn. Tôi tò mò muốn biết curl thực sự làm gì khi tải lên sơ yếu lý lịch, bởi vì ngữ nghĩa chuẩn của tải lên là nhiều dữ liệu / biểu mẫu (cho wget và curl), nhưng tôi không tin rằng ngữ nghĩa tải lên sơ yếu lý lịch được đồng ý. Chẳng hạn, YouTube và Nginx có thể làm điều này khác nhau.
Cướp

1

Phụ thuộc vào giao thức được sử dụng để chuyển. Nhưng curl sử dụng http và nó chuyển dữ liệu tuần tự theo thứ tự xuất hiện trong tệp. Vì vậy, curl có thể tiếp tục dựa trên kích thước tập tin của một lần chuyển hoàn thành một phần. Trong thực tế, bạn có thể lừa nó bỏ qua N byte đầu tiên bằng cách tạo một tệp có độ dài N (của bất cứ thứ gì) và yêu cầu nó xử lý tệp đó dưới dạng tải xuống hoàn thành một phần (và sau đó loại bỏ N byte đầu tiên).

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.