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 beginning
18개 기능의 레이어에 대해 에러 메시지를 흘려보낼 필요가 없습니다.
★★★★★★★★★★와 같이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
'programing' 카테고리의 다른 글
서로 다른 보존 정책이 주석에 어떤 영향을 미칩니까? (0) | 2022.06.14 |
---|---|
8비트 문자 이외의 기능이 있는 플랫폼은 어떤 것입니까? (0) | 2022.06.14 |
Nuxt를 사용하여 하나의 프로젝트에 여러 저장소를 구축하는 방법 (0) | 2022.06.13 |
VueJs에서 더티 스테이트를 구현하는 방법 (0) | 2022.06.13 |
Vue 모델이 업데이트되지 않음 (0) | 2022.06.13 |