最近看到 sigsetjmp siglongjmp 的代码,musl
里对 jmp_buf
的写法很独特。原来 C 里面还是有好多东西自己不知道的啊。
// aarch64
typedef unsigned long __jmp_buf[22];
typedef struct __jmp_buf_tag {
__jmp_buf __jb;
unsigned long __fl;
unsigned long __ss[128/sizeof(long)];
} jmp_buf[1];
typedef jmp_buf sigjmp_buf;
上面的代码是 setjmp.h
里的定义,把一个 struct 类型定义成一个数组。第一次看有点让人疑惑。
typedef
数组的简单的场景:#include
typedef char STR[1024]; // STR是一个长度为1024的char类型数组类型的别名
int main()
{
STR a = "1234"; // -> char a[1024] = "1234";
printf("%s\n", a);
return 0;
}
// 在函数中传递一个一维数组作为参数
void myFunction(int *param); // 传递指针
void myFunction(int param[]); // 未定义大小的数组
void myFunction(int param[10]); // 已定义大小的数组
综合上面两点,我们再看 jmp_buf
的声明,可以看到它是长度为1,struct __jmp_buf_tag
类型数组的别名。
它的好处是这样的:
jmp_buf
的时候,可以把数据分配到堆栈上。// env_buf, array of one element of `struct __jmp_buf_tag`
sigjmp_buf env_buf; // -> struct __jmp_buf_tag env_buf[1];
// env is treated as a pointer of `struct __jmp_buf_tag`
// If the return is from a successful direct invocation, sigsetjmp() returns 0.
// If the return is from a call to siglongjmp(), sigsetjmp() returns a non-zero value.
int sigsetjmp(sigjmp_buf env, int savemask); // set jump point for a non-local goto
// - savemask 表示是否保存当前线程的信号掩码
// After siglongjmp() is completed, program execution continues as if the corresponding invocation of sigsetjmp() had just returned the value specified by val. The siglongjmp() function cannot cause sigsetjmp() to return 0; if val is 0, sigsetjmp() returns the value 1.
// 可以看到 siglongjmp 调用的效果是回到上次 sigsetjmp 调用返回的位置,并且返回值通过 val 指定。如果上次 sigsetjmp 的时候保存了信号掩码,siglongjmp 会恢复保存的信号掩码。
void siglongjmp(sigjmp_buf env, int val); // non-local goto with signal handling
综上其中一种用法如下:
...
sigjmp_buf env;
func()
{
...
siglongjmp(env, 1); // back to last sigsetjmp
}
if (sigsetjmp(env, 1) == 0) {
do_sth();
} else {
// back from siglongjmp
do_sth_else();
}
...