Як програми, які можуть відновити помилкові передачі файлів, знають, з чого почати додавання даних?


23

Деякі програми копіювання файлів на зразок rsyncта curlмають можливість відновити невдалі передачі / копії.

Відзначаючи, що причин цих збоїв може бути багато, в деяких випадках програма може зробити «очищення», а в деяких випадках програма не може.

Коли ці програми поновлюються, вони, здається, просто обчислюють розмір файлу / даних, який був успішно переданий, і просто починають читати наступний байт з джерела та додавати його до фрагмента файлу.

наприклад, розмір фрагмента файлу, який "зробив його" до місця призначення, становить 1378 байт, тому вони просто починають читати з байту 1379 на оригіналі та додавати до фрагменту.

Моє запитання полягає в тому, що, знаючи, що байти складаються з бітів, і не всі файли мають свої дані сегментовані в чисті шматки байтів, як ці програми знають, що вони вирішили обрати початок додавання даних до правильних?

Під час написання цільового файлу відбувається якесь буферизація або "транзакції", подібні до баз даних SQL, або на рівні програми, ядра або файлової системи, щоб гарантувати, що лише чисті, добре сформовані байти переходять у базовий блок пристрою?
Або програми припускають, що останній байт може бути неповним, тому вони видаляють його за умови, що він поганий, копіюють байт і починають додавати звідти?

знаючи, що не всі дані представлені як байти, ці здогадки здаються невірними.

Коли ці програми "відновляться", як вони знають, що вони починаються в потрібному місці?


21
"не всі файли мають свої дані сегментовані в чисті шматки байтів", яких вони не мають? Як ви можете написати щось менше, ніж байт у файл?
муру

17
Я не знаю жодних системних викликів, які могли б написати що-небудь менше, ніж байт, а що стосується самого диска, я думаю, що сьогодні жоден диск не пише менше 512 байт-блоків (або 4096 байт-блоків).
муру

8
Ні, я кажу, що мінімум - байт. Додаткові програми будуть використовувати шматки 4 КБ або 8 КБ:, head -c 20480 /dev/zero | strace -e write tee foo >/dev/nullі тоді ОС буде їх буферувати і відправляти на диск ще більшими шматками.
муру

9
@the_velour_fog: Як ви пишете лише один біт fwrite()?
psmears

9
Для всіх практичних цілей, даних буде складено з байтів , і все працює з ними як найменша одиниця. Деякі системи (в основному стосуються стиснення, наприклад, gzip, h264) розпаковують окремі біти з байтів, але операційна система та оперативна пам'ять знаходяться на рівні байтів.
pjc50

Відповіді:


40

Для наочності - справжня механіка складніша для забезпечення ще кращої безпеки - ви можете уявити собі операцію запису на диск таким чином:

  • додаток пише байти (1)
  • ядро (та / або файлова система IOSS) буферизує їх
  • коли буфер заповнений, він отримує почервонів до файлової системи:
    • блок виділений (2)
    • блок записаний (3)
    • інформація про файл та блок оновлена ​​(4)

Якщо процес переривається в (1), ви нічого не отримаєте на диску, файл недоторканий і усічений у попередньому блоці. Ви надіслали 5000 байт, лише 4096 на диску, ви перезапустите передачу при зміщенні 4096.

Якщо в (2), нічого не відбувається, крім пам’яті. Те саме, що (1). Якщо в (3), дані записуються, але про них ніхто не пам’ятає . Ви надіслали 9000 байт, 4096 написали, 4096 написали та втратили , решту просто загубили. Передача поновлюється з компенсацією 4096.

Якщо в (4), дані тепер мали бути зроблені на диску. Наступні байти в потоці можуть бути втрачені. Ви надіслали 9000 байт, 8192 записуються, решта втрачається, передача поновлюється при зміщенні 8192.

Це спрощений прийом. Наприклад, кожне "логічне" записування на етапах 3-4 не є "атомним", але породжує іншу послідовність (давайте пронумеруємо його №5), завдяки чому блок, підрозділений на підблоки, придатні для пристрою призначення (наприклад, жорсткий диск ) надсилається на хост-контролер пристрою, який також має механізм кешування , і, нарешті, зберігається на магнітній платі. Ця підпослідовність не завжди повністю перебуває під контролем системи, тому надсилання даних на жорсткий диск не є гарантією того, що вони були фактично записані і будуть читабельні назад.

Кілька файлових систем реалізують журнал , щоб переконатися, що найбільш вразлива точка (4) насправді не є вразливою, записуючи метадані в, як ви здогадалися, транзакції, які працюватимуть послідовно, що б сталося на етапі (5).

Якщо система перезавантажується в середині транзакції, вона може відновити шлях до найближчої непошкодженої контрольної точки. Записані дані все ще втрачаються, як і у випадку (1), але відновлення піклується про це. Немає інформації на самому ділі не втрачається.


