Как обрабатываются прерывания во время системного вызова?
Как мне известно, syscall — это команда процессору, что нужно перейти в режим ядра и вызвать прерывание из IDT. Пусть процесс a сделает системный вызов, тем самым процессор перейдёт в режим ядра, вызовет обработчик прерывания, который сделает что-то полезное, и вернётся обратно вызвав iret. Но что будет, если во время исполнения syscall (то есть в коде ядра линукс) произойдёт прерывание от таймера, и, соответственно, Linux поменяет контекст процесса на b?
Как обработается следующее прерывание от таймера, которое на этот раз для исполнения выберет процесс a? Оно, понятное дело, восстановит контекст процесса a и, соответственно, должно переместиться в точку где a прервался в прошлый раз. Это точка - какаято инстуркция внутри syscall обработчика. Как себя тогда поведёт процесор?
Ответы (1 шт):
Почти все процессоры автоматически запрещают прерывания, когда входят в системный вызов или в другое прерывание. Например, процессоры x86 автоматически сбрасывают флаг IF, который отвечает за возможность прерываний.
Когда прерывания запрещены, процессор не обрабатывает их, но контроллер прерываний запоминает, что прерывания были. Когда прерывания снова разрешены, процессор немедленно начинает обрабатывать всё что пришло за время запрета.
Если системный короткий, то он может держать прерывания под запретом - инструкция возврата iret
автоматически разрешит их обратно. Если системный вызов долгий - то он может разрешить прерывания специальной инструкцией. Разумеется, перед этим контекст возврата должен быть сохранён на какой-то стек - так как стек позволяет реализовать вложенные вызовы.
- В некоторых архитектурах процессор автоматически кладёт контекст возврата на стек ядра (в ядре используется другой указатель стека, который неподконтролен обычным программам). Например, x86 кладёт адрес возврата (RIP), сегмент кода CS, флаги, а если произошел переход между режимами - ещё и старый стек RSP и сегмент стека SS.
- В других архитектурах (например, MIPS) контекст возврата хранится в специальном регистре и на стек автоматически не кладётся. В этом случае, обработчик должен сделать это вручную, прежде чем разрешать прерывания - иначе новое прерывание перезапишет регистр возврата. Перед возвратом нужно запретить прерывания и достать контекст из стека. Если обработчик не разрешает прерываний - класть контекст на стек не обязательно.
Контекст возврата всегда содержит режим, из которого было прерывание - так что процессор всегда знает, нужно ли вернуться в режим ядра или пользователя. Например, в x86 режим хранится в регитре CS - который, как я уже сказал, сохраняется на стеке. В других системах он может храниться в регистре флагов, "регистре состояния", и т.п. - разные названия в разных архитектурах. В любом случае, этот регистр будет сохранён при входе в прерывание, и восстановлен при выходе.