Как программа просит ядро о помощи, из чего состоит ядро и что происходит от нажатия кнопки питания до входа в систему
bcdedit и Confirm-SecureBootUEFI без этого не работают). Примеры Linux повторите в своей ВМ после раздела 03; strace ставится командой sudo apt install strace.Если любая программа может напрямую писать в память и в порты устройств, одна ошибка в игре затирает данные редактора, а вредоносная программа читает чужие пароли. Поэтому процессор умеет работать в двух режимах.
У процессоров x86 режимы называют кольцами защиты: кольцо 0 — ядро, кольцо 3 — программы; кольца 1 и 2 современные ОС не используют. Если программа в кольце 3 попробует выполнить запрещённую команду или обратиться к чужой памяти, процессор остановит её и передаст управление ядру — а ядро завершит программу с ошибкой.
Windows считает, сколько времени процесс провёл в каждом режиме. UserProcessorTime — работа в пользовательском режиме, PrivilegedProcessorTime — в режиме ядра:
PS> Get-Process -Name lsass, explorer, System | Select-Object ProcessName, UserProcessorTime, PrivilegedProcessorTime
ProcessName UserProcessorTime PrivilegedProcessorTime
----------- ----------------- -----------------------
explorer 00:00:26.7187500 00:00:27.7343750
lsass 00:01:12.8593750 00:01:20.4062500
System 00:00:00 00:31:52.1718750
У процесса System пользовательского времени нет совсем: это не программа, а потоки самого ядра. У lsass (проверка паролей и прав) и Проводника время делится примерно поровну: значительную часть работы за них делает ядро — читает файлы, рисует окна, проверяет доступ.
Программе в пользовательском режиме всё равно нужно читать файлы и выводить текст. Для этого она делает системный вызов — просит ядро выполнить действие за неё:
syscall на x86-64).Программы обычно не делают вызовы сами, а пользуются библиотекой: в Linux — libc (функция read вызывает системный вызов read), в Windows — функции Win32 API из kernel32.dll, которые через ntdll.dll вызывают ядро (ReadFile → NtReadFile).
В Linux системные вызовы любой программы показывает strace. Посмотрим, как cat выводит файл /etc/hostname — имя компьютера. Трассу пишем в файл trace.txt, чтобы она не смешалась с выводом самой программы, а при просмотре убираем вызовы загрузки библиотек и закрытия служебных файлов:
$ strace -o trace.txt -e trace=openat,read,write,close cat /etc/hostname
mypc
$ grep -v -e "\.so" -e ELF -e "^close(3)" trace.txt
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
read(3, "mypc\n", 262144) = 5
write(1, "mypc\n", 5) = 5
read(3, "", 262144) = 0
close(1) = 0
close(2) = 0
+++ exited with 0 +++
Первым openat программа открыла файл языковых настроек (локали) — так начинает почти любая программа. Дальше вся полезная работа: openat открыл /etc/hostname и получил номер 3 (дескриптор), read прочитал 5 байт, write записал их в дескриптор 1 — стандартный вывод, то есть на экран; второй read вернул 0 — конец файла. В конце close закрывает стандартный вывод и вывод ошибок. Ключ -c вместо подробностей считает вызовы:
$ strace -c -f ls /etc > /dev/null
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
32,44 0,000133 66 2 getdents64
20,24 0,000083 8 10 close
11,46 0,000047 5 8 openat
9,76 0,000040 4 9 fstat
5,85 0,000024 1 23 mmap
5,85 0,000024 12 2 2 ioctl
4,39 0,000018 18 1 statx
4,15 0,000017 8 2 2 access
3,17 0,000013 13 1 write
2,68 0,000011 1 6 read
0,00 0,000000 0 6 mprotect
0,00 0,000000 0 1 munmap
0,00 0,000000 0 3 brk
0,00 0,000000 0 2 pread64
0,00 0,000000 0 1 execve
0,00 0,000000 0 2 2 statfs
0,00 0,000000 0 6 4 prctl
0,00 0,000000 0 1 arch_prctl
0,00 0,000000 0 1 set_tid_address
0,00 0,000000 0 1 set_robust_list
0,00 0,000000 0 1 prlimit64
0,00 0,000000 0 1 getrandom
0,00 0,000000 0 1 rseq
------ ----------- ----------- --------- --------- ----------------
100,00 0,000410 4 91 10 total
Чтобы вывести список одной папки, ls сделал почти сто вызовов; большая часть — mmap и mprotect: загрузка библиотек при старте программы.
Переход в ядро и обратно стоит времени. Скопируем 2 МБ двумя способами: по 1 байту за вызов и по 1 МБ:
$ time dd if=/dev/zero of=/dev/null bs=1 count=2000000
2000000+0 records in
2000000+0 records out
2000000 bytes (2,0 MB, 1,9 MiB) copied, 3,70452 s, 540 kB/s
real 0m3,707s
user 0m1,832s
sys 0m1,872s
$ time dd if=/dev/zero of=/dev/null bs=1M count=2
2+0 records in
2+0 records out
2097152 bytes (2,1 MB, 2,0 MiB) copied, 0,000633011 s, 3,3 GB/s
real 0m0,002s
user 0m0,002s
sys 0m0,000s
Данных столько же, а разница — в тысячи раз. Строка sys — время в режиме ядра, user — в пользовательском. При bs=1 программа сделала четыре миллиона вызовов (два миллиона read и два миллиона write), и около половины времени ушло на работу в ядре. Отсюда правило, которое пригодится и администратору, и программисту: читать и писать крупными блоками, а не по байту.
Главный вопрос устройства ядра — что работает в режиме ядра, а что вынести в пользовательский режим.
| Архитектура | Что в режиме ядра | Плюсы | Минусы | Примеры |
|---|---|---|---|---|
| Монолитное ядро | Всё: процессы, память, файловые системы, сеть, драйверы | Быстро: части ядра вызывают друг друга напрямую | Ошибка любого драйвера роняет всю систему | Linux, FreeBSD |
| Микроядро | Минимум: процессы, память, передача сообщений | Надёжно: упавший драйвер перезапускается, как программа | Медленнее: части общаются сообщениями через ядро | QNX, MINIX 3 |
| Гибридное ядро | Почти всё, как в монолитном, но устроено слоями и модулями | Скорость монолита и часть гибкости микроядра | Ошибка драйвера всё равно роняет систему | Windows NT, macOS (XNU) |
Различие на практике видно по тому, что бывает при ошибке драйвера. В Windows — «синий экран» (BSOD) и перезагрузка, в Linux — kernel panic. В QNX упавший драйвер перезапускается, а система работает дальше — поэтому микроядро выбирают там, где остановка недопустима: в автомобилях и на производстве.
Монолитное ядро Linux при этом расширяется на ходу: драйверы и файловые системы оформлены модулями ядра, которые загружаются по необходимости. Модуль, загруженный в ядро, работает в режиме ядра с теми же правами, что и само ядро.
Ядро Windows — файл ntoskrnl.exe. В нём две части: исполнительная система (диспетчер процессов, памяти, ввода-вывода, безопасности) и собственно ядро (планирование потоков, прерывания). Под ними — уровень абстракции оборудования HAL, который прячет различия материнских плат.
PS> Get-Item C:\Windows\System32\ntoskrnl.exe | Select-Object Name, @{n="Описание"; e={$_.VersionInfo.FileDescription}}, @{n="Версия"; e={$_.VersionInfo.ProductVersion}}
Name Описание Версия
---- -------- ------
ntoskrnl.exe NT Kernel & System 10.0.26100.9711
Драйвер — программа, которая знает, как управлять конкретным устройством, и даёт ядру единый интерфейс к нему. В Windows драйверы — файлы .sys в C:\Windows\System32\drivers:
PS> (Get-CimInstance Win32_SystemDriver | Where-Object State -eq Running).Count
123
PS> Get-CimInstance Win32_SystemDriver | Where-Object State -eq Running | Select-Object -First 8 Name, PathName
Name PathName
---- --------
ACPI C:\WINDOWS\system32\drivers\ACPI.sys
acpiex C:\WINDOWS\system32\Drivers\acpiex.sys
AFD C:\WINDOWS\system32\drivers\afd.sys
afunix C:\WINDOWS\system32\drivers\afunix.sys
ahcache C:\WINDOWS\system32\DRIVERS\ahcache.sys
amdwps C:\WINDOWS\system32\drivers\amdwps.sys
AppID C:\WINDOWS\system32\drivers\appid.sys
applockerfltr C:\WINDOWS\system32\drivers\applockerfltr.sys
Больше сотни драйверов работают даже в виртуальной машине без принтеров и камер: шины, диски, файловые системы, сеть, защита. Многие из них — не драйверы устройств, а части ядра, оформленные как драйверы: AFD — сокеты, AppID — проверка разрешённых программ.
В Linux драйверы — модули ядра, их список показывает lsmod:
$ lsmod | wc -l
104
$ lsmod
Module Size Used by
dm_mod 221184 0
cpuid 12288 0
xt_nat 12288 0
xt_tcpudp 16384 0
xt_conntrack 12288 1
…
У ядра есть свои процессы — точнее, потоки, которые работают только в режиме ядра. В Windows это процесс System с номером 4 и Idle с номером 0 — «процесс простоя», который занимает процессор, когда больше нечего делать:
PS> Get-Process -Id 0, 4 | Select-Object Id, ProcessName
Id ProcessName
-- -----------
0 Idle
4 System
В Linux процесс номер 1 — systemd, первый процесс пользовательского режима: он запускает все службы. Номер 2 — kthreadd, родитель всех потоков ядра; в списке процессов их имена в квадратных скобках:
$ ps -o pid,ppid,comm -p 1,2
PID PPID COMMAND
1 0 systemd
2 0 kthreadd
$ ps -ef | grep -F "[" | grep -v grep
root 2 0 0 авг26 ? 00:00:02 [kthreadd]
root 3 2 0 авг26 ? 00:00:00 [pool_workqueue_release]
root 4 2 0 авг26 ? 00:00:00 [kworker/R-kvfree_rcu_reclaim]
root 5 2 0 авг26 ? 00:00:00 [kworker/R-rcu_gp]
root 6 2 0 авг26 ? 00:00:00 [kworker/R-sync_wq]
root 7 2 0 авг26 ? 00:00:00 [kworker/R-slub_flushwq]
root 8 2 0 авг26 ? 00:00:00 [kworker/R-netns]
root 11 2 0 авг26 ? 00:00:00 [kworker/0:0H-events_highpri]
…
$ ps -e --no-headers | wc -l
137
$ ps --ppid 2 --no-headers | wc -l
72
Половина «процессов» на сервере — потоки ядра: они пишут данные на диск, обслуживают сеть, освобождают память.
При включении в памяти нет ОС. Её загружает цепочка программ, каждая из которых запускает следующую.
| Этап | Что происходит | Windows | Linux |
|---|---|---|---|
| 1. Прошивка | Проверка оборудования (POST), выбор диска загрузки | UEFI (или старый BIOS) на материнской плате | |
| 2. Загрузчик | Находит ОС, показывает меню, загружает ядро | Диспетчер загрузки bootmgfw.efi, настройки — хранилище BCD | GRUB, настройки — /boot/grub/grub.cfg |
| 3. Ядро | Инициализирует память, процессор, драйверы, монтирует системный диск | winload.efi загружает ntoskrnl.exe и драйверы загрузки | vmlinuz и initrd.img — временная система с драйверами дисков |
| 4. Первый процесс | Запускает службы и вход в систему | smss.exe → wininit.exe (службы), winlogon.exe (вход) | systemd (PID 1) |
| 5. Вход | Пользователь вводит пароль, запускается оболочка | Экран входа → Проводник | Экран входа или login → bash |
UEFI заменил BIOS: работает с дисками больше 2 ТБ (разметка GPT, раздел 03), загружает файлы .efi с отдельного небольшого раздела ESP и поддерживает безопасную загрузку (Secure Boot) — запускает только загрузчики с доверенной цифровой подписью, чтобы вредоносная программа не встроилась до запуска ОС. Windows 11 требует UEFI и Secure Boot.
PS> $env:firmware_type
UEFI
PS> Confirm-SecureBootUEFI
True
Настройки Диспетчера загрузки Windows хранятся в базе BCD и выводятся командой bcdedit. {bootmgr} — сам Диспетчер, {current} — запись загруженной сейчас Windows:
PS> bcdedit /enum "{bootmgr}"
Диспетчер загрузки Windows
--------------------
идентификатор {bootmgr}
device partition=\Device\HarddiskVolume1
path \EFI\Microsoft\Boot\bootmgfw.efi
description Windows Boot Manager
locale ru-RU
inherit {globalsettings}
default {current}
resumeobject {3e8baebd-d2f9-11f1-9894-f58ab4346bd2}
displayorder {current}
toolsdisplayorder {memdiag}
timeout 30
PS> bcdedit /enum "{current}"
Загрузка Windows
-------------------
идентификатор {current}
device partition=C:
path \WINDOWS\system32\winload.efi
description Windows 11
locale ru-RU
inherit {bootloadersettings}
recoverysequence {3e8baebf-d2f9-11f1-9894-f58ab4346bd2}
displaymessageoverride Recovery
recoveryenabled Yes
isolatedcontext Yes
allowedinmemorysettings 0x15000075
osdevice partition=C:
systemroot \WINDOWS
resumeobject {3e8baebd-d2f9-11f1-9894-f58ab4346bd2}
nx OptIn
bootmenupolicy Standard
Видно всю цепочку: Диспетчер \EFI\Microsoft\Boot\bootmgfw.efi лежит на разделе ESP, ждёт выбора timeout секунд и запускает {current} — загрузчик winload.efi с диска C:, который загружает систему из \WINDOWS. Если загрузка не удалась, используется recoverysequence — среда восстановления.
bcdedit /set и /delete меняют загрузку, и ошибка в них оставляет компьютер незагружаемым. Пробуйте их только в виртуальной машине и только после снимка (раздел 03). Безопасный способ поменять загрузку на учебном ПК — Win+R → msconfig → вкладка «Загрузка»: там включается безопасный режим и журнал загрузки драйверов (C:\Windows\ntbtlog.txt).Когда система загрузилась, ядро записывает событие в журнал «Система». Время последней загрузки:
PS> Get-CimInstance Win32_OperatingSystem | Select-Object LastBootUpTime
LastBootUpTime
--------------
29.10.2026 21:06:18
PS> Get-WinEvent -FilterHashtable @{LogName="System"; ProviderName="Microsoft-Windows-Kernel-General"; Id=12} -MaxEvents 1 | Format-List TimeCreated, Message
TimeCreated : 29.10.2026 21:06:16
Message : Системное время запуска операционной системы: 2026-10-29T18:06:16.500000000Z.
В Linux ядра лежат в /boot: по файлу vmlinuz и initrd.img на каждую версию. Старые версии хранятся, чтобы загрузиться с них, если новое ядро не заработает:
$ ls /boot
config-6.12.113+deb13-amd64
config-6.12.43+deb13-amd64
config-6.12.95+deb13-amd64
grub
initrd.img-6.12.113+deb13-amd64
initrd.img-6.12.43+deb13-amd64
initrd.img-6.12.95+deb13-amd64
System.map-6.12.113+deb13-amd64
System.map-6.12.43+deb13-amd64
System.map-6.12.95+deb13-amd64
vmlinuz-6.12.113+deb13-amd64
vmlinuz-6.12.43+deb13-amd64
vmlinuz-6.12.95+deb13-amd64
$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-6.12.95+deb13-amd64 root=UUID=97c23eb6-eee2-49d6-917b-29ce316c376b ro quiet
$ systemd-analyze
Startup finished in 2.521s (kernel) + 3.211s (userspace) = 5.732s
graphical.target reached after 3.210s in userspace.
$ systemd-analyze blame
2.427s postgresql@17-main.service
1.171s dev-sda1.device
874ms docker.service
803ms fstrim.service
681ms apt-daily-upgrade.service
677ms apt-daily.service
…
/proc/cmdline — параметры, с которыми GRUB запустил ядро: какой файл ядра, какой диск сделать корневым (root=UUID=…), ro — сначала смонтировать его только для чтения, quiet — меньше сообщений на экране. systemd-analyze делит время загрузки на ядро и пользовательский режим, а blame показывает, какие службы запускались дольше всех, — с этого начинают, когда сервер долго загружается.
Обратите внимание: в /boot есть ядро 6.12.113, а /proc/cmdline показывает загруженное 6.12.95. Значит, обновление ядра установлено, но сервер с тех пор не перезагружали, и работает старое ядро. Новое ядро начинает работать только после перезагрузки — это нужно планировать после каждого обновления безопасности.
smss.exe или systemd) → входbcdedit), Linux — GRUB и /boot; новое ядро работает только после перезагрузки