1
Чудове пояснення. що все має багато сенсу. Так що якщо процес робить його повністю до (4) інформації про блок файлів оновленою, ви знаєте, що всі ці байти хороші. то будь-які байти, які були на будь-якому попередньому етапі, або не внесли його на диск, або - якщо вони це зробили - вони будуть "не запам’ятовуються" (жодних посилань на них)
the_velour_fog

4
@the_velour_fog І лише для доповнення передостаннього абзацу - якщо ви використовуєте файлову систему, яка не реалізує журнал, ви дійсно можете отримати "зламані" дані, що спричинить збій резюме та створить сфабрикований файл, не надаючи вам помилок. Це раніше відбувалося, особливо з файловими системами, розробленими для пристроїв з високою затримкою (наприклад, дискети). Існували ще деякі хитрощі, щоб цього уникнути, навіть якщо файлова система не була надійною таким чином, але для компенсації та припущень, які, можливо, помилялися в деяких системах, вона потребувала розумнішої програми.
Луань

Ця відповідь завищує корисність ведення журналів у файлових системах. Це не працює надійно, якщо все не реалізує трансакційну семантику, включаючи програми для користувальницького простору (через fsync) та контролер жорсткого диска (часто зламаний, навіть у нібито «корпоративних» накопичувачах). Без fsyncбагатьох операцій з файлами, які інтуїтивно впорядковані та атомні, не гарантується, що вони є POSIX: файли, відкриті з, O_APPENDможуть поводитись інакше, ніж файли без і т.д. Все інше - це в основному пух.
user1643723

11

Примітка. Я не переглядав джерела rsyncабо будь-яку іншу утиліту передачі файлів.

Тривіально написати програму C, яка стрибає кінець файлу і отримує положення цього місця в байтах.

Обидві операції виконуються одним викликом до стандартної функції бібліотеки С lseek()( lseek(fd, 0, SEEK_END)повертає довжину файлу, відкритого для дескриптора файлу fd, вимірюється в байтах).

Як тільки це буде зроблено для цільового файлу, подібний виклик lseek()може бути зроблений на вихідний файл , щоб перейти до відповідного положення: lseek(fd, pos, SEEK_SET). Потім передача може продовжуватися в цьому пункті, припускаючи, що попередня частина вихідного файлу була визначена як незмінена (різні утиліти можуть робити це по-різному).

Файл може бути фрагментований на диску, але файлова система забезпечить, щоб програма сприймала файл як послідовну послідовність байтів.


Щодо обговорення в коментарях щодо бітів і байтів: Найменша одиниця даних, яка може бути записана на диск, - це байт . Один байт вимагає, щоб принаймні один блок даних був розподілений на диску. Розмір блоку залежить від типу файлової системи та, можливо, також від параметрів, які використовує адміністратор при ініціалізації файлової системи, але зазвичай це десь між 512 байтами і 4 КБ. Операції запису можуть бути буферовані ядром, базовою бібліотекою С або самим додатком, і власне запис на диск може відбуватися в кратних розмірах відповідного розміру блоку як оптимізація.

Неможливо записати одиничні біти у файл, і якщо операція запису не вдасться, вона не залишить у файлі "напівзаписаних байтів".


дякую, так що ж це гарантує, якщо операція запису не вдасться - вона не залишить половини написаних байтів? це описував муру буферизації ядра? - тобто якщо процес переривається в середині відправлення фрагмента 8 КБ до ядра і припиняється несподівано - що шматок 8 КБ ніколи не дістанеться до ядра - але будь-які попередні, які дійшли до ядра та файлової системи, можна вважати хорошими?
the_velour_fog

6
@the_velour_fog такого несподіваного припинення не може відбутися, оскільки процес буде безперебійним посеред системного виклику вводу / виводу (саме тому незвично бачити нерозбірливий процес, що застряг у викликах доступу до файлової системи для файлу NFS). Також дивіться: unix.stackexchange.com/q/62697/70524
муру

2
Можуть виникнути проблеми, якщо система втрачає живлення точно в неправильний час. Це час від часу може спричинити сміття в останній точці запису файлу. Це дуже складна проблема в дизайні баз даних. Але нормальний найменший блок, який є "дійсним" або "недійсним", є блоком дисків.
pjc50

1
@the_velour_fog Це не так багато, як ви не можете отримати " наполовину записаних байтів " (або, точніше, напівзаписаний блок байтів), оскільки напівзаписаний блок не буде записаний як записаний (у повному обсязі) ) - див. кроки (3) та (4) відповіді Л.Серні .
TripeHound

5

Це в основному два питання, тому що такі програми, як curl і rsync, сильно відрізняються.

