programing

SIGPINPY를 방지하는 방법(또는 올바르게 처리)

prostudy 2022. 5. 16. 21:00
반응형

SIGPINPY를 방지하는 방법(또는 올바르게 처리)

나는 TCP나 로컬 유닉스 소켓의 연결을 받아들이고 간단한 명령을 읽고 (명령에 따라) 회신을 보내는 작은 서버 프로그램을 가지고 있다.

문제는 고객이 답변에 관심이 없을 수 있고 때로는 일찍 퇴근할 수도 있다는 점이다.그래서 그 소켓에 쓰는 것은SIGPIPE내 서버를 망가뜨릴 거야

여기서 충돌을 막기 위한 최선의 방법은 무엇인가?줄의 반대쪽이 아직도 읽고 있는지 확인할 수 있는 방법이 있을까?(select()소켓이 쓰기 가능하다고 항상 말하는 것처럼 여기서는 작동하지 않는 것 같다.아니면 그냥 잡을까?SIGPIPE그걸 무시하는 게 어때?

당신은 일반적으로 그 일을 무시하길 원한다.SIGPIPE코드에서 직접 오류를 처리하십시오.C의 신호 핸들러에는 그들이 할 수 있는 것에 대한 제약이 많기 때문이다.

이렇게 하는 가장 편리한 방법은SIGPIPE에 대한 취급자.SIG_IGN이렇게SIGPIPE신호를 보내다

을 무시하다SIGPIPE신호, 다음 코드를 사용하십시오.

signal(SIGPIPE, SIG_IGN);

만약 당신이 그것을 사용하고 있다면.send() 또 , 다다를 하는 이다.MSG_NOSIGNAL선택사항, 그 선택사항으로 인해SIGPIPE통화당 행동.일부 운영 체제가 다음 작업을 지원하는 것은 아니라는 점에 유의하십시오.MSG_NOSIGNAL깃발을 꽂다

마지막으로, 여러분은 또한 다음 사항들을 고려해 보는 것이 좋을 것이다.SO_SIGNOPIPE설정할 수 있는 소켓 플래그setsockopt()어떤 운영체제에서.이것은 예방할 것이다.SIGPIPE쓰기 작업으로 인해 발생한 소켓을 설정했다.

또 다른 방법은 소켓을 변경하여 쓰기()에 SIGPIPPY를 생성하지 않도록 하는 것이다.이는 SIGPIPPY용 글로벌 신호 처리기를 원하지 않을 수 있는 라이브러리에서 더욱 편리하다.

대부분의 BSD 기반(MacOS, FreeBSD...) 시스템에서 (C/C++를 사용한다고 가정할 때) 다음을 사용하여 이 작업을 수행할 수 있다.

int set = 1;
setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));

이를 통해 SIGPIPER 신호가 생성되는 대신 EPIPE가 반환된다.

파티에 너무 늦었지만SO_NOSIGPIPE휴대할 수 없고, 시스템에서 작동하지 않을 수 있음(BSD 같은 것임)

예를 들어, Linux 시스템을 사용하지 않고 사용하는 경우 좋은 대안SO_NOSIGPIPE에 관한 것이다.MSG_NOSIGNAL발신(2)에 플래그를 표시하십시오.

교체 예 replacing 예write(...)에 의해send(...,MSG_NOSIGNAL)(노바의 코멘트 참조)

char buf[888];
//write( sockfd, buf, sizeof(buf) );
send(    sockfd, buf, sizeof(buf), MSG_NOSIGNAL );

현대적인 POSIX 시스템(즉, Linux)에서는, 를 사용할 수 있다.sigprocmask()기능을 발휘하다

#include <signal.h>

void block_signal(int signal_to_block /* i.e. SIGPIPE */ )
{
    sigset_t set;
    sigset_t old_state;

    // get the current state
    //
    sigprocmask(SIG_BLOCK, NULL, &old_state);

    // add signal_to_block to that existing state
    //
    set = old_state;
    sigaddset(&set, signal_to_block);

    // block that signal also
    //
    sigprocmask(SIG_BLOCK, &set, NULL);

    // ... deal with old_state if required ...
}

