programing

C에서의 setjmp와 longjmp의 실용적 사용

prostudy 2022. 6. 13. 22:25
반응형

C에서의 setjmp와 longjmp의 실용적 사용

어디에 해 줄 사람 요?setjmp() ★★★★★★★★★★★★★★★★★」longjmp()함수는 임베디드 프로그래밍에서 실질적으로 사용될 수 있습니까?이것들은 에러 처리용인 것을 알고 있습니다.하지만 몇 가지 사용 사례를 알고 싶습니다.

★★★
다른 많은 함수에 내포된 함수에 오차가 있고 최상위 함수에서만 오차를 처리할 수 있다고 가정합니다.

그 사이에 있는 모든 함수가 정상적으로 반환되어 반환값 또는 글로벌오류 변수를 평가하여 더 이상의 처리가 의미가 없거나 심지어 나쁘다고 판단해야 한다면 매우 지루하고 어색할 것입니다.

setjmp/longjmp가 이치에 맞는 상황입니다.이러한 상황은 다른 언어(C++, Java)의 예외가 적용되는 상황과 비슷합니다.


에러 처리 외에 C에서 setjmp/longjmp가 필요한 상황도 생각할 수 있습니다.

코루틴을 실장할 필요가 있는 경우입니다.

다음은 간단한 데모 예시입니다.Sivaprasad Palas로부터의 몇 가지 코드 예에 대한 요구를 충족시키고 setjmp/longjmp가 어떻게 corroutines의 구현을 지원하는지 TheBlastOne의 질문에 답변하기를 바랍니다(비표준 또는 새로운 동작에 근거하지 않는 것으로 보이는 한).

★★★★★★
실제로는 정의되지 않은 동작일 수 있습니다.longjmp (MikeMB의 코멘트를 참조해 주세요.다만, 아직 검증할 기회가 없었습니다).

#include <stdio.h>
#include <setjmp.h>

jmp_buf bufferA, bufferB;

void routineB(); // forward declaration 

void routineA()
{
    int r ;

    printf("(A1)\n");

    r = setjmp(bufferA);
    if (r == 0) routineB();

    printf("(A2) r=%d\n",r);

    r = setjmp(bufferA);
    if (r == 0) longjmp(bufferB, 20001);

    printf("(A3) r=%d\n",r);

    r = setjmp(bufferA);
    if (r == 0) longjmp(bufferB, 20002);

    printf("(A4) r=%d\n",r);
}

void routineB()
{
    int r;

    printf("(B1)\n");

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10001);

    printf("(B2) r=%d\n", r);

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10002);

    printf("(B3) r=%d\n", r);

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10003);
}


int main(int argc, char **argv) 
{
    routineA();
    return 0;
}

을 사용하다
실행 흐름


setjmp/longjmp를 사용할 경우 고려되지 않는 로컬 변수의 유효성에 영향을 미친다는 점에 유의하십시오.
주제에 대한 나의 질문.

이론상으로는 이들 콜체인을 에러 처리에 사용할 수 있기 때문에 체인의 모든 기능에서 에러를 처리할 필요 없이 콜체인을 완전히 네스트 할 수 있습니다.

모든 영리한 이론처럼 현실과 만날 때 이것은 무너진다.중간 기능에서는 메모리 할당, 잠금 해제, 파일 열기 및 청소가 필요한 모든 종류의 작업을 수행합니다. 실제로 ★★★★★★★★★★★★★★★★★★★★★」setjmp/longjmp환경(일부 임베디드 플랫폼)을 완전히 제어할 수 있는 매우 제한된 상황을 제외하고는 일반적으로는 좋지 않은 생각입니다.

, 대부분의 , 부, 부, 부, 부, 부, 부, in, in, in, in, in, in, 를 사용하면setjmp/longjmp콜 내의 할 수 너무 에 이 을 수행해야 .또는 너무 복잡하고 수정이 불가능하기 때문에 해야 합니다.exit에러가 발생했을 경우.

