Чтобы ответить на этот вопрос, важно понять разницу между режимом пользователя и режима ядра.Режим ядра является наиболее привилегированным режимом ЦП, где исполняемый код имеет полный доступ к оборудованию.Он используется для самой низкой функциональности операционной системы.Пользовательский режим является гораздо более ограниченным режимом процессора.Это предотвращает прямой доступ кода к оборудованию.Приложения запускаются в пользовательском режиме.Конечно, им все равно нужно как-то получить доступ к аппаратному обеспечению, поэтому им нужно обратиться к ядру.
Вот к чему приводит ваш вопрос.Чтобы код пользовательского режима мог вызывать ядро, ядро Windows устанавливает точку входа.В системах на базе x86 эта точка входа является либо программным прерыванием (int 2e), либо инструкцией sysenter / syscall.Выполнение этих инструкций приводит к переключению режима ЦП, переводя ЦП из режима пользователя в режим ядра.Как только процессор переключил режимы, он вызывает функцию, указанную ядром.В Windows эта функция является диспетчером системных служб.
Диспетчер системных служб отвечает за вызов службы ядра, которую хочет код пользовательского режима.Он берет номер функции, указанный в коде пользовательского режима, и ищет его в таблице дескрипторов системных служб (SSDT).SSDT - это в основном список указателей на функции для каждого ядра.Как только он идентифицирует правильную службу ядра, он вызывает ее с аргументами, которые также указано приложением в режиме пользователя.После завершения работы службы ядра ЦПУ возвращается к приложению либо с помощью инструкции iret (если она поступает из программного прерывания), либо с помощью sysexit / sysret (если она поступает из sysenter / syscall).
Все это звучит вполнесложный, и именно поэтому Windows скрывает эти детали от программистов.Вместо того, чтобы требовать от них прямой связи с ядром через точку входа, которую устанавливает ядро, Windows предоставляет программистам несколько библиотек DLL, которые делают это для них.
Теперь вот, где это снова становится несколько сложнее.Процесс вызова служб ядра из пользовательского режима реализован в ntdll.dll, но большинство программистов не использует ntdll.dll напрямую.Вместо этого он экспортирует общий набор служб ядра, который называется Native API.Кроме того, Win32 API реализован в kernel32.dll.Большинство функций в kernel32.dll - это просто оболочки функций в ntdll.dll.
Вы можете спросить, почему это делается.Почему бы просто не вызвать kernel32.dll напрямую вызывать функции ядра?Причина этого заключается в том, чтобы учитывать разные многопользовательские API-интерфейсы.Windows NT была разработана для поддержки нескольких API, не только Win32, но также POSIX и OS / 2.Каждый API пользовательского режима вызывает ntdll.dll для реализации своих собственных API, не позволяя им напрямую вызывать службы ядра.