如何在程序中保存多个函数的状态(上下文)

时间:2014-07-21 08:41:00

标签: c linux context-switch longjmp setjmp

在使用setjmplongjmp保存状态后,我们尝试在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}}

2 个答案:

答案 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,这就是有趣的地方。您siglongjmpenv1。此时你的堆栈看起来像这样:

timer_handler, fun1

就是这样。 fun2fun3的堆栈中可能存在甚至在您siglongjmp时可能有效的东西,但是这些部分完全无效,因为您已将堆栈指针重新回到fun1。致printf的电话封印了这笔交易。即使堆栈中存在可能存在长时间存在的部分,printf肯定会覆盖这些部分。

  

请建议代码中是否需要进行必要的更改。

必要的更改是删除代码,忘记setjmplongjmp存在,再也不说这个。你不能这样做。我已经看到使用setjmplongjmp成功实现了糟糕的mans线程,但它们也为sigaltstack的线程设置了特殊的堆栈,实现了正确的锁定和关键部分并限制了允许函数调用一组非常有限的函数。运行此类代码时使用printf就可以从已定义的行为中获得。所有成功实现的都是“嘿伙计,看看这个,不是很可怕”,不是任何人都会在实际代码中使用的东西。

答案 1 :(得分:1)

好悲伤!!

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

POSIX先生说:

  

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

这可能足以阻止这种工作......但是,坦率地说,它绝对是可怕的,它不值得工作。

要解决此问题,您需要从信号动作功能中取出所有逻辑。信号是非常非常讨厌的“软件中断”,需要非常小心地处理,最好是手套,护目镜和稳定的手。除非这是setjmp等的作业,否则我会把它们视为更加讨厌,并且根本不会触及它们。