SISPIPE 가 문제가 아니였나 봅니다.

wkdjjywkd의 이미지

초짜 리눅스를 사랑하고 싶은 C 코더.. 입니다.

제가 이번에 서비스서버를 모니터링하는 Client를 만들었습니다.

근데 이놈이 불규칙적으로 죽어버리길래.. 구글링하다보니 SISPIPE를 알게되어서.

signal(SIGPIPE, SIG_IGN); // ignore SIGPIPE
요렇게 막아주었습니다.

네!! 갑자기 죽는일은 없어졌습니다.

하지만.. 이번에는 쓰레드한놈이 의문사 했습니다.. 정확히는 코드가 block 되었습니다.
시그널로 강제종료하였는데(종료시 2초의 딜레이를 주었습니다.) 갑자기 block되었던놈이 디버깅 메시지 몇줄 찍더니 종료되는 것 입니다.

그래서 다시 구글링을 하였더니 mutex의 임계영역 접근이 의심이 들어서

pthread_mutex_lock(&mutex_flag);
pthread_mutex_trylock(&mutex_flag);으로 바꾸고 기약없는 테스트 중입니다.

대략적인 테스트코드를 올려드리오니 저의 무지를 지적해주시고, 답으로 갈수 있는 길을 알려주시면 감사하겠습니다.

int main()
{

signal(SIGINT, SignalHandler);
signal(SIGTERM, SignalHandler);
signal(SIGABRT, SignalHandler);
signal(SIGUSR1, SignalHandler);
signal(SIGPIPE, SIG_IGN);

소켓접속();

pthread_mutex_init(&mutex_flag, NULL);

// Thread condtions initialization
pthread_cond_init(&thread_cond_vm, NULL);
pthread_cond_init(&thread_cond_sys, NULL);
pthread_cond_init(&thread_cond_node, NULL);
pthread_cond_init(&thread_cond_command, NULL);

// Thread creation
pthread_create(&watch_for_thread, NULL, WatchForThread, NULL);
pthread_create(&vm_connect_thread, NULL, VMConnectThread, NULL);
pthread_create(&func_sys_thread, NULL, SysStatThread, &g_sock);
pthread_create(&func_node_thread, NULL, NodeInfoThread, &g_sock);
pthread_create(&func_command_thread, NULL, RecvThread, &g_sock);

// main() launches these four threads and starts waiting for their completion
pthread_join(watch_for_thread, NULL);
pthread_join(vm_connect_thread, NULL);
pthread_join(func_sys_thread, NULL);
pthread_join(func_node_thread, NULL);
pthread_join(func_command_thread, NULL);

return 0;
}

/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/

void *VMConnectThread(){

pthread_mutex_lock(&mutex_flag);
pthread_cond_wait(&thread_cond_vm, &mutex_flag);
pthread_mutex_unlock(&mutex_flag);

if(quit_flag) pthread_exit(NULL);

while(!exit_flag){

if(!tflag_sys_on) {
pthread_mutex_trylock(&mutex_flag);
pthread_cond_wait(&thread_cond_vm, &mutex_flag);
pthread_mutex_unlock(&mutex_flag);
}

if(quit_flag) break;

Aplication block
{
버추얼박스컨트롤, Nmap 등등 ~~
nanosleep(&req, NULL ); 1초
}
}
}
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/

void *SysStatThread(){

pthread_mutex_lock(&mutex_flag);
pthread_cond_wait(&thread_cond_vm, &mutex_flag);
pthread_mutex_unlock(&mutex_flag);

if(quit_flag) pthread_exit(NULL);

while(!exit_flag){

if(!tflag_sys_on) {
pthread_mutex_trylock(&mutex_flag);
pthread_cond_wait(&thread_cond_sys, &mutex_flag);
pthread_mutex_unlock(&mutex_flag);
}

if(quit_flag) break;

Aplication block
{
시스템정보 파싱 후 정보 날렷
nanosleep(&req, NULL ); 1초
}
}
}
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
void *NodeInfoThread(){

pthread_mutex_lock(&mutex_flag);
pthread_cond_wait(&thread_cond_vm, &mutex_flag);
pthread_mutex_unlock(&mutex_flag);

if(quit_flag) pthread_exit(NULL);

while(!exit_flag){

if(!tflag_sys_on) {
pthread_mutex_trylock(&mutex_flag);
pthread_cond_wait(&thread_cond_sys, &mutex_flag);
pthread_mutex_unlock(&mutex_flag);
}

if(quit_flag) break;

Aplication block
{
버추얼박스 정보 파싱 후 정보 날렷
nanosleep(&req, NULL ); 1초
}
}
}

/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/

