У просторі користувача багато операцій виділення пам'яті, зокрема деякі розглянуті раніше приклади, можуть бути виконані з використанням стека, тому що апріорі відомий розмір виділеної області пам'яті. У просторі користувача доступна така розкіш, як дуже великий і динамічно зростаючий стек завдання, проте в режимі ядра такої розкоші немає - стек ядра маленький і фіксований за розміром. Коли процесу виділяється невелика і фіксований за розміром стек, то витрати пам'яті зменшуються і ядру немає необхідності виконувати додаткові функції з управління пам'яттю.
Розмір стека залежить як від апаратної платформи, так і від конфігураційних параметрів, які були вказані на етапі компіляції. Історично розмір стека ядра дорівнював двом сторінок пам'яті для кожного процесу. Це відповідає
8 Кбайт для 32-розрядних апаратних платформ і 16 Кбайт для 64-розрядних апаратних платформ.
У перших версіях ядер серії 2.6 була введена можливість конфігурації, для якої розмір стека ядра дорівнює одній сторінці пам'яті. Коли встановлюється така конфігурація, то процес отримує стек, за розміром дорівнює всього одній сторінці пам'яті: 4 Кбайт на 32-розрядних апаратних платформах і 8 Кбайт - на 64-розрядних. Це зроблено з двох причин. По-перше це зменшує витрати пам'яті на одну сторінку для кожного процесу. По-друге, що найбільш важливо, при збільшенні часу роботи системи (uptime) стає все важче шукати дві фізично суміжні сторінки пам'яті. Фізична пам'ять стає все більш фрагментованою, і навантаження на систему управління віртуальною пам'яттю при створенні нових процесів стає все більш суттєвою.
Існує ще одна складність (залишайтеся з нами, і Ви дізнаєтеся все про стеках ядра). Вся послідовність вкладених викликів функцій в режимі ядра повинна поміститися в стеці. Історично обробники переривань використовують стек того процесу, виконання якого вони перервали. Це означає, що в гіршому випадку
8 Кбайт стека має використовуватися спільно усіма вкладеними викликами функцій і ще парою обробників переривань. Все це ефективно і просто, але це накладає ще більше обмежень на використання стека ядра. Коли розмір стека скоротився до однієї сторінки пам'яті, обробники переривань туди перестали поміщатися.
Для вирішення зазначеної проблеми була реалізована ще одна можливість - стеки обробників переривань. Стеки переривань є один стек на кожен процесор, які використовуються для обробки переривань. При такій конфігурації обробники переривань більше не використовують стеки ядра тих процесів, які цими обработчиками перериваються. Замість цього вони використовують свої власні стеки. Це вимагає тільки одну сторінку пам'яті на процесор.
Підведемо підсумки. Стек ядра займає одну або дві сторінки пам'яті, в залежності від конфігурації, яка виконується перед компіляцією ядра. Отже, розмір стека ядра може мати діапазон від 4 до 16 Кбайт. Історично обробники переривань спільно використовували стек перерваного ними процесу. При появі стеків ядра розміром в одну сторінку пам'яті обробникам переривань були призначені свої стеки. У будь-якому випадку необмежена рекурсія і використання функцій на зразок alloca () явно не припустимі.
Чесна гра зі стеком
У будь-якої функції необхідно скорочувати використання стека до мінімуму. Хоча не існує твердих правил, проте слід підтримувати максимальний сумарний обсяг усіх локальних змінних (також відомих як автоматичні змінні або змінні, виділені в стеці) не більш декількох сотень байтів. Небезпечно статично виділяти великі об'єкти в стеку, такі як великі масиви структур. В іншому випадку виділення пам'яті в стеку буде виконуватися так само, як і в просторі користувача. Переповнення стека відбувається непомітно і зазвичай призводить до проблем. Так як ядро не виконує ніякої управління стеком, то дані стека просто перепишуть всі, що знаходиться за стеком. В першу чергу постраждає структура thread_info. яка розташована в самому кінці стека процесу (згадайте главу 3). За межами стека все дані ядра можуть пропасти. У кращому випадку при переповненні стека відбудеться збій в роботі машини. У гірших випадках може статися пошкодження даних.
У зв'язку з цим, для виділення великих обсягів пам'яті необхідно використовувати одну з динамічних схем виділення пам'яті, які були розглянуті раніше в цій главі.