nonblock select - thread poool 관련 질문입니다.

sunbee의 이미지

질문드립니다.
전체 프로그램 구조는

main(parent) - fork1(child) - create thread1 (thread pool)
             				- create thread2 (thread pool)
		             		- create thread3 (thread pool)

nonblocking 이며,
parent에서는 client를 accept 합니다. 그리고 accept된 client socket을 child에게 넘깁니다.
child에서는 client에서 접속이 들어오면 client socket을 select() fd_set에 등록하며,
select() 합니다.

<child process code>

	select();
    if (ret_val == 0 || ret_val == -1) continue;

	if(pthread_mutex_lock(&mutex_client) < 0) return;
    for (i = 0; i < MAX_RECORD; i++) {

        if(hmt_clients.socket[i].sockfd > 0) {
            if(FD_ISSET (hmt_clients.socket[i].sockfd, (fd_set *)&allfds)) {

                memset(cli_msg.msg, '\0', MSG_BUFFER);
                cli_msg.event_fd = hmt_clients.socket[i].sockfd;

                if ((ret_val = receive_message (cli_msg.event_fd,
                                    (char *)cli_msg.msg, MSG_BUFFER)) < 0) {

printf("Disconnect \n");
                    disconnect_sockfd(cli_msg.event_fd, i, &readfds);
                }
                else {
                    /* 놀고있는 thread에게 일을 시킨다. */
                    go_work();
				}
			}
		}
	}
	pthread_mutex_unlock(&mutex_client);

select()하면서 event가 발생하면 thread pool에서 놀고 있는 thread를 꺼내어 일을 시킵니다.

<thread function>

	pthread_mutex_lock(&t_pool.mutex);
	while(t_pool.nready == 0)
	    pthread_cond_wait(&t_pool.cond, &t_pool.mutex);
	
	t_pool.nready = 0;

	/* working */	
	printf("event [%d]th thread [%d] !!! : [%s]\n",my_num, cli_msg.event_fd, cli_msg.msg);
	if(writen(cli_msg.event_fd, READY_PACKET1, strlen(READY_PACKET1)) < 0) {
		printf("응답 실패 \n");
		return;
	}
	pthread_mutex_unlock(&t_pool.mutex);

client 하나가 접속했을경우는 문제가 없이 잘 됩니다.
두번째 client가 접속했을 경우 첫번째 client는 먹통이 되어버리고, 두번째 들어온 client에게만 서비스 합니다.

첫번째 client의 프로세스를 체크해보니 read() 에서 멈춰져 있습니다.
위 코드의 writen(.....)을 수행하지 못했단 말인데요...
왜 수행을 못하는지 이해가 안되는군요..

thread를 이용하지 않고 그냥 select() 해서 처리를 해주면 잘되거덩요.. 근데 thread를 쓰면 이런 현상이 발생하는군요..
고수님들의 조언을 듣고싶습니다.

Tony의 이미지

도무지 두번을 읽어봐도 질문이 뭔지 도통 알수가 없네요. thread프로그램이나 select등에대해 잘 이해를 못하고 질문을 하신게 아닌가 싶습니다.

sunbee의 이미지

제가 표현을 잘 못했나봅니다..

좀더 첨언하자면..

동시 접속자수가 1000명 이상인 서버프로그램을 제작중입니다.
select()로는 1024가 한계니 프로세스를 생성해야겠죠..
생성된 프로세스당 thread pool을 생성합니다..
그게 전체 구조 그림이죠..

<프로그램 구조>

main(parent) 	
		- fork(child1) 	- create thread1
             			- create thread2
		             	- create thread3
		- fork(child2) 	- create thread1
             			- create thread2
		             	- create thread3
		- fork(child3) 	- create thread1
             			- create thread2
		             	- create thread3

위에서 parent는 accept()만 합니다.
accept()된 client는 child에게 넘기고 다시 accept()합니다.