void *RecvThread(void *_sock) {

int sock = *(int *)_sock;
int RecvMsgSize = -1;
int Connector = 0;
int lArg, oArg = 0;

VM_MESSAGE rd_msg, wr_msg;
NODE_INFO_ACK_t nodeInfo_ack_t;
NODE_CONTROL_ACK_t nodeCont_ack_t;
NODE_CONTROL_t nodeCont_t;
NODE_REFRESH_CMD_t nodeRef_t;

pthread_mutex_lock(&mutex_flag);
pthread_cond_wait(&thread_cond_command, &mutex_flag);
pthread_mutex_unlock(&mutex_flag);

if(quit_flag) pthread_exit(NULL);

while(TRUE) {

if(!tflag_command_on) {
pthread_mutex_lock(&mutex_flag);
pthread_cond_wait(&thread_cond_command, &mutex_flag);
pthread_mutex_unlock(&mutex_flag);
}
if(quit_flag) break;

memset(&rd_msg,0,sizeof(rd_msg));
memset(&nodeInfo_ack_t,0,sizeof(NODE_INFO_ACK_t));
memset(&nodeCont_ack_t,0,sizeof(NODE_CONTROL_ACK_t));
memset(&nodeCont_t,0,sizeof(NODE_CONTROL_t));

RecvMsgSize = 0;

if(quit_flag) pthread_exit(NULL);

recv{
0오면 죽이고 -1 많이쌓이면 쓰레드 멈춰 아니면 너 할거해!!

}

}

/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/

void *WatchForThread() {

struct timespec req;

req.tv_sec = 0;
req.tv_nsec = 500000000;

do {

switch(g_search){
case THREAD_OFF:
fprintf(stderr, "[nodeInfoClient]Sys> Threads suspend.\n\n");

if(!tflag_sys_on) {
tflag_sys_on = TRUE;
pthread_mutex_lock(&mutex_flag);
pthread_cond_signal(&thread_cond_sys);
pthread_mutex_unlock(&mutex_flag);
} else {

tflag_sys_on = FALSE;
}
if(!tflag_node_on) {
tflag_node_on = TRUE;
pthread_mutex_lock(&mutex_flag);
pthread_cond_signal(&thread_cond_node);
pthread_mutex_unlock(&mutex_flag);
} else {
tflag_node_on = FALSE;
}
if(!tflag_vm_on) {
tflag_vm_on = TRUE;
pthread_mutex_lock(&mutex_flag);
pthread_cond_signal(&thread_cond_vm);
pthread_mutex_unlock(&mutex_flag);
} else {
tflag_vm_on = TRUE;
}

g_search = 0;
break;
case THREAD_ON:
fprintf(stderr, "[nodeInfoClient]Sys> Threads restart.\n\n");

if(!tflag_sys_on) {
tflag_sys_on = TRUE;
pthread_mutex_lock(&mutex_flag);
pthread_cond_signal(&thread_cond_sys);
pthread_mutex_unlock(&mutex_flag);
} else {
tflag_sys_on = TRUE;
}
if(!tflag_node_on) {
tflag_node_on = TRUE;
pthread_mutex_lock(&mutex_flag);
pthread_cond_signal(&thread_cond_node);
pthread_mutex_unlock(&mutex_flag);
} else {
tflag_node_on = TRUE;
}
if(!tflag_vm_on) {
tflag_vm_on = TRUE;
pthread_mutex_lock(&mutex_flag);
pthread_cond_signal(&thread_cond_vm);
pthread_mutex_unlock(&mutex_flag);
} else {
tflag_vm_on = TRUE;
}

g_search = 0;
break;
}// switch end

if(!tflag_command_on) {
tflag_command_on = TRUE;
pthread_mutex_lock(&mutex_flag);
pthread_cond_signal(&thread_cond_command);
pthread_mutex_unlock(&mutex_flag);

} else {
tflag_command_on = FALSE;
}

nanosleep(&req, NULL);

}while(!quit_flag);

pthread_cond_broadcast(&thread_cond_sys);
pthread_cond_broadcast(&thread_cond_node);
pthread_cond_broadcast(&thread_cond_command);

pthread_exit(NULL);
}

/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/

대략 이런식입니다. ㅠㅜ

과장님께 육두문자로 사랑받고 있습니다. 도와주세요..

yhsuk의 이미지

일단 trylock을 이렇게 쓰면 안됩니다. pthread_mutex_lock으로 원복해야 합니다.
(mutex의 lock과 unlock은 쌍이 맞아야 합니다. trylock의 리턴값을 확인해서 성공했을 경우에만 unlock을 수행해야 합니다.)

자세히 보진 않았지만, Recv할 게 없어도 Recv로 들어가는 것 같은데, 거기서 블럭되는게 아닐까 합니다.
일단 블럭지점을 확인해야 합니다. pstack PID을 이용해도 되고, 의심가는 지점 전후로 디버그 로그를 더 찍어주든지 해서요.

게다가 WatchForThread가 하는 역할이 0.5초마다 다른 쓰레드를 깨워주는 역할만을 한다면 굳이 mutex와 조건변수를 사용할 필요는 없어 보입니다.
각 쓰레드가 알아서 0.5초씩 쉬면서 할 일 하면 되니까요.
flag변수도 exit_flag, quit_flag가 같은 역할을 하는 것 같은데 딱히 이유가 없다면 하나가 되어야 될테고요.

Signature :) - "여유를 갖고 행동하되 게을러지지 말자"