나중에 이전 상태를 복원하려면 다음 항목을 저장하십시오.old_state안전한 곳에그 기능을 여러 번 호출하면 스택을 사용하거나 첫 번째 또는 마지막만 저장하면 된다.old_state 수 ... 또는 특정 차단 신호를 제거하는 기능이 있을 수 있다.

자세한 내용은 man 페이지를 참조하십시오.

로컬로 SIGPIPY 처리

일반적으로 글로벌 신호 이벤트 핸들러보다는 로컬에서 오류를 처리하는 것이 가장 좋으며, 로컬에서 진행 중인 내용과 취해야 할 조치에 대해 더 많은 맥락을 가질 수 있기 때문이다.

내 앱 중 하나에 외부 액세서리와 통신할 수 있는 통신 계층이 있어.쓰기 오류가 발생하면 나는 통신 계층에서 예외를 던지고 거기서 그것을 처리하기 위해 그것을 캐치 블록까지 거품이 일도록 한다.

코드:

로컬로 처리할 수 있도록 SIGPIPY 신호를 무시하는 코드는 다음과 같다.

// We expect write failures to occur but we want to handle them where 
// the error occurs rather than in a SIGPIPE handler.
signal(SIGPIPE, SIG_IGN);

이 코드는 SIGPIPER 신호가 상승하는 것을 방지하지만, 소켓 사용 시 읽기/쓰기 오류가 발생하므로, 그 여부를 확인해야 한다.

아니면 그냥 핸들러로 SIGPIPPY를 잡아서 무시할까?

나는 그것이 옳다고 믿는다.다른 쪽 끝의 설명자가 닫혔을 때 SIGPIPPY가 알려 주는 내용을 알고 싶을 겁니다.

샘.

여기서 충돌을 막기 위한 최선의 방법은 무엇인가?

모든 사용자에 따라 시그니처를 비활성화하거나 오류를 포착하고 무시하십시오.

줄의 반대쪽이 아직도 읽고 있는지 확인할 수 있는 방법이 있을까?

예, 선택()을 사용하십시오.

항상 소켓이 쓰기 가능하다고 해서 셀렉터블이 여기서 작동하지 않는 것 같다.

읽기 비트를 선택해야 한다.당신은 아마도 쓰기 비트를 무시할 수 있을 것이다.

맨 끝에서 파일 핸들을 닫으면 데이터를 읽을 준비가 되었음을 알려준다.당신이 가서 그것을 읽으면, 당신은 0바이트를 돌려받을 것이고, 이것이 OS가 파일 핸들이 닫혔다고 알려주는 방식이다.

쓰기 비트를 무시할 수 없는 유일한 시간은 대용량 볼륨을 전송하는 경우일 뿐이고, 다른 쪽 끝은 백로그될 위험이 있어 버퍼가 채워질 수 있다.그런 경우 파일 핸들에 쓰려고 하면 프로그램/스레드가 차단되거나 실패할 수 있다.쓰기 전에 테스트를 선택한다고 해서 그것으로부터 보호받을 수는 있지만, 다른 쪽 끝이 건강하거나 데이터가 도착한다는 것을 보장하지는 않는다.

문장 작성 시뿐만 아니라 가까운()에서 시그파이프를 얻을 수 있다는 점에 유의하십시오.

닫기 버퍼링된 데이터를 플러시하십시오.다른 쪽 끝은 이미 닫혔으면 닫기가 실패하고 시그피프가 나온다.

버퍼링된 TCPIP를 사용하는 경우, 성공적인 쓰기는 데이터가 전송 대기열에 있음을 의미하며, 전송되었다는 의미는 아니다.성공적으로 클로즈업되기 전까지는 데이터가 전송되었는지 알 수 없다.