C에 자바와 같은 예외 처리 메커니즘을 작성했습니다.setjmp(),longjmp()및 시스템 기능.할 수 있을 만 아니라, 「예외」와 같은 합니다.SIGSEGV예외 처리 블록의 무한 네스트 기능을 갖추고 있어 함수 호출 전체에서 동작하며 가장 일반적인2가지 스레드화 구현을 지원합니다.으로 하는 과 「」를할 수 있습니다.catch스테이트먼트는 이 트리를 걸어 이 트리가 캐치 또는 패스할 필요가 있는지 확인합니다.

이를 사용한 코드 표시 예를 다음에 나타냅니다.

try
{
    *((int *)0) = 0;    /* may not be portable */
}
catch (SegmentationFault, e)
{
    long f[] = { 'i', 'l', 'l', 'e', 'g', 'a', 'l' };
    ((void(*)())f)();   /* may not be portable */
}
finally
{
    return(1 / strcmp("", ""));
}

다음은 많은 논리를 포함하는 포함 파일의 일부입니다.

#ifndef _EXCEPT_H
#define _EXCEPT_H

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include "Lifo.h"
#include "List.h"

#define SETJMP(env)             sigsetjmp(env, 1)
#define LONGJMP(env, val)       siglongjmp(env, val)
#define JMP_BUF                 sigjmp_buf

typedef void (* Handler)(int);

typedef struct _Class *ClassRef;        /* exception class reference */
struct _Class
{
    int         notRethrown;            /* always 1 (used by throw()) */
    ClassRef    parent;                 /* parent class */
    char *      name;                   /* this class name string */
    int         signalNumber;           /* optional signal number */
};

typedef struct _Class Class[1];         /* exception class */

typedef enum _Scope                     /* exception handling scope */
{
    OUTSIDE = -1,                       /* outside any 'try' */
    INTERNAL,                           /* exception handling internal */
    TRY,                                /* in 'try' (across routine calls) */
    CATCH,                              /* in 'catch' (idem.) */
    FINALLY                             /* in 'finally' (idem.) */
} Scope;

typedef enum _State                     /* exception handling state */
{
    EMPTY,                              /* no exception occurred */
    PENDING,                            /* exception occurred but not caught */
    CAUGHT                              /* occurred exception caught */
} State;

typedef struct _Except                  /* exception handle */
{
    int         notRethrown;            /* always 0 (used by throw()) */
    State       state;                  /* current state of this handle */
    JMP_BUF     throwBuf;               /* start-'catching' destination */
    JMP_BUF     finalBuf;               /* perform-'finally' destination */
    ClassRef    class;                  /* occurred exception class */
    void *      pData;                  /* exception associated (user) data */
    char *      file;                   /* exception file name */
    int         line;                   /* exception line number */
    int         ready;                  /* macro code control flow flag */
    Scope       scope;                  /* exception handling scope */
    int         first;                  /* flag if first try in function */
    List *      checkList;              /* list used by 'catch' checking */
    char*       tryFile;                /* source file name of 'try' */
    int         tryLine;                /* source line number of 'try' */

    ClassRef    (*getClass)(void);      /* method returning class reference */
    char *      (*getMessage)(void);    /* method getting description */
    void *      (*getData)(void);       /* method getting application data */
    void        (*printTryTrace)(FILE*);/* method printing nested trace */
} Except;

typedef struct _Context                 /* exception context per thread */
{
    Except *    pEx;                    /* current exception handle */
    Lifo *      exStack;                /* exception handle stack */
    char        message[1024];          /* used by ExceptGetMessage() */
    Handler     sigAbrtHandler;         /* default SIGABRT handler */
    Handler     sigFpeHandler;          /* default SIGFPE handler */
    Handler     sigIllHandler;          /* default SIGILL handler */
    Handler     sigSegvHandler;         /* default SIGSEGV handler */
    Handler     sigBusHandler;          /* default SIGBUS handler */
} Context;

extern Context *        pC;
extern Class            Throwable;

#define except_class_declare(child, parent) extern Class child
#define except_class_define(child, parent)  Class child = { 1, parent, #child }

