C 언어에서 신호 처리기를 사용하는 방법은 무엇입니까?

How Use Signal Handlers C Language



이 기사에서는 C 언어를 사용하여 Linux에서 신호 처리기를 사용하는 방법을 보여줍니다. 그러나 먼저 신호가 무엇인지, 프로그램에서 사용할 수 있는 몇 가지 일반적인 신호를 생성하는 방법에 대해 논의한 다음 프로그램이 실행되는 동안 프로그램에서 다양한 신호를 처리하는 방법을 살펴보겠습니다. 시작하겠습니다.

신호

시그널은 어떤 중요한 상황이 도래했음을 프로세스나 스레드에 알리기 위해 생성되는 이벤트입니다. 프로세스 또는 스레드가 신호를 수신하면 프로세스 또는 스레드는 수행 중인 작업을 중지하고 조치를 취합니다. 신호는 프로세스 간 통신에 유용할 수 있습니다.







표준 신호

신호는 헤더 파일에 정의되어 있습니다. 신호.h 매크로 상수로. 신호 이름은 SIG로 시작하고 신호에 대한 간단한 설명이 이어집니다. 따라서 모든 신호에는 고유한 숫자 값이 있습니다. 프로그램은 항상 신호 번호가 아닌 신호 이름을 사용해야 합니다. 그 이유는 신호 번호는 시스템에 따라 다를 수 있지만 이름의 의미는 표준입니다.



매크로 NSIG 정의된 신호의 총 개수입니다. 의 가치 NSIG 정의된 총 신호 수보다 하나 더 큽니다(모든 신호 번호는 연속적으로 할당됨).



다음은 표준 신호입니다.





신호 이름 설명
시업 프로세스를 끊습니다. SIGHUP 신호는 원격 연결이 끊겼거나 끊겼기 때문에 사용자 터미널의 연결이 끊어진 것을 보고하는 데 사용됩니다.
사인트 프로세스를 중단합니다. 사용자가 INTR 문자(보통 Ctrl + C)를 입력하면 SIGINT 신호가 전송됩니다.
시그킷 프로세스를 종료합니다. 사용자가 QUIT 문자(보통 Ctrl + )를 입력하면 SIGQUIT 신호가 전송됩니다.
봉인 불법 지시. 가비지 또는 특권 명령을 실행하려고 하면 SIGILL 신호가 생성됩니다. 또한 SIGILL은 스택이 오버플로되거나 시스템에서 신호 처리기를 실행하는 데 문제가 있을 때 생성될 수 있습니다.
시그랩 추적 트랩. 중단점 명령 및 기타 트랩 명령은 SITRAP 신호를 생성합니다. 디버거는 이 신호를 사용합니다.
시가브트 중단 SIGABRT 신호는 abort() 함수가 호출될 때 생성됩니다. 이 신호는 프로그램 자체에 의해 감지되고 abort() 함수 호출에 의해 보고되는 오류를 나타냅니다.
SIGFPE 부동 소수점 예외. 치명적인 산술 오류가 발생하면 SIGFPE 신호가 생성됩니다.
SIGUSR1 및 SIGUSR2 SIGUSR1 및 SIGUSR2 신호는 원하는 대로 사용할 수 있습니다. 간단한 프로세스 간 통신을 위해 신호를 수신하는 프로그램에서 신호 처리기를 작성하는 것이 유용합니다.

신호의 기본 동작

각 신호에는 다음 중 하나인 기본 작업이 있습니다.

용어: 프로세스가 종료됩니다.
핵심: 프로세스가 종료되고 코어 덤프 파일이 생성됩니다.
점화: 프로세스는 신호를 무시합니다.
중지: 프로세스가 중지됩니다.
계정: 프로세스가 중지된 상태에서 계속됩니다.



기본 동작은 핸들러 기능을 사용하여 변경할 수 있습니다. 일부 신호의 기본 동작은 변경할 수 없습니다. 시그킬 그리고 시가브트 signal의 기본 동작은 변경하거나 무시할 수 없습니다.

신호 처리

프로세스가 신호를 수신하면 프로세스는 해당 신호에 대한 조치를 선택할 수 있습니다. 프로세스는 신호를 무시하거나 처리기 기능을 지정하거나 해당 신호 종류에 대한 기본 작업을 수락할 수 있습니다.

  • 신호에 대해 지정된 작업이 무시되면 신호가 즉시 삭제됩니다.
  • 프로그램은 다음과 같은 함수를 사용하여 핸들러 함수를 등록할 수 있습니다. 신호 또는 시그액션 . 이것을 핸들러가 신호를 포착한다고 합니다.
  • 신호가 처리되거나 무시되지 않은 경우 기본 작업이 수행됩니다.