Sigpipe는 당신에게 뭔가 잘못되었다고 말한다. 그것은 당신에게 무엇을, 또는 무엇을 해야 하는지를 말해주지 않는다.

게시물에서 SO_NOSIGPIGPY와 MSG_NOSIGNAL 중 어느 것도 사용할 수 없는 Solaris 사례에 대한 가능한 해결책을 설명했다.

대신 라이브러리 코드를 실행하는 현재의 스레드에서 일시적으로 SIGPINPY를 억제해야 한다.SIGPIPPY를 억제하기 위해서는 먼저 SIGPIPPIN이 보류 중인지 확인해야 한다.만약 그렇다면, 이것은 이 실에 막혔다는 것을 의미하며, 우리는 아무것도 할 수 없다.도서관이 추가로 SIGPIPY를 생성하면 미결 SIGPIPY와 병합이 되는데, 그건 불가.SIGPIPY가 미결 상태라면 이 스레드에서 차단하고, 이미 차단되었는지도 확인한다.그러면 우리는 자유롭게 우리의 글을 실행할 수 있다.SIGPIPY를 원래 상태로 복원하려고 할 때, 우리는 다음을 수행한다: SIGPIPY가 원래 보류 중이었다면, 우리는 아무것도 하지 않는다.그렇지 않으면 우리는 그것이 지금 미결인지 확인한다.만일 (즉, 아웃 액션이 하나 이상의 SIGPIPY를 생성했다는 것을 의미)한다면, 우리는 이 스레드에서 대기하고, 따라서 보류 상태를 삭제한다(이를 위해 우리는 시간 초과가 0인 sigtimed wait()를 사용한다). 이는 악의적인 사용자가 전체 프로세스에 SIGPIPY를 수동으로 보낸 시나리오에서 차단을 피하기 위함이다. 이 경우 보류 중인 것으로 볼 수 있지만,우리가 그것을 기다리기 전에 r 실이 그것을 처리할 수 있을 것이다).보류 중인 상태를 지운 후 이 스레드에서 SIGPIPY를 차단 해제하지만 원래 차단되지 않은 경우에만 차단 해제하십시오.

https://github.com/kroki/XProbes/blob/1447f3d93b6dbf273919af15e59f35cca58fcc23/src/libxprobes.c#L156의 예 코드

파이프의 맨 끝에 있는 공정이 빠져나가는 것을 막을 수 없으며, 다 쓰기 전에 공정이 빠져나간다면 SIGPIPPY 신호가 들어온다.만약 당신이 SIG_IGN 신호를 보낸다면, 당신의 쓰기는 오류와 함께 되돌아올 것이고, 당신은 그 오류를 기록하고 반응할 필요가 있다.핸들러에서 신호를 잡아서 무시하는 것 만으로는 좋은 생각이 아니다. 이제 파이프가 없어진 것을 알고 프로그램의 행동을 수정하여 파이프에 다시 쓰지 않도록 해야 한다(신호가 다시 생성되고 무시되고, 다시 시도하면 다시 시도하게 되고, 전체 과정이 오랫동안 진행되어 CPU가 많이 낭비될 수 있기 때문이다).권력에 호소하다

Linux 설명서에는 다음과 같이 나와 있다.

EPIPE 연결 지향 소켓에서 로컬 엔드가 종료됨.이 경우 MSG_NOSIGNAL이 설정되지 않은 한 프로세스도 SIGPINEL을 받게 된다.

그러나 우분투 12.04에게는 맞지 않는다.나는 그 케이스에 대한 테스트를 작성했고 나는 항상 SIGPIPER와 함께 EPIPER를 받는다. SIGPIPER는 같은 고장난 소켓에 두 번 쓰려고 하면 유전자가 처리된다.그러므로 만약 이 신호가 발생한다면, 당신은 SIGPINPY를 무시할 필요가 없다. 그것은 당신의 프로그램에서 논리 오류를 의미한다.

참조URL: https://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly

반응형