wkdjjywkd의 이미지

먼저 정말 감사합니다.

lock unlock쪽을 한번 찾아보겠습니다.

WatchForThread는 작업쓰레드, 정보전송관련쓰레드를 flag를 이용하여 조건에 따라서 suspend 및 실행을 합니다.

수신 쓰레드만 계속해서 수신할게 없어도 lock/unlock을 해줍니다.(non-block이라 상관없다고 생각합니다)

디버깅부분과 함께 프로그램 구조도 다시 생각 해 보겠습니다.

감사합니다.

yhsuk의 이미지

다시 소스를 확인하고 문제를 정리해 보았습니다.

1. signal은 큐잉이 되지 않습니다. cond_signal 바로 직후에 cond_wait가 수행되면 계속 블럭될 수 있습니다.
wait 들어가기 전에 체크하는 보초값 변수들은 뮤텍스의 보호를 받아야 합니다.
(아래 FALSE변경시에는 하지 않았습니다, 한 번정도 더 돌면 적용이 되어서)

2. cond_value뿐 아니라 mutex역시 따로 주어야 불필요한 경합을 줄일 수 있습니다. (이 경우엔 문제가 안되어 보이지만)

3. Sys 쓰레드에서 vm 조건변수가 사용되는 등 copy & paste를 해서 생긴 듯한 문제가 있습니다.

4. exit_flag의 역할과 quit_flg역할이 중복됩니다.

5. 불필요한 중복이 여러군데 보입니다.

6. 소스의 순서가 정리되지 않았습니다. (vm, sys, node, command 등의 순서를 지킨다면 일관성 있게)

개인적으로는 아래처럼 정리해 보았습니다.

int quit_flag = 0;
int tflag_vm_on = 0;
int tflag_sys_on = 0;
int tflag_node_on = 0;
int tflag_command_on = 0;
 
int main()
{
    signal(SIGINT, SignalHandler);
    signal(SIGTERM, SignalHandler);
    signal(SIGABRT, SignalHandler);
    signal(SIGUSR1, SignalHandler);
    signal(SIGPIPE, SIG_IGN);
 
    소켓접속();
 
    pthread_mutex_init(&mutex_vm, NULL);
    pthread_mutex_init(&mutex_sys, NULL);
    pthread_mutex_init(&mutex_node, NULL);
    pthread_mutex_init(&mutex_command, NULL);
 
    // Thread condtions initialization
    pthread_cond_init(&thread_cond_vm, NULL);
    pthread_cond_init(&thread_cond_sys, NULL);
    pthread_cond_init(&thread_cond_node, NULL);
    pthread_cond_init(&thread_cond_command, NULL);
 
    // Thread creation
    pthread_create(&watch_for_thread, NULL, WatchForThread, NULL);
    pthread_create(&func_vm_thread, NULL, VMConnectThread, NULL);
    pthread_create(&func_sys_thread, NULL, SysStatThread, &g_sock);
    pthread_create(&func_node_thread, NULL, NodeInfoThread, &g_sock);
    pthread_create(&func_command_thread, NULL, RecvThread, &g_sock);
 
    // main() launches these four threads and starts waiting for their completion
    pthread_join(watch_for_thread, NULL);
    pthread_join(func_vm_thread, NULL);
    pthread_join(func_sys_thread, NULL);
    pthread_join(func_node_thread, NULL);
    pthread_join(func_command_thread, NULL);
 
    return 0;
}
 
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
 
void *VMConnectThread() {
 
    while(!quit_flag) {
        pthread_mutex_lock(&mutex_vm);
        if(!tflag_sys_on)
            pthread_cond_wait(&thread_cond_vm, &mutex_vm);
        pthread_mutex_unlock(&mutex_vm);
 
        Aplication block
        {
            버추얼박스컨트롤, Nmap 등등 ~~
                nanosleep(&req, NULL ); 1}
    }
}
 
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
 
