#c #linux #assembly #signals #glibc
Вопрос:
В x86_64 Linux, с:
#include <ucontext.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
int main(void)
{
#define PRSZ(X) printf("sizeof(" #X ")=%zun", sizeof(X))
PRSZ(ucontext_t); //contains mcontext_t, stack_t, sigset_t, and on glibc this struct _libc_fpstate
PRSZ(mcontext_t);
PRSZ(stack_t);
PRSZ(sigset_t);
//PRSZ(struct _libc_fpstate); //512
PRSZ(sigjmp_buf); //contains sigset_t
}
Я получаю:
sizeof(ucontext_t)=936
sizeof(mcontext_t)=256
sizeof(stack_t)=24
sizeof(sigset_t)=128
sizeof(sigjmp_buf)=200
Почему ucontext_t
так много больше, чем sigjmp_buf
и mcontext_t
так много больше, чем sizeof(sigjmp_buf)-sizeof(sigset_t)
?
Я думал, что (sig)longjmp можно использовать для восстановления контекста из (sig)jmp_buf точно так же, как setcontext можно использовать для восстановления формы контекста ucontext_t, но в последнем случае сохраняется и восстанавливается гораздо больше контекста.
С чем setcontext
можно сравнить дополнительные вещи ? siglongjmp
Комментарии:
1.
siglongjmp
нужно только сохранить те регистры, которые любая процедура должна была бы сохранить перед их использованием. Это намного меньше, чем все регистры, и это то, чтоsetcontext
нужно сохранить.2. Продолжая мысль @fuz, помните, что в x86-64 System V все векторные регистры заблокированы вызовами. YMM0..15-это в общей сложности 512 байт (
32B * 16
), в то время как ZMM0..31 будет64B * 32
= 2048 плюс 8×8 байт для правил маски. Около половины 16-кратных 8-байтовых целочисленных правил GP сохраняются при вызове, поэтому setjmp пропускает около 64 байтов целочисленных правил. (Поскольку это выглядит как обычный вызов функции для кода, сгенерированного компилятором, таким образом, компилятор будет предполагать, что регистры, заблокированные вызовом, заблокированы.)3. Спасибо вам, Фуз и Питер Кордес. Теперь я понимаю, почему обработчики sigaction используют ucontext_t, а не просто sigjmp_buf. Я оставлю этот вопрос на случай, если кто-то захочет ответить более подробно.
4. Это заставляет меня задуматься, почему люди когда-либо хотели использовать ucontext_t и семейство для переключения контекста. Мне кажется, что есть только один законный пользователь, и это ОС при настройке кадров обработчика сигналов (потому что только использование регистра действительно непредсказуемо из-за асинхронности прерывания). Везде (sig)setjmp/(sig)longjmp должно быть достаточно.