0

在保存状态后,我们试图在 C 程序中的多个函数之间切换setjmplongjmp但是对于一个函数,我们能够保存上下文而不是其他两个函数。可能的解决方案是什么。如果需要在代码中进行必要的更改,请提出建议。在o/p的状态下fun1()成功保存env1,我们可以恢复它,但如果fun2()fun3()状态没有保存在env2, 中env3

样品 o/p:

   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..

代码如下:

#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;
}
4

2 回答 2

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,这就是有趣的地方。你siglongjmpenv1。此时您的堆栈如下所示:

timer_handler, fun1

而已。堆栈上可能有一些东西,fun2fun3siglongjmp使用它们时它们甚至可能会起作用,但是现在这些部分完全无效,因为你已经将堆栈指针倒回到fun1. 呼吁printf达成交易。即使堆栈上可能存在您可以 longjmp 到的幸存部分,printf 也肯定会覆盖这些部分。

如果需要在代码中进行必要的更改,请提出建议。

必要的更改是删除代码,忘记它setjmplongjmp存在并且不再谈论这个。你不能这样做。setjmp我已经看到使用and成功实现了穷人线程longjmp,但他们也为线程设置了特殊堆栈,实现sigaltstack了适当的锁定和关键部分,并将允许的函数调用限制为非常有限的一组函数。在运行此类代码时使用printf尽可能从定义的行为中获得。所有成功的实现都是“看看这个,这不是很可怕吗?” 善良,不是任何人都会在实际代码中使用的东西。

于 2014-07-21T09:03:19.307 回答
1

好悲伤!!

我看到您SA_NODEFER在这段代码中添加了以下您之前的问题。

POSIX 先生说:

setitimer() 和 alarm() 或 sleep() 之间的交互是未指定的。

这可能足以阻止它工作......但是,坦率地说,它非常可怕,不值得工作。

要解决此问题,您需要将所有逻辑从信号操作函数中取出。信号是非常非常讨厌的“软件中断”,需要非常小心地处理,最好戴上手套、护目镜和稳定的手。除非这是setjmp等人的作业,否则我会将它们视为更讨厌的,并且根本避免触摸它们。

于 2014-07-21T09:18:08.180 回答