출처 : 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 |