输出:
pam:~/test/jmp$ ./goto reach goto 非局部跳转 setjmp 和 longjmp 在C中, goto语句是不能跨越函数的, 而执行这种类型跳转功能的是函数setjmp 和 longjmp, 这两个函数对处理发生在很深层次中的出错情况是很有用的. #include <errno.h> #include <stdarg.h> #include <setjmp.h> #include <signal.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #define MAXLINE 4096 /* max line length */ static jmp_buf env_alrm; unsigned int sleep2(unsigned int); static void sig_int(int); static void err_sys(const char *fmt, ...); static void err_doit(int errnoflag, int error, const char *fmt, va_list ap); int main(void) { unsigned int unslept; if (signal(SIGINT, sig_int) == SIG_ERR) err_sys("signal(SIGINT) error"); unslept = sleep2(5); printf("sleep2 returned: %u\n", unslept); exit(0); } static void sig_int(int signo) { int i, j; volatile int k; /* * Tune these loops to run for more than 5 seconds * on whatever system this test program is run. */ printf("\nsig_int starting\n"); for (i = 0; i < 300000; i++) for (j = 0; j < 4000; j++) k += i * j; printf("sig_int finished\n"); } /* * Fatal error related to a system call. * Print a message and terminate. */ static void err_sys(const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(1, errno, fmt, ap); va_end(ap); exit(1); } /* * Print a message and return to caller. * Caller specifies "errnoflag". */ static void err_doit(int errnoflag, int error, const char *fmt, va_list ap) { char buf[MAXLINE]; vsnprintf(buf, MAXLINE-1, fmt, ap); if (errnoflag) snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s", strerror(errno)); strcat(buf, "\n"); fflush(stdout); /* in case stdout and stderr are the same */ fputs(buf, stderr); fflush(NULL); /* flushes all stdio output streams */ } static void sig_alrm(int signo) { longjmp(env_alrm, 1); printf("%s(%d)\n", __FILE__, __LINE__); } static void print(void) { longjmp(env_alrm, 3); printf("%s(%d)\n", __FILE__, __LINE__); } unsigned int sleep2(unsigned int seconds) { int ret; printf("%s(%d)\n", __FILE__, __LINE__); if (signal(SIGALRM, sig_alrm) == SIG_ERR) return(seconds); ret = setjmp(env_alrm); if ( ret == 0) { alarm(seconds); /* start the timer */ printf("%s(%d): ret = %d\n", __FILE__, __LINE__, ret); pause(); /* next caught signal wakes us up */ } else if (ret == 1) { printf("%s(%d): ret = %d\n", __FILE__, __LINE__, ret); print(); } else { printf("%s(%d): ret = %d\n", __FILE__, __LINE__, ret); } printf("%s(%d)\n", __FILE__, __LINE__); return(alarm(0)); /* turn off timer, return unslept time */ }正常情况下输出:
pam:~/test/jmp$ ./setjmp setjmp.c(92) setjmp.c(98): ret = 0 setjmp.c(103): ret = 1 setjmp.c(108): ret = 3 setjmp.c(111) sleep2 returned: 0解读: void longjmp(jmp_buf env, int val) 中的第二参数回作为 int setjmp(jmp_buf env) 的返回值, 主程序注册SIGINT信号及其处理函数后调用sleep2(5), sleep2 中注册SIGALRM及其信号处理函数, setjmp之前没有longjmp, 所以返回值为0, 然后alarm(5)并等待pause等待信号才继续往下走, 5秒后发出SIGALRM, 调用信号handler: sig_alrm, 运行 longjmp(env_alrm, 1)再次从ret = setjmp(env_alrm);处开是执行, 此时ret=1, 计入ret==1分支, 在调用print(); 再次跳到ret = setjmp(env_alrm);开始往下执行, 此时进入else分支.
5秒内按ctrl-c输出可能是:
pam:~/test/jmp$ ./setjmp setjmp.c(92) setjmp.c(98): ret = 0 ^C sig_int starting sig_int finished setjmp.c(111) sleep2 returned: 1或者
pam:~/test/jmp$ ./setjmp setjmp.c(92) setjmp.c(98): ret = 0 ^C sig_int starting setjmp.c(103): ret = 1 setjmp.c(108): ret = 3 setjmp.c(111) sleep2 returned: 0造成上述原因是,
for (i = 0; i < 300000; i++) for (j = 0; j < 4000; j++) k += i * j;的延时时间比5秒长不到一点点, 如果快速按下的ctrl-c的话就就没有ret=1和ret=3的情况, 如果把300000改成3000000, 4000改成40000的话(远远大于5s), 就总是能输出ret=1和ret=3.
sigsetjmp 和 siglongjmp https://blog.csdn.net/wllinux12138/article/details/82284343