Для клієнтів HTTP, таких як curl, вони перевіряють розмір поточного файлу, а потім надсилають Content-Rangeзаголовок зі своїм запитом. Сервер або відновлює надсилання діапазону файлів, використовуючи код статусу 206(частковий вміст), а не 200(успіх), і завантаження поновлюється, або він ігнорує заголовок і починається з початку, а HTTP-клієнт не має іншого вибору, ніж повторно завантажити все знову.

Далі сервер може або не може надсилати Content-Lengthзаголовок. Можливо, ви помітили, що деякі завантаження не показують відсоток і розмір файлів. Це завантаження, коли сервер не повідомляє клієнту довжину, тому клієнт знає лише суму, яку він завантажив, але не кількість байтів.

Використання Content-Rangeзаголовка з позицією запуску та зупинки використовується деяким менеджером завантажень для завантаження файлу відразу з різних джерел, що прискорює передачу, якщо кожне дзеркало саме по собі повільніше, ніж ваше мережеве з'єднання.

rsync, з іншого боку, є вдосконаленим протоколом для поступових передач файлів. Він генерує контрольні суми частин файлу на стороні сервера та клієнта, щоб виявити, які байти однакові. Тоді він лише посилає відмінності. Це означає, що він не може відновити завантаження, але навіть може завантажити змінені байти, якщо ви змінили кілька байтів у середині дуже великого файлу, не повторно завантажуючи файл.

Ще один протокол, створений для відновлення передачі, - це бітторент, де .torrentфайл містить перелік контрольних сум для блоків з файлу, тому блоки можна завантажувати та перевіряти у довільному порядку та паралельно з різних джерел.

Зауважте, що rsync та bittorent перевірять часткові дані на вашому диску, тоді як відновлення завантаження HTTP не буде. Отже, якщо ви підозрюєте, що часткові дані були пошкоджені, вам потрібно перевірити цілісність інакше, тобто за допомогою контрольної суми остаточного файлу. Але просто переривання завантаження або втрата мережевого з'єднання зазвичай не пошкоджує частковий файл, в той час як відключення живлення під час передачі може статися.


4

TL; DR: Вони не можуть, якщо протокол, який вони використовують, не дозволяє.

Програми не завжди можуть відновитись з довільного місця розташування: наприклад, HTTP-запити перезавантажуються лише у тому випадку, якщо сервер підтримує його та клієнт реалізує: це не є універсальним, тому перегляньте документацію програми. Якщо сервер його підтримує, програми можуть відновити передачу, просто запитавши їх як частину протоколу. Зазвичай ви побачите часткові передачі у каталозі завантажень (вони зазвичай позначені розширенням ".partial" або чимось подібним.)

Якщо завантаження файлів призупинено або припинено іншим способом, клієнт може записати файл на диск і мати чітке уявлення про те, де відновити. Якщо, з іншого боку, клієнт виходить з ладу або є помилка запису у файл, клієнт повинен припустити, що файл пошкоджений, і почати його заново. BitTorrent дещо пом’якшує це, розбиваючи файли на "шматки" та відслідковуючи, які з них успішно завантажені; найбільше, що коли-небудь доведеться переробити, - це кілька шматочків. Rsync робить щось подібне.

Як програми знають, що вміст однаковий? Один із способів полягає у перевірці того, що якийсь ідентифікатор однаковий між клієнтом та сервером. Деякі приклади цього можуть бути часова мітка та розмір, але є механізми, які можуть бути специфічними для протоколу. Якщо ідентифікатори відповідають, клієнт може припустити, що відновлення запрацює.

Якщо ви хочете більш чітко підтвердити, HTTP та друзі не повинні бути вашим першим вибором. Ви хочете використовувати протокол, який також має контрольну суму чи хеш для всього файлу та кожного перекладеного фрагменту, щоб ви могли порівняти контрольну суму завантаження з контрольною сумою комп’ютера сервера: все, що не відповідає, буде потім повторно завантажено. Знову ж таки, BitTorrent є прикладом такого роду протоколів; rsync може також це зробити.


для прикладу rsync це буде просто, тому що існує лише один протокол rsync. для завантаження http, стандартним є запит на діапазон. Мені цікаво знати, що насправді робить curl при повторному завантаженні, тому що стандартна семантика завантаження - це багаточастинні / форми-дані (для wget і curl), але я не вірю, що семантика відновлення завантаження поширюється загально. Наприклад, YouTube і Nginx можуть робити це по-різному.
Роб

1

Залежить від протоколу, який використовується для передачі. Але curl використовує http, і він передає дані послідовно в тому порядку, в якому вони відображаються у файлі. Таким чином, згортання може відновитись на основі розміру файлу частково завершеної передачі. Насправді, ви можете виправдати це, щоб пропустити перші N байтів, створивши файл довжиною N (нічого) і попросивши його розглянути цей файл як частково завершене завантаження (а потім відкинути перші N байтів).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.