except_class_declare(Exception,           Throwable);
except_class_declare(OutOfMemoryError,    Exception);
except_class_declare(FailedAssertion,     Exception);
except_class_declare(RuntimeException,    Exception);
except_class_declare(AbnormalTermination, RuntimeException);  /* SIGABRT */
except_class_declare(ArithmeticException, RuntimeException);  /* SIGFPE */
except_class_declare(IllegalInstruction,  RuntimeException);  /* SIGILL */
except_class_declare(SegmentationFault,   RuntimeException);  /* SIGSEGV */
except_class_declare(BusError,            RuntimeException);  /* SIGBUS */


#ifdef  DEBUG

#define CHECKED                                                         \
        static int checked

#define CHECK_BEGIN(pC, pChecked, file, line)                           \
            ExceptCheckBegin(pC, pChecked, file, line)

#define CHECK(pC, pChecked, class, file, line)                          \
                 ExceptCheck(pC, pChecked, class, file, line)

#define CHECK_END                                                       \
            !checked

#else   /* DEBUG */

#define CHECKED
#define CHECK_BEGIN(pC, pChecked, file, line)           1
#define CHECK(pC, pChecked, class, file, line)          1
#define CHECK_END                                       0

#endif  /* DEBUG */


#define except_thread_cleanup(id)       ExceptThreadCleanup(id)

#define try                                                             \
    ExceptTry(pC, __FILE__, __LINE__);                                  \
    while (1)                                                           \
    {                                                                   \
        Context *       pTmpC = ExceptGetContext(pC);                   \
        Context *       pC = pTmpC;                                     \
        CHECKED;                                                        \
                                                                        \
        if (CHECK_BEGIN(pC, &checked, __FILE__, __LINE__) &&            \
            pC->pEx->ready && SETJMP(pC->pEx->throwBuf) == 0)           \
        {                                                               \
            pC->pEx->scope = TRY;                                       \
            do                                                          \
            {

#define catch(class, e)                                                 \
            }                                                           \
            while (0);                                                  \
        }                                                               \
        else if (CHECK(pC, &checked, class, __FILE__, __LINE__) &&      \
                 pC->pEx->ready && ExceptCatch(pC, class))              \
        {                                                               \
            Except *e = LifoPeek(pC->exStack, 1);                       \
            pC->pEx->scope = CATCH;                                     \
            do                                                          \
            {

#define finally                                                         \
            }                                                           \
            while (0);                                                  \
        }                                                               \
        if (CHECK_END)                                                  \
            continue;                                                   \
        if (!pC->pEx->ready && SETJMP(pC->pEx->finalBuf) == 0)          \
            pC->pEx->ready = 1;                                         \
        else                                                            \
            break;                                                      \
    }                                                                   \
    ExceptGetContext(pC)->pEx->scope = FINALLY;                         \
    while (ExceptGetContext(pC)->pEx->ready > 0 || ExceptFinally(pC))   \
        while (ExceptGetContext(pC)->pEx->ready-- > 0)

#define throw(pExceptOrClass, pData)                                    \
    ExceptThrow(pC, (ClassRef)pExceptOrClass, pData, __FILE__, __LINE__)

#define return(x)                                                       \
    {                                                                   \
        if (ExceptGetScope(pC) != OUTSIDE)                              \
        {                                                               \
            void *      pData = malloc(sizeof(JMP_BUF));                \
            ExceptGetContext(pC)->pEx->pData = pData;                   \
            if (SETJMP(*(JMP_BUF *)pData) == 0)                         \
                ExceptReturn(pC);                                       \
            else                                                        \
                free(pData);                                            \
        }                                                               \
        return x;                                                       \
    }

#define pending                                                         \
    (ExceptGetContext(pC)->pEx->state == PENDING)

extern Scope    ExceptGetScope(Context *pC);
extern Context *ExceptGetContext(Context *pC);
extern void     ExceptThreadCleanup(int threadId);
extern void     ExceptTry(Context *pC, char *file, int line);
extern void     ExceptThrow(Context *pC, void * pExceptOrClass,
                            void *pData, char *file, int line);
