Відповіді:
Спіновий замок - це спосіб захистити спільний ресурс від модифікації двома або більше процесами одночасно. Перший процес, який намагається модифікувати ресурс, «набуває» блокування і продовжує свій шлях, роблячи все необхідне з ресурсом. Будь-які інші процеси, які згодом намагаються придбати замок, зупиняються; кажуть, що вони «крутяться на місці», чекаючи, коли замок буде відпущений першим процесом, таким чином, назва блокується.
Ядро Linux використовує спінові блокування для багатьох речей, наприклад, для надсилання даних на певну периферію. Більшість апаратних периферійних пристроїв не розроблені для обробки кількох оновлень одночасного стану. Якщо мають відбутися дві різні модифікації, одна повинна суворо слідувати іншій, вони не можуть перетинатися. Спіновий замок забезпечує необхідний захист, гарантуючи, що модифікації відбуваються одна за одною.
Спінові блокування є проблемою, оскільки спінінг блокує те, що ядро центрального процесора виконує будь-яку іншу роботу. Хоча ядро Linux надає послуги багатозадачності для програм простору користувача, що працюють під ним, це багатозадачне завдання загального призначення не поширюється на код ядра.
Ця ситуація змінюється і стосується більшої частини існування Linux. Через Linux 2.0 ядро було майже суто програмою з однозначним завданням: кожного разу, коли в процесорі був запущений код ядра, використовувалося лише одне ядро процесора, оскільки існувало одне блокування спіна, що захищало всі спільні ресурси, зване великим блокуванням ядра (BKL ). Починаючи з Linux 2.2, BKL поступово розпадається на безліч незалежних замків, які захищають більш цілеспрямований клас ресурсів. Сьогодні з ядром 2.6 BKL все ще існує, але його використовує лише дійсно старий код, який неможливо легко перенести на якийсь більш детальний замок. Зараз у багатоядерній коробці цілком можливо мати кожен процесор, який виконує корисний код ядра.
Існує обмеження на корисність розбиття BKL, оскільки в ядрі Linux бракує загальної багатозадачності. Якщо ядро центрального процесора заблоковано спінінг на блокуванні спина ядра, його не можна повторно застосувати, щоб зробити щось інше, поки блокування не буде звільнено. Він просто сидить і крутиться, поки замок не звільниться.
Спінові замки можуть ефективно перетворити 16-ядерну коробку монстра в одноядерну коробку, якщо навантаження така, що кожне ядро завжди чекає єдиного блокування віджиму. Це головний ліміт масштабованості ядра Linux: подвоєння ядер процесора з 2 до 4, ймовірно, майже вдвічі перевищить швидкість вікна Linux, але подвоєння його з 16 до 32, мабуть, не буде, при більшості навантажень.
Спіновий замок - це коли процес, який постійно запитує, щоб зняти замок. Це вважається поганим, оскільки процес вимагає циклів (як правило) без потреби. Це не специфічний для Linux, а загальний шаблон програмування. І хоча це, як правило, вважається поганою практикою, насправді це правильне рішення; бувають випадки, коли вартість використання планувальника вища (з точки зору процесорних циклів), ніж вартість кількох циклів, які, як очікується, триватиме.
Приклад спінінгу:
#!/bin/sh
#wait for some program to clear a lock before doing stuff
while [ -f /var/run/example.lock ]; do
sleep 1
done
#do stuff
Часто існує спосіб уникнути фіксації закрутки. Для цього конкретного прикладу є інструмент Linux, який називається inotifywait (він зазвичай не встановлюється за замовчуванням). Якби це було написано на C, ви просто використали API inotify, який надає Linux.
Цей самий приклад, використовуючи inotifywait, показує, як зробити те ж саме без блокування віджиму:
#/bin/sh
inotifywait -e delete_self /var/run/example.lock
#do stuff
Коли потік намагається придбати замок, три речі можуть статися, якщо він не виходить, він може спробувати блокувати, він може спробувати і продовжити, він може спробувати потім перейти до сну, кажучи ОС перекрити його, коли відбудеться якась подія.
Тепер спроба і продовження використовує набагато менше часу, ніж спроба та блокування. Скажімо на даний момент, що "спробувати і продовжувати" займе i одиницю часу, а "спробувати і заблокувати" займе сотню.
Тепер давайте на даний момент припустимо, що в середньому нитка займе 4 одиниці часу, утримуючи замок. Марно чекати 100 одиниць. Тому замість цього ви пишете цикл "спробуйте і продовжуйте". З четвертої спроби ви зазвичай придбаєте замок. Це прядильний замок. Це називається тому, що нитка продовжує крутитися на місці, поки не отримає замок.
Додатковим заходом безпеки є обмеження кількості запуску циклу. Так у прикладі ви робите цикл для запуску, наприклад, шість разів, якщо він не працює, то ви "спробуйте і заблокуйте".
Якщо ви знаєте, що нитка завжди буде утримувати замок, скажімо, 200 одиниць, то ви витрачаєте час на комп'ютер на кожну спробу і продовжуйте.
Таким чином, врешті-решт, замок для віджиму може бути дуже ефективним або марним. Це марно, коли "типовий" час для проведення блокування перевищує час, необхідний для "спробу та блокування". Це ефективно, коли типовий час утримувати замок набагато менший, ніж час "спробувати і заблокувати".
Ps: Книга, яку потрібно прочитати на темах, - це "Підручник з теми", якщо ви все ще можете її знайти.
Замок є способом для двох або більше завдань (процеси, потоки) для синхронізації. Зокрема, коли обидві завдання потребують переривчастого доступу до ресурсу, який може використовуватися лише одне завдання за один раз, це спосіб упорядкувати завдання не використовувати ресурс одночасно. Для доступу до ресурсу завдання повинно виконати наступні дії:
take the lock
use the resource
release the lock
Зняти замок неможливо, якщо його вже взяли інші завдання. (Подумайте про замок як про фізичний предмет-маркер. Або об'єкт знаходиться у шухляді, або хтось має його в руці. Тільки людина, яка тримає об'єкт, може отримати доступ до ресурсу.) Тож "взяти замок" насправді означає "почекати, поки ні в кого іншого немає замка, тоді візьміть його ».
З точки зору високого рівня, існують два основні способи реалізації блокування: спинлок і умови. З спин - блокувань , не беручи блокують кошти просто «прядіння» (тобто нічого не робити в циклі) , поки ніхто більше має блокування. За умов, якщо завдання намагається зняти замок, але заблоковано, оскільки його виконує інше завдання, новачок входить у чергу очікування; операція звільнення сигналізує до будь-якого завдання очікування, що блокування тепер доступне.
(Цих пояснень недостатньо, щоб дозволити вам здійснити блокування, тому що я нічого не сказав про атомність. Але атомність тут не важлива.)
Спінлок, очевидно, марнотратний: завдання очікування постійно перевіряє, чи зроблено замок. То чому і коли він використовується? Спінлок часто буває дуже дешевим, якщо замок не тримається. Це робить його привабливим, коли шанс утримувати замок невеликий. Крім того, спінові замки є життєздатними лише в тому випадку, якщо очікується, що отримання замка не займе багато часу. Тому спинлок, як правило, застосовується в ситуаціях, коли вони залишатимуться дуже короткими, тому очікується, що більшість спроб вдасться досягти успіху з першої спроби, а ті, які потребують очікування, не чекають довго.
Існує хороше пояснення спінлок та інших механізмів сумісності ядра Linux в драйверах пристроїв Linux , глава 5.
synchronized
було б реалізовано спінлок: synchronized
блок міг би працювати дуже довго. synchronized
- це мовна конструкція, яка дозволяє зробити блокування простими у використанні в певних випадках, а не примітивом для побудови великих примітивів для синхронізації.
Спінлок - це замок, який функціонує, вимикаючи планувальник і, можливо, перериває (варіант irqsave) на тому конкретному ядрі, на якому придбано замок. Він відрізняється від mutex тим, що він відключає планування, тому під час утримування спінлок може працювати лише ваша нитка. Мутекс дозволяє намітити інші потоки з більш високим пріоритетом, поки він утримується, але не дозволяє їм одночасно виконувати захищену секцію. Оскільки відключення відключають багатозадачність, ви не можете взяти спінлок, а потім зателефонувати до іншого коду, який спробує придбати мютекс. Ваш код у розділі відключення ніколи не повинен спати (код зазвичай спить, коли він стикається із замкнутим мютексом або порожнім семафором).
Інша відмінність мутексу полягає в тому, що нитки, як правило, в черзі на мютекс, так що під ім'ям мутекс є черга. Тоді як спінлок просто гарантує, що жоден інший потік не запуститься, навіть якщо це потрібно. Таким чином, ви ніколи не повинні утримувати відключення під час виклику функцій поза вашим файлом, які ви не впевнені, що вони не сплять.
Коли ви хочете поділитися спінлок із перервою, вам слід скористатися варіантом irqsave. Це не просто вимкне планувальник, але й відключить переривання. Це має сенс правильно? Spinlock працює, переконуючись, що більше нічого не працюватиме. Якщо ви не хочете запускати переривання, вимкніть його та безпечно перейдіть до критичного розділу.
На багатоядерній машині спінлок буде насправді крутитися в очікуванні іншого ядра, яке тримає замок, щоб звільнити його. Таке обертання відбувається лише на багатоядерних машинах, оскільки на одноядерних це не може відбутися (ви або тримаєте спінлок і продовжуєте, або ніколи не запускаєтесь, поки блокування не буде звільнено).
Спінлок не марнотратний там, де це має сенс. Для дуже малих критичних розділів було б марно виділяти чергу завдань mutex, порівняно з тим, щоб просто призупинити планувальник на кілька мікросекунд, необхідних для завершення важливої роботи. Якщо вам потрібно спати або утримувати замок протягом операції io (яка може спати), тоді використовуйте мютекс. Звичайно ніколи не фіксуйте спінлок, а потім намагайтеся звільнити його всередині переривання. Поки це буде працювати, це буде схоже на ардуїнське лайно часу (flagnotset); у такому випадку використовуйте семафор.
Захопіть спінлок, коли вам потрібно просте взаємне виключення для блоків транзакцій пам'яті. Візьміть мутекс, коли ви хочете, щоб декілька потоків зупинилися прямо перед блокуванням мютексу, а потім вибирати нитку найвищого пріоритету, щоб продовжуватись, коли мутекс стає вільним і коли ви блокуєте та відпускаєте в одній темі. Візьміть семафор, коли ви маєте намір опублікувати його в одній нитці або переривці та візьміть його в іншій нитці. Це три трохи різні способи забезпечення взаємного виключення, і вони використовуються для дещо інших цілей.