우리는 다음을 사용하여 신호를 처리할 수 있습니다. 신호 또는 시그액션 기능. 여기에서 우리는 가장 간단한 방법을 봅니다. 신호() 함수는 신호를 처리하는 데 사용됩니다.

정수신호() (정수징후, 무효의 (*기능)(정수))

NS 신호() 부를 것이다 기능 프로세스가 신호를 받으면 함수 징후 . NS 신호() 함수에 대한 포인터를 반환 기능 성공하면 errno에 오류를 반환하고 그렇지 않으면 -1을 반환합니다.

NS 기능 포인터는 세 가지 값을 가질 수 있습니다.

  1. SIG_DFL : 시스템 기본 기능에 대한 포인터입니다. SIG_DFL() , 선언 시간 헤더 파일. 신호의 기본 조치를 취하는 데 사용됩니다.
  2. SIG_IGN : 시스템 무시 기능에 대한 포인터입니다. SIG_IGN() 에서 선언 시간 헤더 파일.
  3. 사용자 정의 핸들러 함수 포인터 : 사용자 정의 핸들러 함수 유형은 무효(*)(int) , 반환 유형이 void이고 int 유형의 인수가 하나 있음을 의미합니다.

기본 신호 처리기 예제

#포함하다
#포함하다
#포함하다
무효의시그_핸들러(정수징후){

// 핸들러 함수의 반환 유형은 void여야 합니다.
인쇄 ('N내부 핸들러 함수N');
}

정수기본(){
신호(사인트,시그_핸들러); // 시그널 핸들러 등록
~을위한(정수NS=1;;NS++){ //무한 루프
인쇄 ('%d : 내부 메인 함수N',NS);
(1); // 1초 동안 딜레이
}
반품 0;
}

Example1.c의 출력 스크린샷에서 main 함수에서 무한 루프가 실행되고 있는 것을 볼 수 있습니다. 사용자가 Ctrl+C를 입력하면 주요 기능 실행이 중지되고 신호의 핸들러 기능이 호출됩니다. 핸들러 함수 완료 후 메인 함수 실행이 재개되었습니다. 사용자가 Ctrl+를 입력하면 프로세스가 종료됩니다.

신호 무시 예

#포함하다
#포함하다
#포함하다
정수기본(){
신호(사인트,SIG_IGN); // 시그널을 무시하기 위한 시그널 핸들러 등록

~을위한(정수NS=1;;NS++){ //무한 루프
인쇄 ('%d : 내부 메인 함수N',NS);
(1); // 1초 동안 딜레이
}
반품 0;
}

여기서 핸들러 함수는 SIG_IGN() 신호 동작을 무시하는 기능입니다. 따라서 사용자가 Ctrl+C를 입력하면 사인트 신호가 생성되고 있지만 동작은 무시됩니다.

신호 처리기 재등록 예제

#포함하다
#포함하다
#포함하다

무효의시그_핸들러(정수징후){
인쇄 ('N내부 핸들러 함수N');
신호(사인트,SIG_DFL); // 기본 동작에 대한 신호 처리기를 다시 등록합니다.
}

정수기본(){
신호(사인트,시그_핸들러); // 시그널 핸들러 등록
~을위한(정수NS=1;;NS++){ //무한 루프
인쇄 ('%d : 내부 메인 함수N',NS);
(1); // 1초 동안 딜레이
}
반품 0;
}

Example3.c의 출력 스크린샷에서 사용자가 처음 Ctrl+C를 입력했을 때 핸들러 함수가 호출되었음을 알 수 있습니다. 핸들러 함수에서 신호 핸들러는 다시 등록합니다. SIG_DFL 신호의 기본 동작을 위해. 사용자가 두 번째로 Ctrl+C를 입력하면 기본 동작인 프로세스가 종료됩니다. 사인트 신호.

신호 보내기:

프로세스는 또한 자신이나 다른 프로세스에 명시적으로 신호를 보낼 수 있습니다. raise() 및 kill() 함수는 신호를 보내는 데 사용할 수 있습니다. 두 함수 모두 signal.h 헤더 파일에 선언되어 있습니다.

