Чи буде буфер автоматично змиватися на диск, коли процес закінчується?


21

Коли я перенаправляю висновок команди у файл (наприклад, echo Hello > file), чи гарантовано буде мати такі дані відразу після виходу команди? Або все ще є дуже маленьке вікно між виходами команди та записаними у файл даними? Я хотів би прочитати файл відразу після завершення команди, але я не хочу читати порожній файл.


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

З точки зору наведеного прикладу, що таке «процес»? Чи є, echoа >не окремими (недовговічними) процесами? І куди виконується вихід echoзалишку перед >?
oɔɯǝɹ

1
@ oɔɯǝɹ >- перенаправлення оболонки. Це так само, як якщо б програма відкрила названий файл для запису і замінила stdout на нього, що саме робить оболонка.
Ден Д.

7
Я думаю, що відповідальність за операційну систему повинна надавати вам fileвміст Helloнезалежно від того, змивається він чи ні.
Салман А

1
Якщо програма працює на машині A, а ви читаєте файл на машині B, з файловою системою машини A, встановленою через мережу, ви можете закінчити читання порожнього файлу, залежно від типу мережевої файлової системи та налаштувань кріплення. Тому ви можете відключити кешування для цього кріплення.
пт

Відповіді:


21

Існує кілька шарів буферів / кеш-пам'яток.

  1. Кеш процесора.

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

  2. Вбудовані буфери.

    У процесі збирання даних є деяка частина пам'яті, тому нам потрібно зробити якомога менше запитів до ОС, оскільки це порівняно дорого. Процес копіює дані в ці буфери, які знову можуть бути підкріплені кешами процесора, тому немає гарантії, що дані будуть скопійовані в основну пам'ять. Програмі потрібно явно очистити ці буфери, наприклад, використовуючи fclose (3) або fsync (3). Функція exit (3) також робить це до завершення процесу, тоді як функція _exit (2) не робить , тому на сторінці керівництва для цієї функції є велике попередження, щоб викликати її, лише якщо ви знаєте, що ви є робити.

  3. Буфери ядра

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

  4. Кеш диска

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