extern int      ExceptCatch(Context *pC, ClassRef class);
extern int      ExceptFinally(Context *pC);
extern void     ExceptReturn(Context *pC);
extern int      ExceptCheckBegin(Context *pC, int *pChecked,
                                 char *file, int line);
extern int      ExceptCheck(Context *pC, int *pChecked, ClassRef class,
                            char *file, int line);


#endif  /* _EXCEPT_H */

신호 처리 및 부기 로직을 포함하는 C 모듈도 있습니다.

실행하기가 너무 까다로워서 거의 그만둘 뻔했어요.자바에 최대한 가깝게 하려고 노력했는데, C만으로 얼마나 멀리 갔는지 놀랐습니다.

관심 있으면 연락 주세요.

setjmp ★★★★★★★★★★★★★★★★★」longjmp유닛 테스트에 매우 도움이 됩니다.

다음 모듈을 테스트한다고 가정합니다.

#include <stdlib.h>

int my_div(int x, int y)
{
    if (y==0) exit(2);
    return x/y;
}

보통 테스트하는 함수가 다른 함수를 호출할 경우 stub 함수를 호출하도록 선언할 수 있습니다.이 함수는 특정 흐름의 테스트에 실제 함수가 수행하는 동작을 모방합니다. 이 는 ", " 를 호출합니다.exit하다스터브는 어떻게든 이 동작을 에뮬레이트할 필요가 있습니다. setjmp ★★★★★★★★★★★★★★★★★」longjmp해드릴 수 있어요

이 기능을 테스트하기 위해 다음과 같은 테스트 프로그램을 만들 수 있습니다.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <setjmp.h>

// redefine assert to set a boolean flag
#ifdef assert
#undef assert
#endif
#define assert(x) (rslt = rslt && (x))

// the function to test
int my_div(int x, int y);

// main result return code used by redefined assert
static int rslt;

// variables controling stub functions
static int expected_code;
static int should_exit;
static jmp_buf jump_env;

// test suite main variables
static int done;
static int num_tests;
static int tests_passed;

//  utility function
void TestStart(char *name)
{
    num_tests++;
    rslt = 1;
    printf("-- Testing %s ... ",name);
}

//  utility function
void TestEnd()
{
    if (rslt) tests_passed++;
    printf("%s\n", rslt ? "success" : "fail");
}

// stub function
void exit(int code)
{
    if (!done)
    {
        assert(should_exit==1);
        assert(expected_code==code);
        longjmp(jump_env, 1);
    }
    else
    {
        _exit(code);
    }
}

// test case
void test_normal()
{
    int jmp_rval;
    int r;

    TestStart("test_normal");
    should_exit = 0;
    if (!(jmp_rval=setjmp(jump_env)))
    {
        r = my_div(12,3);
    }

    assert(jmp_rval==0);
    assert(r==4);
    TestEnd();
}

// test case
void test_div0()
{
    int jmp_rval;
    int r;

    TestStart("test_div0");
    should_exit = 1;
    expected_code = 2;
    if (!(jmp_rval=setjmp(jump_env)))
    {
        r = my_div(2,0);
    }

    assert(jmp_rval==1);
    TestEnd();
}

int main()
{
    num_tests = 0;
    tests_passed = 0;
    done = 0;
    test_normal();
    test_div0();
    printf("Total tests passed: %d\n", tests_passed);
    done = 1;
    return !(tests_passed == num_tests);
}

예에서는, 「」를 사용하고 있습니다.setjmp"stubled를 입력합니다.exit은 ""라고 부른다"longjmp테스트 케이스로 바로 돌아갈 수 있습니다.

, 정의되어 있는 「」, 「」는 「」로 되어 있는 해 주세요.exit에는 실제로 , 이 를 사용하여 ", "를 호출합니다._exit그렇게 하기 위해서.이렇게 하지 않으면 테스트 프로그램이 완전히 종료되지 않을 수 있습니다.

