Skip to main content
LibreTexts - Ukrayinska

3.4: Малі та великі системи

  • Page ID
    34499
    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)

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

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

    Симетричний термін позначає той факт, що всі процесори в системі однакові (наприклад, архітектура, тактова частота). У системі SMP є кілька процесорів, які поділяють інші всі інші системні ресурси (пам'ять, диск і т.д.).

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

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

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

    Одним з протоколів для цього є протокол MOESI; що стоїть за Модифікований, Власник, Ексклюзивний, Загальний, Недійсний. Кожен з них - це стан, в якому може перебувати рядок кешу на процесорі в системі. Є й інші протоколи для того, щоб зробити стільки ж, проте всі вони поділяють схожі поняття. Нижче ми розглянемо MOESI, щоб ви мали уявлення про те, що тягне за собою процес.

    Коли процесор вимагає читання рядка кешу з основної пам'яті, він спочатку повинен стежити за всіма іншими процесорами в системі, щоб побачити, чи знають вони що-небудь про цю область пам'яті (наприклад, кешування). Якщо його немає ні в одному іншому процесі, то процесор може завантажити пам'ять в кеш і позначити її як ексклюзивну. Коли він записує в кеш, він потім змінює стан, який потрібно змінити. Тут вступають в дію конкретні деталі кешу; деякі кеші негайно запишуть змінений кеш в системну пам'ять (відомий як кеш запису, оскільки записи потрапляють до основної пам'яті). Інші не будуть, і залишати змінене значення тільки в кеші, поки він не буде виселений, коли кеш стане заповненим наприклад.

    Інший випадок, коли процесор підглядає і виявляє, що значення знаходиться в кеші інших процесорів. Якщо це значення вже було позначено як змінене, воно скопіює дані у власний кеш і позначить їх як спільні. Він надішле повідомлення для іншого процесора (від якого ми отримали дані), щоб позначити його рядок кешу як власника. Тепер уявіть, що третій процесор в системі хоче використовувати цю пам'ять теж. Він буде підглядати і знайти як спільну, так і копію власника; таким чином, він буде приймати свою цінність від вартості власника. Поки всі інші процесори читають лише значення, рядок кешу залишається спільним у системі. Однак, коли одному процесору потрібно оновити значення, він надсилає недійсне повідомлення через систему. Будь-які процесори з цим рядком кешу повинні потім позначити його як недійсний, оскільки він більше не відображає значення «true». Коли процесор надсилає недійсне повідомлення, він позначає рядок кешу як модифікований у своєму кеші, а всі інші будуть позначені як недійсні (зверніть увагу, що якщо рядок кешу виключна, процесор знає, що жоден інший процесор не залежить від нього, тому можна уникнути надсилання визнати недійсним повідомлення).

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

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

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

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

    У розділі під назвою «Cache in depth» ми описали inclusive v ексклюзивні кеші. Як правило, кеші L1 зазвичай є інклюзивними - тобто всі дані в кеші L1 також знаходяться в кеші L2. У багатопроцесорної системі включний кеш L1 означає, що тільки кеш L2 потребує трафіку пам'яті snoop для підтримки узгодженості, так як будь-які зміни в L2 гарантовано будуть відображені L1. Це зменшує складність L1 і від'єднує його від процесу стеження, що дозволяє йому бути швидшим.

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

    Значна частина часу сучасного процесора витрачається на очікування набагато повільніших пристроїв в ієрархії пам'яті для доставки даних для обробки.

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

    Хоча кожен процесор має свої регістри, вони все одно повинні ділитися основною логікою, кешем та пропускною здатністю введення та виведення від процесора до пам'яті. Таким чином, хоча два потоки інструкцій можуть тримати основну логіку процесора зайнятіше, збільшення продуктивності не буде таким великим, що має два фізично окремих процесорів. Зазвичай покращення продуктивності нижче 20% (перевірка XXX), однак воно може бути значно кращим або гіршим залежно від навантажень.

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

    Хоча зазвичай процесори мають власний кеш L1, їм доводиться ділити шину, що підключається до основної пам'яті та інших пристроїв. Таким чином, продуктивність не така велика, як повноцінна система SMP, але значно краще, ніж система гіперпоточності (насправді кожне ядро все ще може реалізувати гіперпоточність для додаткового вдосконалення).

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

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

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

    Програмне забезпечення, таке як MPI, дозволяє програмістам писати своє програмне забезпечення, а потім «фарм» частини програми на інші комп'ютери в системі. Наприклад, образ циклу, який виконує кілька тисяч разів, виконуючи незалежну дію (тобто ніяка ітерація циклу не впливає на будь-яку іншу ітерацію). З чотирма комп'ютерами в кластері програмне забезпечення може змусити кожен комп'ютер робити 250 циклів кожен.

    Взаємозв'язок між комп'ютерами варіюється і може бути настільки ж повільним, як інтернет-зв'язок, або настільки ж швидко, як спеціальні спеціальні шини (Infiniband). Незалежно від взаємозв'язку, однак, він все ще буде далі вниз по ієрархії пам'яті і набагато, набагато повільніше, ніж оперативна пам'ять. Таким чином, кластер не буде добре працювати в ситуації, коли кожен процесор вимагає доступу до даних, які можуть зберігатися в оперативній пам'яті іншого комп'ютера; оскільки кожен раз, коли це відбувається, програмне забезпечення потрібно буде запитувати копію даних з іншого комп'ютера, скопіювати через повільне посилання і в локальну оперативну пам'ять перед процесором. може отримати будь-яку роботу.

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

    Ще одним додатком є рендеринг зображень, особливо для спецефектів у фільмах. Кожному комп'ютеру можна передати один кадр фільму, який містить каркасні моделі, текстури та джерела світла, які повинні бути об'єднані (надані) в дивовижні спецефекти, які ми зараз приймаємо для зернистого. Оскільки кожен кадр статичний, після того, як комп'ютер має початковий вхід, він більше не потребує зв'язку, поки остаточний кадр не буде готовий до відправки назад та об'єднання в хід. Наприклад, блокбастер «Володар кілець» мав свої спецефекти, надані на величезному кластері під управлінням Linux.

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

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

    Оскільки так багато вузлів розмовляють один з одним в системі, мінімізація відстані між кожним вузлом має першорядне значення. Очевидно, що краще, якщо кожен окремий вузол має прямий зв'язок з будь-яким іншим вузлом, оскільки це мінімізує відстань, яку потрібно пройти будь-якому вузлу, щоб знайти дані. Це не практична ситуація, коли кількість вузлів починає зростати в сотні і тисячі, як це робиться з великими суперкомп'ютерами; якщо ви пам'ятаєте, що ваша середня школа математики проблема в основному комбінація взяті два за раз (кожен вузол розмовляє з іншим), і буде рости п! /2 * (н-2)! .

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

    Гіперкуб має суворе математичне визначення (далеко за межами цієї дискусії), але як куб є тривимірним аналогом квадрата, тому гіперкуб є 4-вимірним аналогом куба.

    Малюнок 3.8. Гіперкуб. Приклад гіперкуба. Гіперкуби забезпечують хороший компроміс між відстанню між вузлами та кількістю необхідних взаємозв'язків.

    Вище ми бачимо зовнішній куб містить чотири 8 вузлів. Максимальна кількість шляхів, необхідних для того, щоб будь-який вузол спілкувався з іншим вузлом, дорівнює 3. Коли інший куб розміщений всередині цього куба, тепер ми маємо подвоєну кількість процесорів, але максимальна вартість шляху зросла лише до 4. Це означає, що у міру зростання кількості процесорів на 2 n максимальна вартість шляху зростає тільки лінійно.

    Узгодженість кешу все ще може підтримуватися в системі NUMA (це називається кеш-когерентною системою NUMA або CCNumA). Як ми вже згадували, схема на основі трансляції, яка використовується для збереження цілісних кешів процесорів у системі SMP, не масштабується до сотень або навіть тисяч процесорів у великій системі NUMA. Однією з поширених схем узгодженості кешу в системі NUMA називають моделлю на основі каталогів. У цій моделі процесори в системі зв'язуються зі спеціальним кешем каталогу апаратного забезпечення. Апаратне забезпечення каталогу підтримує послідовну картину для кожного процесора; ця абстракція приховує роботу системи NUMA від процесора.

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

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

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

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

    Системи NUMA найкраще підходять для тих типів проблем, які вимагають великої взаємодії між процесором і пам'яттю. Наприклад, у моделюванні погоди загальною ідіомою є поділ навколишнього середовища на невеликі «ящики», які реагують по-різному (наприклад, океани та земля відображають або зберігають різну кількість тепла). Коли симуляції виконуються, невеликі варіації будуть подаватися, щоб побачити, який загальний результат. Оскільки кожна коробка впливає на навколишні коробки (наприклад, трохи більше сонця означає, що конкретна коробка виділяє більше тепла, впливаючи на коробки поруч), буде багато зв'язку (контрастність, що з окремими рамками зображення для процесу візуалізації, кожен з яких не впливає на інший). Подібний процес може статися, якщо ви моделювали автомобільну аварію, де кожна маленька коробка імітованого автомобіля певним чином складається і поглинає деяку кількість енергії.

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

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

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

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

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

    Візьмемо, наприклад, наступну ситуацію.

    Приклад 3.1. Замовлення пам'яті
      1 typedef struct {
        int a;
        int b;
        } a_struct;
      5 
        /*
         * Pass in a pointer to be allocated as a new structure
         */
        void get_struct(a_struct *new_struct)
     10 {
        	void *p = malloc(sizeof(a_struct));
        
        	/* We don't particularly care what order the following two
        	 * instructions end up acutally executing in */
     15 	p->a = 100;
        	p->b = 150;
        
        	/* However, they must be done before this instruction.
        	 * Otherwise, another processor who looks at the value of p
     20 	 * could find it pointing into a structure whose values have
        	 * not been filled out.
        	 */
        	new_struct = p;
        }
     25 

    У цьому прикладі у нас є два магазини, які можна зробити в будь-якому конкретному порядку, так як це підходить процесору. Однак у кінцевому випадку покажчик повинен бути оновлений лише після того, як відомо, було зроблено два попередні магазини. Інакше інший процесор може подивитися на значення p, слідувати покажчику на пам'ять, завантажити її, і отримати якесь абсолютно некоректне значення!

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

    За замовчуванням завантаження або магазин можна повторно замовити в будь-якому місці.

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

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

    Малюнок 3.9. Отримання та вивільнення семантики. Ілюстрація дійсних переупорядковувань навколо операцій з семантикою придбання та випуску.

    Повний паркан пам'яті - це комбінація обох; де ніякі навантаження або магазини не можуть бути змінені в будь-якому напрямку навколо поточного навантаження або магазину.

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

    Різні процесори реалізують різні моделі пам'яті.

    Процесор x86 (і AMD64) має досить сувору модель пам'яті; всі магазини мають семантику випуску (тобто результат магазину гарантовано буде видно при будь-якому подальшому завантаженні або магазині), але всі навантаження мають нормальну семантику. Префікс блокування дає загородження пам'яті.

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

    Знання вимог до порядку пам'яті кожної архітектури не є практичним для всіх програмістів, і це ускладнить перенесення та налагодження програм у різних типах процесорів.

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

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

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

    І навпаки, коли ми відпускаємо блокування, ми повинні бути впевнені, що кожна операція, яку ми зробили, поки ми тримали блокування, завершена (пам'ятаєте приклад оновлення покажчика раніше?). Це семантика релізу.

    Є багато програмних бібліотек, які дозволяють програмістам не турбуватися про деталі семантики пам'яті і просто використовувати більш високий рівень абстракції lock () і unlock ().

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

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

    Подібна ситуація протилежна тупику, званому лайвлоком. Однією з стратегій, щоб уникнути тупику, може бути «ввічливий» замок; той, який ви віддаєте кожному, хто запитує. Ця ввічливість може призвести до того, що дві нитки постійно дають один одному замок, не приймаючи замок досить довго, щоб виконати критичну роботу і закінчити з замком (подібна ситуація в реальному житті може бути двома людьми, які зустрічаються біля дверей одночасно, обидва кажуть: «Ні, ви перший, я наполягати». Жоден кінець не проходить через двері!).

    Під ним є багато різних стратегій реалізації поведінки замків.

    Простий замок, який просто має два стани - заблокований або розблокований, називається м'ютексом (скорочення від взаємного виключення; тобто якщо одна людина має його, інша не може мати його).

    Існує, однак, ряд способів реалізації блокування м'ютекса. У найпростішому випадку ми маємо те, що його зазвичай називають спинлоком. При такому типі блокування процесор сидить у щільному циклі, чекаючи, щоб взяти блокування; еквівалентно тому, що він говорить «чи можу я мати його зараз» постійно так, як маленька дитина може запитати у батьків.

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

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

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

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

    Поясніть, що це таке.