关于sigsetjmp,siglongjmp注意点 (UDP超时重传应用)

本文探讨了在UDP编程中实现超时重传机制以提高可靠性的方法,包括Jacobson算法和Karn算法。重点介绍了如何使用alarm作为定时器,并通过sigsetjmp和siglongjmp函数在信号处理中进行高效处理。
问题来源:UDP编程中,应用程序实现的超时重传机制,在计算每个分组的RTO时。在发送请求和接受应答之间要运用超时重传机制,来提高UDP传输的可靠性。

题外话:【Jacobson算法:每次测得一个RTT后就计算RTO以及重传时如何增加RTO。一般当定时器满,实行RTO指数回退。
Karn算法:只有收到不是重传的请求时,才更新RTT并重新计算RTO。】

使用alarm作为定时器,在对SIGALRM信号处理时,不是希望信号处理函数直接返回,而是希望跳转到主进程。这就需要用到setjmp,longjmp或sigsetjmp和siglongjmp函数。
sigsetjmp和siglongjmp函数应用于信号处理函数中的原因是,使用setjmp,longjmp并不对信号屏蔽字有处理,即捕捉到一个信号后,进入到信号处理函数,内核自动把当前信号加到进程的信号屏蔽字中,避免多次中断,然而在这个时候使用longjmp跳出信号处理函数,信号屏蔽字却没有恢复,使得进程再也捕捉不到这个信号。
int sigsetjmp(sigjmp_buf env, int savesigs);
注意点::::若直接调用则返回0,若用siglongjmp返回则返回非零值
void siglongjmp(sigjmp_buf env, int val);

若savesigs非0,则内核在env中保存了信号的屏蔽字。调用siglongjmp时,siglongjmp从env中恢复信号屏蔽字。
测试1:可以多次捕捉SIGALRM
#include "unp.h"
#include <setjmp.h>
#include <time.h>
sigjmp_buf buf;

void func(int signo){
	printf("call func\n");
	siglongjmp(buf,1);
	printf("next will return func\n");
}
int main(int argc, char **argv){
	int nsecs;
	signal(SIGALRM, func);
	alarm(3);
	if(sigsetjmp(buf, 1) == 0){/*调用*/
		printf("call success\n");
		sleep(10);
	}
	else{/*从siglongjmp返回*/
		printf("siglong jmp return\n");
		alarm(0);
		alarm(2);
		exit(0);
	}
}
测试2:第一次SIGALRM捕捉,以后都失去。
#include "unp.h"
#include <setjmp.h>
#include <time.h>
sigjmp_buf buf;

void func(int signo){
	printf("call func\n");
	longjmp(buf,1);
	printf("next will return func\n");
}
int main(int argc, char **argv){
	int nsecs;
	signal(SIGALRM, func);
	alarm(3);
	if(setjmp(buf) == 0){/*调用*/
		printf("call success\n");
		sleep(10);
	}
	else{/*从siglongjmp返回*/
		printf("siglong jmp return\n");
		sleep(0);
		alarm(2);
		alarm(1);
		sleep(5);
		exit(0);
	}
}

### 使用 `setjmp` 和 `siglongjmp` 的注意事项 在C编程中,`setjmp` 函数用于保存程序当前的执行环境到一个特定的位置,而 `siglongjmp` 则用来恢复之前由 `setjmp` 保存下来的环境状态并继续执行。当涉及到信号处理时,推荐使用 `sigsetjmp` 而不是普通的 `setjmp` 来配合 `siglongjmp` 进行非局部跳转操作[^1]。 这是因为标准库中的 `setjmp` 可能不会保留关于阻塞或未决信号的信息,在某些情况下这可能会导致不可预测的行为;相反,`sigsetjmp` 提供了一个标志参数来控制是否要保存这些额外的状态信息,从而使得与 `siglongjmp` 结合更加安全可靠[^2]。 下面是一个简单的例子展示如何正确地将两者结合起来: ```c #include <stdio.h> #include <setjmp.h> #include <signal.h> static jmp_buf env; void handler(int signum){ printf("Caught signal %d\n",signum); siglongjmp(env, 1); // 非本地返回至调用sigsetjmp处 } int main(){ struct sigaction sa; sa.sa_handler = &handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGINT,&sa,NULL) == -1){ perror("Error setting signal action"); return 1; } int ret_val=sigsetjmp(env, 1); if(ret_val==0){ puts("Press Ctrl+C to trigger the jump..."); while(1){} /* Infinite loop until SIGINT is caught */ }else{ puts("Returned from longjump!"); } } ``` 在这个实例里,通过设置SIGINT(通常是Ctrl+C触发)的动作指向自定义处理器函数,并在此函数内部调用了 `siglongjmp()` 。如果一切正常,则按下Ctrl+C之后会跳出无限循环回到最初调用 `sigsetjmp()` 后面的地方继续往下走。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值