child들은 accept()된 client를 select() 합니다.
select()하다가 client에게서 요청이 들어오면(event가 들어오면) thread에게 작업을 시킵니다.

여기서... thread를 빼고 동작을 하면 잘 됩니다.
이말은 위 <child process code> 중에서

Quote:

/* 놀고있는 thread에게 일을 시킨다. */
go_work();

이 부분을 주석처리 한다는 말입니다.
즉 i/o multiplexing에는 문제가 없다는 말이겠죠....

그런데 위 코드를 추가하면
client 하나가 접속했을경우는 문제가 없이 잘 됩니다.
두번째 client가 접속했을 경우 첫번째 client는 먹통이 되어버리고, 두번째 들어온 client에게만 서비스 합니다.

Quote:

첫번째 client의 프로세스를 체크해보니 read() 에서 멈춰져 있습니다.

이 말은 위 코드(<thread function>)의 writen(.....)을 수행하지 못했단 말입니다.

왜 못하냐! 이게 제 질문입니다..^_^ 이해되셨나요?

그리고 thread에서 하는 일은 현재코드에서는 단지 받은 메세지를 출력하고, client에게 잘받았다는 응답을 해주는일만 하고 있습니다.

IsExist의 이미지

설명이 약간 이상한듯 하여 질문합니다.

parent는 accept() 하는게 아니라 listen() 하는게 아닌지?
child는 parent가 linsten() 한 후 fork 한뒤 소켓을 각기 accept 할거 같군요.

accept() 리턴된 fd를 thread 한테 돌린다는 건지(아니면 fd를 non 블럭으로 만든건지). 내용상으로는 non 블럭 얘기가 없네요. 그럼 child의 accept() 에서 블럭되고, 스레드에서 다시 block 될겁니다.

그런데 좀 이해가 안되는게 왜 연결된 fd를 다시 thread 들이 select 하나요? client의 요청이 메시지 단위여서 그렇게 한건가요?

쓰레드를 사용할때 동작이 안된다고 했는데 클라이언트한테 서비스 하는 스레드 id를 각기 출력 해보세요. 설명으로 봐서는 한개의 스레드가 다수의 클라이언트를 서버스하는 형태는 아닌거 같은데 결과 현상으로 봐서는 그렇게 된거 같네요.

---------
간디가 말한 우리를 파괴시키는 7가지 요소

첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스

이익추구를 위해서라면..

다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치

sunbee의 이미지

Quote:

parent는 accept() 하는게 아니라 listen() 하는게 아닌지?
child는 parent가 linsten() 한 후 fork 한뒤 소켓을 각기 accept 할거 같군요.

accept() 리턴된 fd를 thread 한테 돌린다는 건지(아니면 fd를 non 블럭으로 만든건지). 내용상으로는 non 블럭 얘기가 없네요. 그럼 child의 accept() 에서 블럭되고, 스레드에서 다시 block 될겁니다.

parent는 listent도 하고 accept도 합니다.
parent는 block socket 입니다.
client에서 접속이 들어오면 child에게 접속된 client socket을 넘기죠...
child 는 nonblock 입니다. 접속된 client들을 select() 하죠..
이해됐습니까~?

Quote:

그런데 좀 이해가 안되는게 왜 연결된 fd를 다시 thread 들이 select 하나요? client의 요청이 메시지 단위여서 그렇게 한건가요?

thread가 select하지 않습니다. child가 select하죠...^_^
thread는 단지 작업만 수행할 뿐입니다.

Quote:

쓰레드를 사용할때 동작이 안된다고 했는데 클라이언트한테 서비스 하는 스레드 id를 각기 출력 해보세요. 설명으로 봐서는 한개의 스레드가 다수의 클라이언트를 서버스하는 형태는 아닌거 같은데 결과 현상으로 봐서는 그렇게 된거 같네요.

ㅡㅡ;; 그렇진 않습니다.

답변감사드립니다.

익명 사용자의 이미지

그럼 go_work()가 문제네요. 그쪽이 올려보세요.