임베디드 기기라고 하셨으니, 사용 안 함에 유의할 필요가 있다고 생각합니다.코드 표준으로 금지되어 있는 경우입니다.예를 들어 MISRA(MISRA-C:2004):규칙 20.7) 및 JFS(AV 규칙 20) : "setjmp 매크로 및 longjmp 함수는 사용할 수 없습니다."

「 」의 setjmp ★★★★★★★★★★★★★★★★★」longjmp '초강력'이다goto하십시오." 도도로주주주주사사사 사사사사사다른 했듯이 '아', '아', '아', '아', '아', '아', '아',longjmp 에러 합니다.get me back to the beginning18개 기능의 레이어에 대해 에러 메시지를 흘려보낼 필요가 없습니다.

★★★★★★★★★★와 같이goto하지만, 더 나쁜 것은, 이것을 사용하는 방법을 정말 조심해야 한다는 것입니다. a.longjmp코드 선두로 돌아갑니다.을 주지 setjmp 돌아가다setjmp , , 가 완료되어 .이렇게 하면, 「반초기화」라고 하는 것은,반초기화」 됩니다.setjmp을 사용하다 이런 거 이치노longjmp더 이상 문제를 일으키지 않습니다.물론 하드웨어가 불량 상태인 것을 발견한 임베디드 시스템에서 [에러에 관한 메시지를 저장한 후]를 다음에 실행하는 경우에는 문제가 없습니다.

도 본 적 요.setjmp/longjmp매우 기본적인 스레드 메커니즘을 제공하기 위해 사용됩니다.그러나 이는 매우 특별한 경우이며, "표준" 스레드의 작동 방식은 절대 아닙니다.

편집: 물론 C++가 컴파일된 코드에 예외 포인트를 저장하고 무엇이 예외를 줬는지, 무엇이 청소를 필요로 하는지 알 수 있는 것과 같이 코드를 "deal with cleaning"에 추가할 수 있습니다.여기에는 일종의 함수 포인터 테이블과 "여기서 뛰어내리면 이 함수를 호출하고 이 인수를 사용하여" 저장해야 합니다.다음과 같은 경우:

struct 
{
    void (*destructor)(void *ptr);
};


void LockForceUnlock(void *vlock)
{
   LOCK* lock = vlock;
}


LOCK func_lock;


void func()
{
   ref = add_destructor(LockForceUnlock, mylock);
   Lock(func_lock)
   ... 
   func2();   // May call longjmp. 

   Unlock(func_lock);
   remove_destructor(ref);
}

이 시스템에서는 "C++와 같은 완전한 예외 처리"를 수행할 수 있습니다.하지만 꽤 지저분하고, 코드가 잘 작성되어야만 합니다.

setjmp/longjmp의 가장 중요한 용도는 "비로컬 goto jump"입니다.Goto 명령어(및 goto over for 및 while loops를 사용해야 하는 드문 경우)는 같은 범위에서 가장 안전하게 사용됩니다.goto를 사용하여 범위(또는 자동 할당)를 건너뛸 경우 프로그램 스택이 손상될 가능성이 높습니다.setjmp/longjmp는 점프하는 위치에 스택 정보를 저장함으로써 이를 방지합니다.그런 다음 점프하면 이 스택 정보가 로드됩니다.이 기능이 없으면 대부분의 경우 C 프로그래머는 setjmp/longjmp만이 해결할 수 있는 문제를 해결하기 위해 어셈블리 프로그래밍에 의존해야 합니다.존재해서 다행이다.C 라이브러리의 모든 것은 매우 중요합니다.필요할 때 알게 될 거예요.

에러 처리와 별도로, 지금까지 언급되지 않았던 다른 방법은 스마트한 방법으로 C에서 테일 정류 계산을 구현하는 것입니다.

이것이 실제로 연속 패싱 스타일로 입력 코드를 변환하지 않고 C에서 연속을 구현하는 방법입니다.

언급URL : https://stackoverflow.com/questions/14685406/practical-usage-of-setjmp-and-longjmp-in-c

반응형