SISPIPE 가 문제가 아니였나 봅니다.
초짜 리눅스를 사랑하고 싶은 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);
}
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
대략 이런식입니다. ㅠㅜ
과장님께 육두문자로 사랑받고 있습니다. 도와주세요..
일단 trylock을 이렇게 쓰면 안됩니다.
일단 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 :) - "여유를 갖고 행동하되 게을러지지 말자"
소중한 답변 감사드립니다.
먼저 정말 감사합니다.
lock unlock쪽을 한번 찾아보겠습니다.
WatchForThread는 작업쓰레드, 정보전송관련쓰레드를 flag를 이용하여 조건에 따라서 suspend 및 실행을 합니다.
수신 쓰레드만 계속해서 수신할게 없어도 lock/unlock을 해줍니다.(non-block이라 상관없다고 생각합니다)
디버깅부분과 함께 프로그램 구조도 다시 생각 해 보겠습니다.
감사합니다.
다시 소스를 확인하고 문제를 정리해
다시 소스를 확인하고 문제를 정리해 보았습니다.
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 등의 순서를 지킨다면 일관성 있게)
개인적으로는 아래처럼 정리해 보았습니다.
Signature :) - "여유를 갖고 행동하되 게을러지지 말자"
copy &paste 하다가 틀리게 올린 부분이 많았군요.. 죄송합니다.
일단 처음에 말씀해주신 것 처럼 디버깅을 해보았습니다.
흐름 상 중복된 부분 필요 없는 부분들이 많이 보였습니다.
소스를 고치고보니 Watch 쓰레드는 리뷰해주신 것 처럼 작성하였고
프로그램 흐름 상 Recv 단에서는 mutex가 필요 없었습니다.
Recv 함수에 재접속 예외처리하는 곧에서 block 되기 때문입니다.
초반에 이 부분이 없이 작성하여서 혼란이 있었 던 것 같습니다.
그리고 추가로 하나의 문제가 더 발생 하였습니다. 재접속시 thread start 가 되고 바로 thread stop이 되는 문제 였습니다.
stop이 되는 도중 start가 될 상황이 생긴 것인데 재접속시 1초의 딜레이를 주니 해결 되었습니다.
이외의 문제가 더 있을 것으로 생각됩니다. 최대한 리뷰하여 제것으로 만들겠습니다.
다시한번 정말 감사드립니다.
감사합니다.
댓글 달기