thread를 어떻게 pool에서 돌렸나요?

익명 사용자의 이미지

go_work가 아니라면 mutex가 수상하네요...

alsong의 이미지

writen에서 오류가 났는지 체크해보시고
return앞에 반드시 unlock해주세요.
위 루틴은 오류시에 블럭이 발생합니다.

위의 설명내용만 가지고 좀더 정확하게 짚어드리기는 힘드네요.

추가로
cli_msg.event_fd변수가 공유(?)되고 있을 지도 모르겠군요.
변수 생성이나 선언부가 없어서 확실한 답을 못드리겠고요..
만약 두Thread가 같이 사용한다면 .. 그럴 가능성이 있습니다.

첫번째 fd가 없어진 현상과 비슷한데 참고 하시길.

그나저나 백수 언제 탈출하냐... ㅡㅡ; 배고파라.

익명 사용자의 이미지

hmt_clients 와 cli_msg 구조체는 공유변수입니다.
이 두 구조체는 mutex_client 하나의 mutex로 접근 제어를 합니다.

thread pool은 그 자체 mutex 하나와 cond 하나를 사용하고 있고요..

Quote:

cli_msg.event_fd변수가 공유(?)되고 있을 지도 모르겠군요

맞습니다.. 공유되고 있는셈이죠.... 이게 문제가 될 수 있습니까?? 확인해 봐야겠습니다.
write에서 에러처리는 해줬습니다.

그리고 go_work() 소스는

    pthread_mutex_lock(&t_pool.mutex);
    if(t_pool.full >= 3)
        return ;

    if(t_pool.nready == 0)
        pthread_cond_signal(&t_pool.cond);

	t_pool.full++;
    t_pool.nready = 1;
    pthread_mutex_unlock(&t_pool.mutex);

대기중인 thread에게 signal을 보내는 행위만 하고요,
실제 작업을 하는 thread_func의 소스는

	while(1) {
        pthread_mutex_lock(&t_pool.mutex);

        while(t_pool.nready == 0)
            pthread_cond_wait(&t_pool.cond, &t_pool.mutex);

        t_pool.nready = 0;
        t_pool.full++;

		printf("event [%d]th thread [%d] !!! : [%s]\n",my_num, cli_msg.event_fd, cli_msg.msg);

		if(writen(cli_msg.event_fd, READY_PACKET1, strlen(READY_PACKET1)) < 0) {
	        pthread_mutex_unlock(&t_pool.mutex);
			return;
		}

        t_pool.full++;
        pthread_mutex_unlock(&t_pool.mutex);
	}

테스트를 해보니 두번째 client의 연결이 들어오면 방금 들어온 client에게 서비스를 해주느라
첫번째 client 에게 서비스를 못해주는것 같습니다.
두번째 client의 연결을 끊으면 첫번째 client에게 서비스를 해주거든요....
이것참.. 머리아프네요... ㅡㅡ;;
해결 방안이 안떠오르네욤...

관심가져주신 모든분들께 감사드립니다...^_^ 복 받으실겁니다.

rasungboy의 이미지

pthread_mutex_lock(&t_pool.mutex); 
    if(t_pool.full >= 3) 
        return ; 

왜 락걸고 락 안풀어주고 리턴하나요? ^^;;

sunbee의 이미지

네..^^;;

full 날경우 block하게 만들고 있습니다.. 테스트 중이니까요..

아직까지는 full상황이 발생하지는 않는군요.... 감사합니다.

익명 사용자의 이미지

현재 프로그램의 문제점을 떠나서 pool의 의미가 사라져 버린것 같습니다. 정확한 의도는 모르겠지만 뮤텍스의 목적이 fd를 공유하기 때문이라면 공유하지 마시고 각Thread에게 별도로 fd를 넘기시기 바랍니다.

일단 뮤텍스의 정확한 목적을 세우시는것도 좋을것 같습니다.