Для вашої програми достатньо, щоб дані були зареєстровані в буферах ядра (фактичні дані все ще можуть жити в кешах процесора в цей момент і, можливо, не були записані в основну пам'ять): процес "ехо" закінчується, що означає, що будь-які вбудовані буфери повинні бути очищені, а дані передані в ОС, і коли ви запускаєте новий процес, тоді гарантується, що ОС поверне ті самі дані, коли запитає.


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

3
Дійсно, кеш процесора досить ортогональний.
Саймон Ріхтер

2
І що ще важливіше, кеш процесора є когерентним між ядрами, тому це абсолютно не видно з картини. На x86 вона навіть є когерентною з DMA (а x86 має режим упорядкування пам'яті загальним замовленням), тому все, що може прочитати пам'ять, побачить дані, які останнім часом зберігаються за цією адресою, у глобальному порядку операцій з пам'яттю. (Ядро процесора побачить власні магазини ще до того, як вони стануть видимими у всьому світі, через переадресацію магазину з черги магазинів). На платформах, що не мають x86, без керованої кешами DMA, ядро ​​Linux гарантує, що кеш переноситься перед DMA на ці адреси.
Пітер Кордес

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

1
Пропуски / хіти @Sam кеша разом зі спекулятивним виконанням можуть використовуватися в деяких процесорах для обходу обмежень доступу для читання. Можливо, саме на це йдеться у відповіді?
Джон Дворак

22

Якщо у додатку немає внутрішніх кеш-пам'яток, то зміни будуть негайно записані у файл. Те саме для вашого прикладу. Файл - це логічне утворення в пам'яті, яке буде негайно оновлено. Будь-які наступні операції над файлом побачать зміни, внесені програмою.

Однак це не означає, що зміна була записана на фізичний диск. Зміни можуть затримуватися в кешах файлової системи ОС або кеш-пам'яті обладнання. Для промивання буферів файлової системи використовуйте syncкоманду.

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

Тут не слід стикатися з будь-якими практичними проблемами.


1
"Якщо в додатку немає внутрішніх кеш-пам'яток" - це дуже велике "якщо": переважна більшість реалізацій бібліотеки вводу-виводу за замовчуванням використовує буферну версію. При цьому, наприклад, стандарт C вимагає, щоб буфер stdout був вимитий при виході (але, можливо, ні, якщо exitйого принаймні неявно не називають). Інші бібліотеки / мови (наприклад, Java!) Дають менші гарантії.
Конрад Рудольф

Що робити, якщо просто обмежити це примітивом переадресації (тобто командою в моєму питанні)? У нього немає внутрішніх кешів, правда?
Ерік

@Eric Ні, ти повинен бути добре.
mtak

10
Я не впевнений, чи отримаю цю відповідь. Питання йдеться про те, "коли процес закінчується". Кожна програма із внутрішніми кешами запису передає їх на диск при виході з процесу, якщо цього не сталося раніше. IOW, ці кеші тут не мають значення.
MSalters

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

21

Чи буде буфер автоматично змиватися на диск, коли процес закінчується?

Загалом відповідь - ні .

Це залежить від команди. Як і інші відповіді, згадується, що якщо команда не завантажує внутрішньо дані, всі дані будуть доступні, коли команда припиняється.

Але більшість, якщо не всі, стандартні бібліотеки вводу-виводу роблять буферні вибудовування за замовчуванням (певною мірою) і дають різні гарантії щодо автоматичного промивання буферів, коли програма закривається.

C гарантує, що нормальний вихід змиває буфери . "Нормальний вихід" означає, що exitвикликається - явно, або повертаючись з main. Однак ненормальний вихід може обійти цей виклик (і тому залишити незамкнутими буфери позаду).

Ось простий приклад:

#include <signal.h>
#include <stdio.h>

int main() {
    printf("test");
    raise(SIGABRT);
}

При компіляції цього і виконати його, testбуде НЕ обов'язково бути записані на стандартний висновок.

Інші мови програмування дають ще менші гарантії: наприклад, Java не закінчується автоматичним змиванням після закінчення програми . Якщо вихідний буфер містить незавершений рядок, він може, таким чином, втрачатися, якщо тільки не System.out.flush()було викликано явно.

Тим НЕ менше, ваше тіло питання питає що - то трохи інше: якщо дані надходять в файл на всіх , він повинен зробити це відразу ж після команди припиняється ( при умови застережень , описаних в інших відповідях).


7
Я також бачив ненормальний вихід, коли інструмент командного рядка записує у файл і stdout або stderr, як журнал налагодження, і користувач зробив трубку head або менше, а потім набрав 'q', щоб менше вийти. Файл диска не завжди повністю очищений, якщо інструмент командного рядка не обробляв SIGPIPE.
Зан Лінкс

+1, але "це слід зробити відразу після завершення команди" не зовсім правильно: будь-який write()або pwrite()системний виклик відбудеться до завершення процесу, і тоді зміни файлу стануть видимими. Тож остання зміна файлу, безумовно, до завершення процесу, безпосередньо-раніше, не пізніше. Я думаю, що навіть із mmap(MAP_SHARED)файлом, немає жодного способу спостерігати за тим, як завершення процесу відбувається до того, як відбудуться всі зміни файлу.
Пітер Кордес

9

Я думаю, що жодне питання ще не вирішує це питання достатньо:

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

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

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

  • Якщо процес читає файл, ядро ​​подасть вміст буфера процесу, якщо запитувана частина файлу в даний час знаходиться в буфері; якщо це не так, ядро ​​отримає дані з базового носія інформації та розмістить його всередині буфера, а потім повернеться до попереднього кроку.

  • Якщо процес записує у файл, дані спочатку поміщаються всередину в буфер ядра для цього файлу. Згодом вміст буфера буде передано у сховище. Тим часом доступ до читання задовольняється з того самого буфера (див. Вище).


1 Принаймні для звичайних файлів, каталогів та символічних посилань. FIFO та розетки - це інша справа, оскільки їхній вміст ніколи не зберігається постійно. Є окремі випадки звичайних файлів, вміст яких залежить від того, хто запитує; приклади - файли у procfs та sysfs (подумайте, /proc/selfщо є символічним посиланням на ідентифікатор процесу процесу, що читає символьне посилання).


2
Строго кажучи, це не семантика файлової системи Linux, що це гарантує, це POSIX семантика. Зокрема, BSD поводиться точно так само, як і macOS і навіть Windows (хоча це один з небагатьох випадків, коли Windows дотримується семантики POSIX). Це також передбачає, що ніхто не робить дивні речі з mmap()O_DIRECT, що може призвести до того, що речі не синхронізуються між диском і кешем сторінки (але це вирішить момент, коли процес, який виконує, закінчується).
Остін Хеммельгарн

2
@AustinHemmelgarn: Власне кажучи, ми обидва праві, оскільки Linux був розроблений з підтримкою додатків Unix (System V) на увазі, а пізніше зроблений для підтримки POSIX, який також заснований на багатьох концепціях System V.
Девід Фоерстер,

5

Якщо припустити, що ваша команда виконується якоюсь програмою за допомогою бібліотеки виконання C, в якийсь момент вона повинна викликати, fcloseщоб закрити відкритий файл.

Сторінка man для fcloseфункції C говорить:

ПРИМІТКИ Зауважте, що fclose () промиває лише буфери простору користувача, надані бібліотекою C. Щоб забезпечити фізичне зберігання даних на диску, буфери ядра також повинні бути очищені, наприклад, синхронізацією (2) або fsync (2).

і сторінка man для fflushмає однакову ноту. Сторінка людини для close:

Успішне закриття не гарантує, що дані були успішно збережені на диску, як ядро ​​відкладає. У файловій системі не звичайно промивати буфери, коли потік закритий. Якщо вам потрібно бути впевненим, що дані зберігаються фізично, використовуйте функцію fsync (2). (Це буде залежати від апаратного забезпечення диска в цей момент.)

Зауважте, що дані доступні для інших процесів, навіть якщо вони не синхронізовані з диском. Можливо, це вже досить добре для вас.

Якщо ви сумніваєтесь, напишіть тест.


2
C чи ні, все буде / повинно використовувати close()syscall для закриття дескриптора файлу.
Attie

@Attie: Вам не потрібно подавати closeфайли перед виходом (у хакі-програмах, які не перевіряють на помилки); ядро очистить їх, ефективно закликаючи closeвас після того, як ваш процес загине. Вам потрібні fcloseбудь-які завантажені stdio потоки, хоча або дозвольте libc зробити це для вас exit(3), на відміну від виклику системи виходу безпосередньо.
Пітер Кордес

Якщо ви сумніваєтесь, напишіть тест. Це погана порада для виявлення умов перегонів. Тестування на одному ядрі, що працює на одному апаратному забезпеченні, може сказати вам, що гонка не може відбутися за програмних умов, вироблених вашим тестом у цій системі, або якщо це зробити, це виявляється занадто рідко. Але він не може сказати, чи є це поведінка передбачається бути безпечними у всіх файлових системах, ядер, і все обладнання (наприклад , PowerPC). тобто ви не можете сказати, чи є гарантія, від якої ви залежите, детальна інформація про впровадження чи навмисна гарантія у майбутньому! (У даному випадку це так.)
Пітер Кордес

Це залежить від ситуації. Деякі люди, які намагаються запустити його сценарій оболонки, можуть допомогти цією порадою. Він не розглядався як загальне рішення для більш прогресивних, але менш вірогідних середовищ, наприклад, інженера-програмного забезпечення, що працює над ядром ОС, когось із людей, що працюють над оновленням мікрокоду Intel, або іншого, який виконує роботу над якоюсь системою для МКС.
mvw

3

Коли я перенаправляю висновок команди у файл (наприклад, echo Hello > file), чи гарантовано буде мати такі дані відразу після виходу команди?

Так. Оболонка відкриває вихідний файл і echoвиводить його безпосередньо. Після того, як команда завершиться, вона виконана.

Або все ще є дуже маленьке вікно між виходами команди та записаними у файл даними?

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

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

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


"ядро зберігає лише один погляд на файл": не зовсім вірно для mmap(MAP_SHARED): сховища в mmaped області не є когерентними з читаннями файлу (за допомогою цього потоку або інших процесів). Ось чому msync(2)існує. Принаймні, про це попереджають сторінки чоловіка; Залежно від реалізації, Linux може насправді відображати фізичні сторінки з кеш-сторінки, і в цьому випадку я б здогадався, що вона в основному є когерентною (впорядкованість по модулю). У всякому разі, це все ще відбувається раніше _exit(2).
Пітер Кордес

2

Як правило, будь-які дані, що належать ядру , підтримуються та очищаються ядром, період. Такі дані включають дані, передані в пам'ять ядра системним викликом, таким як write(2).

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

Більше того, я не вірю, що є гарантія часу на прибирання - вона, як правило, виконується на основі "найкращих зусиль" (читай: "коли у мене є сек").


Існує гарантія, що будь-яке очищення / промивання буфера відбудеться до waitpid()повернення батьківського процесу , якщо очищення відбудеться взагалі. тобто інші процеси не можуть безпосередньо спостерігати за тим, як припинення процесу відбудеться перед будь-якими модифікаціями файлів, виконаними цим процесом. (Я сказав "прямо", щоб виключити непряме спостереження через часові позначки файлів NFS, оскільки кешування NFS не є ідеально когерентним між хостами.)
Пітер Кордес,

@PeterCordes: Я думаю, це залежить від того, що ви маєте на увазі під «очищенням» на відміну від «утримання». Для мене "підтримка" - це "надати узгоджений вигляд" (на який є гарантія, про яку ви згадали), а "очищення" - це "змивання з диском", на який я не вірю, що має гарантію часу.
Мехрдад

О, я бачу, ви відповідаєте на "змиту з дисками" частину питання, що не має значення для того, які подальші процеси побачать під час читання файлу. "очистити" в значенні "очистити брудну пам'ять кешу / буфера чистими". Правильно, жодна гарантія на терміни, якщо ви не використовуєте fsync/ fdatasync, хоча списання буфера в Linux почнеться через /proc/sys/vm/dirty_writeback_centisecsсоті секунди (якщо не затримується іншим трафіком вводу-виводу), а також різні інші настройки в каталозі procfs також впливають на речі (наприклад, як великі, щоб буфери зростали перед тим, як робити будь-яке записування).
Пітер Кордес

2

Або все ще є дуже маленьке вікно між виходами команди та записаними у файл даними?

Ні, немає.

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

Ви можете прочитати остаточний вміст файлу одразу після того, як команда закінчиться, а ви ніколи не будете читати порожній файл. (У C і C ++ використовуйте системні виклики wait , waitpid , wait3 або wait4, щоб зачекати, поки програма вийде, і лише потім прочитайте файл. Якщо ви використовуєте оболонку, іншу мову програмування або бібліотеку (наприклад, бібліотека C система викликів або клас Java Process ), ймовірно, вже використовується один із цих системних викликів.)

Як показали інші відповіді та коментарі, ви можете закінчити читання порожнього файлу після виходу з програми, якщо програма вийшла, не змиваючи її внутрішні вихідні буфери (наприклад, через _exit , переривання або отримання фатального сигналу або тому, що це програма Java, яка нормально виходить). Однак ви нічого не можете зробити з цим на даний момент: неочищені дані втрачаються назавжди, додаткове очікування не відновить їх.


0

Так

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

Коли я переспрямую висновок команди у файл (наприклад, echo Hello> файл), чи гарантовано у файлу такі дані відразу після закінчення команди?

Так, безумовно. Використання ">", яке ви описуєте, а також "|" та "<" - це модель обробки на основі труб, на якій ґрунтується світ Unix та Linux. Ви знайдете сотні, якщо не тисячі сценаріїв, повністю залежно від такої поведінки в кожній установці Linux.

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


На жаль, це зайве. Лише пара відповідей здебільшого зосереджена на рудуванні оселедця, що здійснює збирання даних до енергонезалежного сховища. Дивіться відповідь @ pts та кілька інших, щоб отримати чіткий опис: модифікація файлу відбувається перед виходом, або взагалі немає.
Пітер Кордес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.