在使用setjmp
和longjmp
保存状态后,我们尝试在C程序中切换多个函数,但是对于只有一个函数,我们可以保存上下文而不是其他两个函数。可能是什么解决方案。如果代码中需要进行必要的更改,请建议。
o/p
fun1()
状态env1
已成功保存在fun2()
中,我们可以将其恢复,但如果是fun3()
和env2
州未保存在env3
, i=0,j=999
i=1,j=998
i=2,j=997
i=3,j=996
ch=a
ch=b
ch=c
ch=d
a=0
a=-1
a=-2
a=-3
i=4,j=995/*The previous value of i is preserved*/
i=5,j=996
i=6,j=994
i=7,j=993
ch=<garbagevalue> /*The previous value of ch is lost*/
ch=<garbagevalue>
ch=<garbagevalue>
ch=<garbagevalue>
a=<garbagevalue> /*The previous value of ch is lost*/
a=<garbagevalue>
a=<garbagevalue>
a=<garbagevalue>
i=8,j=992
and so on..
。
样本o / p:
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include<setjmp.h>
#define INTERVAL 5000
void fun1();
void fun2();
void fun3();
void timer_handler(int );
struct itimerval it_val;
jmp_buf env1,env2,env3;
int count=0;
void timer_handler (int signum)
{
if(count==0){
count++;
fun1();
}
if(count==1){
count++;
fun2();
}
if(count==2){
count++;
fun3();
}
L1:
if(count==3){
count++;
siglongjmp(env1,7);
}
if(count==4){
count++;
siglongjmp(env2,7);
}
if(count==5){
count++;
siglongjmp(env3,7);
}
count=3;
goto L1;
}
void fun1()
{
struct sigaction sa;
int i=0,j=999;
/* Install timer_handler as the signal handler for SIGVTALRM. */
memset (&sa, 0, sizeof (sa));
sa.sa_flags=SA_NODEFER;
sa.sa_handler = &timer_handler;
/* Configure the timer to expire after 250 msec... */
it_val.it_value.tv_sec = INTERVAL/1000;
it_val.it_value.tv_usec = (INTERVAL*1000) % 1000000;
it_val.it_interval.tv_sec = INTERVAL/1000;;
it_val.it_interval.tv_usec=(INTERVAL*1000) % 1000000;
sigaction (SIGALRM, &sa, NULL);
setitimer (ITIMER_REAL, &it_val, NULL);
while (1) {
while(sigsetjmp(env1,3)==0){
sleep(1);
printf("i=%d,j=%d\n",i,j);
i++;
j--;
}
}
}
void fun2()
{
struct sigaction sa;
char ch='a';
/* Install timer_handler as the signal handler for SIGVTALRM. */
memset (&sa, 0, sizeof (sa));
sa.sa_handler = &timer_handler;
sa.sa_flags=SA_NODEFER;
sigaction (SIGALRM, &sa, NULL);
/* Configure the timer to expire after 250 msec... */
it_val.it_value.tv_sec = INTERVAL/1000;
it_val.it_value.tv_usec = (INTERVAL*1000) % 1000000;
/* ... and every 250 msec after that. */
it_val.it_interval.tv_sec = INTERVAL/1000;;
it_val.it_interval.tv_usec=(INTERVAL*1000) % 1000000;
/* Start a virtual timer. It counts down whenever this process is*/
setitimer (ITIMER_REAL, &it_val, NULL);
/* Do busy work. */
while (1) {
while(sigsetjmp(env2,2)==0) {
sleep(1);
printf("ch=%c\n",ch);
if(ch=='z')
ch='a';
ch++;
}
}
}
void fun3()
{
struct sigaction sa;
int a=0;
/* Install timer_handler as the signal handler for SIGVTALRM. */
memset (&sa, 0, sizeof (sa));
sa.sa_handler = &timer_handler;
sa.sa_flags=SA_NODEFER;
sigaction (SIGALRM, &sa, NULL);
/* Configure the timer to expire after 250 msec... */
it_val.it_value.tv_sec = INTERVAL/1000;
it_val.it_value.tv_usec = (INTERVAL*1000) % 1000000;
/* ... and every 250 msec after that. */
it_val.it_interval.tv_sec = INTERVAL/1000;;
it_val.it_interval.tv_usec=(INTERVAL*1000) % 1000000;
/* Start a virtual timer. It counts down whenever this process is*/
setitimer (ITIMER_REAL, &it_val, NULL);
/* Do busy work. */
while (1){
while(sigsetjmp(env3,1)==0){
sleep(1);
printf("a=%d\n",a);
a--;
}
}
}
int main ()
{
timer_handler(1);
return 0;
}
代码如下:
{{1}}
答案 0 :(得分:3)
您似乎试图在不管理堆栈的情况下实现某种线程。这不行。忽略代码中几乎所有内容都是未定义行为的事实,让我们看看实际发生了什么。
您先致电timer_handler()
。这会调用fun1
。这将设置一个将调用timer_handler()
的实时计时器。然后fun1
调用setjmp
并休眠。此时你的堆栈看起来像这样:
timer_handler, fun1 (env1 points here), sleep
现在你得到一个SIGALRM
。您执行timer_handler
,这次count
为1,因此您致电fun2
。它与fun1
或多或少相同,你的堆栈看起来像这样:
timer_handler, fun1 (&env1), sleep, timer_handler, fun2 (&env2), sleep
还有一个SIGALRM
。堆栈看起来像这样:
timer_handler, fun1(&env1), sleep, timer_handler, fun2 (&env2), sleep, timer_handler, fun3 (&env3), sleep
还有一个SIGALRM
,这就是有趣的地方。您siglongjmp
到env1
。此时你的堆栈看起来像这样:
timer_handler, fun1
就是这样。 fun2
和fun3
的堆栈中可能存在甚至在您siglongjmp
时可能有效的东西,但是这些部分完全无效,因为您已将堆栈指针重新回到fun1
。致printf
的电话封印了这笔交易。即使堆栈中存在可能存在长时间存在的部分,printf肯定会覆盖这些部分。
请建议代码中是否需要进行必要的更改。
必要的更改是删除代码,忘记setjmp
和longjmp
存在,再也不说这个。你不能这样做。我已经看到使用setjmp
和longjmp
成功实现了糟糕的mans线程,但它们也为sigaltstack
的线程设置了特殊的堆栈,实现了正确的锁定和关键部分并限制了允许函数调用一组非常有限的函数。运行此类代码时使用printf
就可以从已定义的行为中获得。所有成功实现的都是“嘿伙计,看看这个,不是很可怕”,不是任何人都会在实际代码中使用的东西。
答案 1 :(得分:1)
好悲伤!!
我看到您在此段代码中添加了SA_NODEFER
之前的问题。
POSIX先生说:
未指定setitimer()和alarm()或sleep()之间的交互。
这可能足以阻止这种工作......但是,坦率地说,它绝对是可怕的,它不值得工作。
要解决此问题,您需要从信号动作功能中取出所有逻辑。信号是非常非常讨厌的“软件中断”,需要非常小心地处理,最好是手套,护目镜和稳定的手。除非这是setjmp
等的作业,否则我会把它们视为更加讨厌,并且根本不会触及它们。