정수 들어 올리다 (정수징후)

신호를 보내는 데 사용되는 raise() 함수 징후 호출 프로세스(자체). 성공하면 0을 반환하고 실패하면 0이 아닌 값을 반환합니다.

정수죽임(pid_t pid, 정수징후)

시그널을 보내기 위해 사용되는 kill 함수 징후 에 의해 지정된 프로세스 또는 프로세스 그룹에 PID .

SIGUSR1 신호 처리기 예

#포함하다
#포함하다

무효의시그_핸들러(정수징후){
인쇄 ('내부 핸들러 함수N');
}

정수기본(){
신호(시구스르1,시그_핸들러); // 시그널 핸들러 등록
인쇄 ('메인 함수 내부N');
들어 올리다 (시구스르1);
인쇄 ('메인 함수 내부N');
반품 0;
}

여기서 프로세스는 raise() 함수를 사용하여 SIGUSR1 신호를 자신에게 보냅니다.

Raise with Kill 예제 프로그램

#포함하다
#포함하다
#포함하다
무효의시그_핸들러(정수징후){
인쇄 ('내부 핸들러 함수N');
}

정수기본(){
pid_t pid;
신호(시구스르1,시그_핸들러); // 시그널 핸들러 등록
인쇄 ('메인 함수 내부N');
PID=getpid(); //자신의 프로세스 ID
죽임(PID,시구스르1); // SIGUSR1을 자신에게 보냅니다.
인쇄 ('메인 함수 내부N');
반품 0;
}

여기에서 프로세스 전송 시구스르1 사용하여 자신에게 신호 죽임() 기능. getpid() 자신의 프로세스 ID를 얻는 데 사용됩니다.

다음 예에서 우리는 부모와 자식 프로세스가 다음을 사용하여 통신하는 방법(프로세스 간 통신)을 볼 것입니다. 죽임() 및 신호 기능.

신호를 통한 부모 자식 커뮤니케이션

#포함하다
#포함하다
#포함하다
#포함하다
무효의sig_handler_parent(정수징후){
인쇄 ('부모 : 자녀로부터 응답 신호를 받았습니다.N');
}

무효의sig_handler_child(정수징후){
인쇄 ('Child : 부모로부터 신호를 받았습니다.N');
(1);
죽임(getppid(),시구스르1);
}

정수기본(){
pid_t pid;
만약((PID=포크())<0){
인쇄 ('포크 실패N');
출구 (1);
}
/* 자식 프로세스 */
또 다른 만약(PID==0){
신호(시구스르1,sig_handler_child); // 시그널 핸들러 등록
인쇄 ('아이: 신호를 기다리는 중N');
정지시키다();
}
/* 부모 프로세스 */
또 다른{
신호(시구스르1,sig_handler_parent); // 시그널 핸들러 등록
(1);
인쇄 ('부모: 자식에게 신호 보내기N');
죽임(PID,시구스르1);
인쇄 ('부모: 응답을 기다리는 중N');
정지시키다();
}
반품 0;
}

여기, 포크() 함수는 자식 프로세스를 만들고 자식 프로세스에 0을 반환하고 부모 프로세스에 자식 프로세스 ID를 반환합니다. 그래서 부모 프로세스와 자식 프로세스를 결정하기 위해 pid를 확인했습니다. 부모 프로세스에서는 자식 프로세스가 시그널 핸들러 기능을 등록하고 부모로부터 시그널을 기다릴 수 있도록 1초간 휴면 상태를 유지한다. 1초 후 부모 프로세스 보내기 시구스르1 자식 프로세스에 신호를 보내고 자식의 응답 신호를 기다립니다. 자식 프로세스에서는 먼저 부모의 신호를 기다리고 신호를 받으면 핸들러 함수가 호출됩니다. 핸들러 함수에서 자식 프로세스는 다른 시구스르1 부모에게 신호를 보냅니다. 여기 getppid() 함수는 부모 프로세스 ID를 얻는 데 사용됩니다.

결론

Linux의 Signal은 큰 주제입니다. 이 기사에서 우리는 매우 기본적인 것부터 신호를 처리하는 방법과 신호가 생성되는 방법, 프로세스가 자신과 다른 프로세스에 신호를 보내는 방법, 프로세스 간 통신에 신호를 사용하는 방법에 대한 지식을 얻었습니다.