Почему ucontext_t намного больше, чем sigjmp_buf?

#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 должно быть достаточно.