В прошлой статье мы реализовали доступ двух потоков к экрану независимо друг от друга. Но при этом допустили страшную ошибку, которая к счастью не проявила себя никак, однако ничто не гарантирует отсутствия в данной ситуации отрицательных последствий для надежности нашего ядра.
Речь идет о том, что в каждом потоке, хоть и однократно, вызывается функция выделения памяти
/* Создаем виртуальный экран в потоке #1 */
vs01 = (vscreen_t*) get_vscreen();
.
.
.
/* Создаем виртуальный экран в потоке #2 */
vs02 = (vscreen_t*) get_vscreen();
Эти функции содержат внутри выделение памяти в куче ядра. Куча ядра у нас одна единственная, все структуры управляющие физической и виртуальной памятью - в одном экземпляре. Что произойдет если момент переключения задачи - ведь он происходит по прерыванию - совпадет с работой функции kmalloc(...)? Ничего хорошего - структуры управления кучей будут перехвачены другим потоком, и он безнадежно испортит их, что приведет к неверной работе другого потока да и системы в целом.
И вот тут мы неизбежно должны применять синхронизацию. Современные операционные системы используют для этого так называемые примитивы синхронизации - информационные структуры, обеспечивающие блокировку и освобождение доступа к общим ресурсам.