0

setjmpandを使用して状態を保存した後、C プログラムで複数の関数を切り替えようとしてlongjmpいますが、コンテキストを保存できる関数は 1 つだけで、他の 2 つの関数では保存できません。それに対する可能な解決策は何でしょうか。コードに必要な変更が必要かどうかを提案してください。のo/p状態でfun1()は に正常に保存されenv1、元に戻すことができますが、 の場合fun2()fun3()状態は に保存されませenv2env3

サンプル 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

もう1つSIGALRM、これが楽しみの場所です。あなたsiglongjmpenv1。この時点で、スタックは次のようになります。

timer_handler, fun1

それでおしまい。のスタックに何かがある可能性があり、それらにアクセスしても機能する可能性がありfun2ますが、スタックポインターを に巻き戻したので、それらの部分は完全に無効になります。取引を封印するための呼び出し。longjmp できる部分がスタックに残っていたとしても、printf は確実にそれらの部分を上書きします。fun3siglongjmpfun1printf

コードに必要な変更が必要かどうかを提案してください。

必要な変更は、コードを削除し、それを忘れてsetjmp存在longjmpし、二度とこれについて話さないことです。これはできません。setjmp私は、とを使用して貧弱なスレッドの実装が成功したのを見てきましたlongjmpが、 を使用してスレッド用の特別なスタックをセットアップしsigaltstack、適切なロックとクリティカル セクションを実装し、許可された関数呼び出しを非常に限られた関数セットに制限しました。そのようなコードを実行するときに使用printfすることは、動作を定義することから得られる限りです。そして、これの成功した実装はすべて、「これを見て、恐ろしいことではありませんか?」というものでした。親切で、誰もが実際のコードで使用するものではありません。

于 2014-07-21T09:03:19.307 に答える