본문 바로가기

Programming/Linux_Platform

linux signals

출처 : http://fehead.tistory.com/146


SIGPIPE

파이프가 끊겼을 경우 발생하는 signal

http://blog.naver.com/PostView.nhn?blogId=hyungii&logNo=130081645365


* 파이프깨짐?

소켓은 프로세스간 통신 메카니즘의 확장으로 볼 수 있습니다.

전형적인 유닉스의 프로세스간통신(IPC라고 하지요) 메카니즘은 파이프입니다.

이러한 관습(?)에 힘입어, 소켓에서도 그 연결이 끊기는 경우 파이프가 끊겼다느니, 깨졌다느니, broken pipe등의 메시지를 사용합니다.

파이프깨짐은 바로 통신의 종결을 의미합니다. 

통신이 끊김은 어떻게 감지하는가? 보통 3가지 방법이 있을 수 있습니다.

1) 상태검사 : 소켓도 파일로 간주하므로, 파일의 상태정보를 읽어봅니다. 소위 bad file number등의 에러가 있는지를 검사

2) 읽어 보기 : 읽어 보면 알 수 있습니다. 읽었을때, 0(zero)이 리턴된다면, 원격지가 끊은 것입니다.

3) 써보기 : write해보면 알수 있습니다. write()를 호출했을때, 실제로는 커널(운영체제)에서 write()의 내부동작을 수행하는데, 이때, 원격지가 연결을 끊은 상태이면, 커널은 응용프로그램에게 "야 파이프 끊겼다~ "라는 메시지를 전달합니다. 이는 일단은 시그널(signal)로 전달하게됩니다. 즉, SIGPIPE라는 시그널을 사용자 프로세스에게 보냅니다. 우리가 유닉스 프로그램에서 시그널관련 루틴을 전혀 달지 않아도 기본적으로 시그널핸들러들이 동작하게됩니다. 이는, 유닉스의 기본철학입니다. 전형적인 시그널핸들러의 예제로 Ctrl+C이지요. 프로그램죽일때 누구나, 애용하는 시그널 보내는 키조작이지요. 물론, 기본 시그널핸들러를 수행하지 않고, 사용자가 정의한 시그널핸들러를 수행하게 할 수도 있습니다. signal()이라는 시스템호출을 통해서 할 수 있지요. 특별히, signal()함수에 SIG_IGN을 넣으면, 해당시그널을 무시하도록 하는 핸들러가 수행됩니다. 

여기서 주의할 사항은 해당시그널을 무시했다고 원격지가 끊은 연결이 다시 재연결되고 하거나 하지는 않겠지요? 당연히, 이 경우에 write()함수의 리턴값이 -1이되며, errno를 찍어보면 EPIPE등 파이프관련 에러번호가 세팅될것입니다. 당연히, 에러 처리해야지요.

SIGPIPE가 안뜬다? 운이 좋게도 write()를 호출하지 않아서라고 보면됩니다. 뜨게끔하려면, 원격지(클라이언트/서버)가 먼저 끊게하고 write()한번 호출하면 됩니다. 이는 write()와 sigpipe의 관계를 보이기 위한 어거지 예시입니다.

[출처] Socket 통신시 SIGPIPE 발생??|작성자 지니사랑


참고 : Ignore SIGPIPE without affecting other threads in a process


signal 무시하는 방법

고전적인 signal 함수 사용

signal(SIGPIPE, SIG_IGN);    // sigpipe 무시.


sigaction을 사용

struct sigaction act;

act.sa_handler = SIG_IGN;

sigemptyset( &act.sa_mask );

act.sa_flags = 0;

sigaction( SIGPIPE, &act, NULL );


SIGCHLD

: 자식프로세스가 종료되었을때 오는 signal

http://s2kiess.blog.me/220157649224




실제 예 (Android)


sigpipe 로 죽었다는 것은, pipe 로 연결된 상대방이 연결을 끊고 이후에 pipe 에 무엇을 쓰려 했다는 것을 뜻한다.


Build fingerprint: 'samsung/herolteskt/herolteskt:6.0.1/MMB29K/G930SKSU1APF5:user/release-keys'

Revision: '8'

ABI: 'arm64'

pid: 9383, tid: 9383, name: blkid  >>> /system/bin/blkid <<<

signal 13 (SIGPIPE), code 0 (SI_USER), fault addr --------

    x0   ffffffffffffffe0  x1   0000007fb38d3000  x2   000000000000004c  x3   0000000000001000

    x4   0000000000000000  x5   000000000000004c  x6   0000007fb3a8e48c  x7   0000000000004001

    x8   0000000000000040  x9   0000000000004000  x10  0000000000000000  x11  0000000000004000

    x12  0000000000004001  x13  0000000000000000  x14  0000000000000001  x15  0000000000000034

    x16  0000007fb3af2498  x17  0000007fb3a84578  x18  0000007fb38c2020  x19  000000000000004c

    x20  0000007fb3af5480  x21  000000000000004c  x22  0000007fb38d3000  x23  0000000000001000

    x24  0000007fb3b007b0  x25  0000007fb3d15c00  x26  0000000000000018  x27  0000007fb3cf9000

    x28  0000007fb3a3b298  x29  0000007fd3730c00  x30  0000007fb3a8e4dc

    sp   0000007fd3730c00  pc   0000007fb3a84580  pstate 0000000020000000 


예를 들어 process 를 아래와 같이 수행한 이후 부모프로세스가 죽거나 하면 

실행된 process 가 return 값을 pipe 로 싣어 보내려는 순간 sigpipe 로 죽게 된다.

status_t ForkExecvp(const std::vector<std::string>& args,

        std::vector<std::string>& output, security_context_t context) {

    std::string cmd;

    for (size_t i = 0; i < args.size(); i++) {

        cmd += args[i] + " ";

        if (i == 0) {

            LOG(VERBOSE) << " : " << args[i];

        } else {

            LOG(VERBOSE) << " " << args[i];

        }

    }

    output.clear();


    if (setexeccon(context)) {

        LOG(ERROR) << "Failed to setexeccon";

        abort();

    }

    FILE* fp = popen(cmd.c_str(), "r");

    if (setexeccon(nullptr)) {

        LOG(ERROR) << "Failed to setexeccon";

        abort();

    }


    if (!fp) {

        PLOG(ERROR) << "Failed to popen " << cmd;

        return -errno;

    }

    char line[1024];

    while (fgets(line, sizeof(line), fp) != nullptr) {

        LOG(VERBOSE) << " : " << line;

        output.push_back(std::string(line));

    }

    if (pclose(fp) != 0) {

        PLOG(ERROR) << "Failed to pclose " << cmd;

        return -errno;

    }


    return OK;

}



'Programming > Linux_Platform' 카테고리의 다른 글

close_on_exec  (0) 2015.10.19
socketpair()  (0) 2015.10.19
linux stand out 을 file 에 logging 하기  (0) 2015.10.19
linux namespace  (0) 2015.10.14
Android log command, logwrapper  (0) 2015.09.30