void *SysStatThread() {
 
    while(!quit_flag) {
        pthread_mutex_lock(&mutex_sys);
        if(!tflag_sys_on)
            pthread_cond_wait(&thread_cond_sys, &mutex_sys);
        pthread_mutex_unlock(mutex_sys);
 
        Aplication block
        {
            시스템정보 파싱 후 정보 날렷
                nanosleep(&req, NULL ); 1}
    }
}
 
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
void *NodeInfoThread() {
 
    while(!quit_flag) {
        pthread_mutex_lock(&mutex_node);
        if(!tflag_sys_node)
            pthread_cond_wait(&thread_cond_node, &mutex_node);
        pthread_mutex_unlock(&mutex_node);
 
        Aplication block
        {
            버추얼박스 정보 파싱 후 정보 날렷
                nanosleep(&req, NULL ); 1}
    }
}
 
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
 
void *RecvThread(void *_sock) {
 
    int sock = *(int *)_sock;
    int RecvMsgSize = -1;
    int Connector = 0;
    int lArg, oArg = 0;
 
    VM_MESSAGE rd_msg, wr_msg;
    NODE_INFO_ACK_t nodeInfo_ack_t;
    NODE_CONTROL_ACK_t nodeCont_ack_t;
    NODE_CONTROL_t nodeCont_t;
    NODE_REFRESH_CMD_t nodeRef_t;
 
    while(!quit_flag) {
        pthread_mutex_lock(&mutex_command);
        if(!tflag_command_on)
            pthread_cond_wait(&thread_cond_command, &mutex_command);
        pthread_mutex_unlock(&mutex_command);
 
        memset(&rd_msg,0,sizeof(rd_msg));
        memset(&nodeInfo_ack_t,0,sizeof(NODE_INFO_ACK_t));
        memset(&nodeCont_ack_t,0,sizeof(NODE_CONTROL_ACK_t));
        memset(&nodeCont_t,0,sizeof(NODE_CONTROL_t));
 
        RecvMsgSize = 0;
 
        recv{
            0오면 죽이고 -1 많이쌓이면 쓰레드 멈춰 아니면 너 할거해!!
 
        }
    }
}
 
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
 
void *WatchForThread() {
 
    struct timespec req;
 
    req.tv_sec = 0;
    req.tv_nsec = 500000000;
 
    do {
        switch(g_search) {
            case THREAD_OFF:
                fprintf(stderr, "[nodeInfoClient]Sys> Threads suspend.\n\n");
                tflag_vm_on = FALSE;
                tflag_sys_on = FALSE;
                tflag_node_on = FALSE;
                break;
            case THREAD_ON:
                fprintf(stderr, "[nodeInfoClient]Sys> Threads resume.\n\n");
                pthread_mutex_lock(&mutex_vm);
                tflag_vm_on = TRUE;
                pthread_cond_signal(&thread_cond_vm);
                pthread_mutex_unlock(&mutex_vm);
 
                pthread_mutex_lock(&mutex_sys);
                tflag_sys_on = TRUE;
                pthread_cond_signal(&thread_cond_sys);
                pthread_mutex_unlock(&mutex_sys);
 
                pthread_mutex_lock(&mutex_node);
                tflag_node_on = TRUE;
                pthread_cond_signal(&thread_cond_node);
                pthread_mutex_unlock(&mutex_node);
                break;
        } // switch end
 
        g_search = 0;
 
        pthread_mutex_lock(&mutex_command);
        tflag_command_on = TRUE;
        pthread_cond_signal(&thread_cond_command);
        pthread_mutex_unlock(&mutex_command);
 
        nanosleep(&req, NULL);
    } while(!quit_flag);
 
    pthread_cond_broadcast(&thread_cond_vm);
    pthread_cond_broadcast(&thread_cond_sys);
    pthread_cond_broadcast(&thread_cond_node);
    pthread_cond_broadcast(&thread_cond_command);
 
    pthread_exit(NULL);
}

Signature :) - "여유를 갖고 행동하되 게을러지지 말자"

wkdjjywkd의 이미지

일단 처음에 말씀해주신 것 처럼 디버깅을 해보았습니다.

흐름 상 중복된 부분 필요 없는 부분들이 많이 보였습니다.

소스를 고치고보니 Watch 쓰레드는 리뷰해주신 것 처럼 작성하였고

프로그램 흐름 상 Recv 단에서는 mutex가 필요 없었습니다.

Recv 함수에 재접속 예외처리하는 곧에서 block 되기 때문입니다.

초반에 이 부분이 없이 작성하여서 혼란이 있었 던 것 같습니다.

그리고 추가로 하나의 문제가 더 발생 하였습니다. 재접속시 thread start 가 되고 바로 thread stop이 되는 문제 였습니다.

stop이 되는 도중 start가 될 상황이 생긴 것인데 재접속시 1초의 딜레이를 주니 해결 되었습니다.

이외의 문제가 더 있을 것으로 생각됩니다. 최대한 리뷰하여 제것으로 만들겠습니다.

다시한번 정말 감사드립니다.

감사합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.