참고로 지금 형태의 프로그램구조(뮤텍스의 사용에의해)는 모양만 몇개의 Thread로 나눠 놓은 상태이고 실제 작동은 단일 프로세스와 다름없이 설계되어 있습니다. thread안에 뮤텍스 없이 만들수 있는 방법을 찾아 보세요.(현재 수준의 기능이라면 가능할것 같습니다.)

alsong의 이미지

그리고 계속 증가만 되고 있는것 같은데..
t_pool.full++;변수의 cnt가 감소되는 부분이 없다면...

그나저나 백수 언제 탈출하냐... ㅡㅡ; 배고파라.

익명 사용자의 이미지

Quote:

그리고 계속 증가만 되고 있는것 같은데..
t_pool.full++;변수의 cnt가 감소되는 부분이 없다면...

ㅋㅋㅋ 이런.. 질문하느라.. copy&paste 를 하다보니 저렇게 됐네욤.. ㅡㅡ;;
저기 full++ 하는 부분은 질문과 상관없는 곳이니 문제 삼지 마시길..
지송....함당...

Quote:

정확한 의도는 모르겠지만 뮤텍스의 목적이 fd를 공유하기 때문이라면 공유하지 마시고 각Thread에게 별도로 fd를 넘기시기 바랍니다.

공유번수를 쓴다는 자체가 thread에게 fd를 넘기는 방법 아닌가요~?
저는 그렇게 알고 있었습니다..
다른 thread에게 어떤방식으로 fd를 넘기죠~?

익명 사용자의 이미지

프로세스 ------- thread1
thread2
thread3

fd를 thread1, 2, 3가 같이 사용하고 있나요?(소스상으론 알수가 없어서) fd를 저장하고 있는곳이 3군데인가요..그럼 관계없고요.
fd변수의 선언부를 보여주세요...

제가 이야기한 아래 단락에 중점을 두시기 바랍니다.

Quote:
참고로 지금 형태의 프로그램구조(뮤텍스의 사용에의해)는 모양만 몇개의 Thread로 나눠 놓은 상태이고 실제 작동은 단일 프로세스와 다름없이 설계되어 있습니다. thread안에 뮤텍스 없이 만들수 있는 방법을 찾아 보세요.(현재 수준의 기능이라면 가능할것 같습니다.)

이부분을 이해하면 fd의 이야기가 저절로 이해가 될실겁니다.
오류를 꼭 잡으시길 :)
익명 사용자의 이미지

공유부분은 프로세스와 3개의 Thread모두(결국 1개의 전역변수)가 공유하는지
아니면 프로세스와 각Thread하나(결국 3개의 전역변수)와 공유하는지의 내용입니다.

sunbee의 이미지

typedef struct _Hmt_Clients{
    Socket socket[MAX_RECORD];
    int count;
}Hmt_Clients;

/*
** client 에서 요청받은 메세지
*/
typedef struct _Cli_Msg{
    char    msg[MSG_BUFFER];
    int     event_fd;
}Cli_Msg;

Hmt_Clients hmt_clients;
Cli_Msg cli_msg;

/* for hmt_clients, cli_msg */
pthread_mutex_t mutex_client;

이렇게 선언했습니다.
결국 공유변수는 프로세스와 3개의 thread모두 공유하게 됩니다.

Quote:

참고로 지금 형태의 프로그램구조(뮤텍스의 사용에의해)는 모양만 몇개의 Thread로 나눠 놓은 상태이고 실제 작동은 단일 프로세스와 다름없이 설계되어 있습니다. thread안에 뮤텍스 없이 만들수 있는 방법을 찾아 보세요.(현재 수준의 기능이라면 가능할것 같습니다.)

제가 아직 초보라 .. 1client-1thread 방식이 아닌이상 뮤텍스 없이 구현 가능합니까??
mutex 없이 요청받은 client의 socket을 어떻게 thread에게 넘기죠?
가르쳐 주세욤... ㅜㅜ
@@

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.