[완료]스레드를 사용한 select에서 block 상태 빠져나오는 방법

dcmru의 이미지

네트워크 프로그램을 하나 짜고 있는데, 이상한 현상이 발생하여서 문의를 드립니다.

부모 스레드가 port 리슨을 하고 있고, 클라이언트가 연결을 하면,

스레드를 띄워 클라이언트로 부터 주기적으로 패킷을 받습니다.

클라이언트를 처리하는 스레드는 select를 사용한 블럭킹 모드입니다.

연결을 오래 유지하는 프로그램이라 패킷을 많이 발생시키지 않도록 하기위해

select 대기 시간을 십 몇 분정도로 해서 테스트하고 있는데,

스레드를 사용한 select 대기 상태에서 클라이언트로 부터 네트워크 단절이 되는 상태로 되었을 때

스레드의 select가 timeout이 설정되었음에도 블락을 빠져나오지 않는 현상이 나타납니다.

이 블락 상태를 빠져나오도록 하기위해 다른 스레드에서 해당 스레드의 select에 패킷을 날려 read 이벤트가 발생하도록 해보았는데,

빠져나오는 경우도 있지만 아닌 경우도 있습니다.

pthread_cancel을 사용해서 스레드를 죽여보려고 하였으나 select 블락 상태에서 스레드가 죽지도 않습니다.

검색을 해본 결과 마지막으로 스레드에 시그널을 주라는 부분만 테스트를 안해본 상태입니다.

이와 같은 현상을 겪어보신 분이 계시면, 해당 문제를 해결하기 위한 좋은 방법에는 어떤 것이 있을까요?

논블락킹 모드로 해야하는지, 시그널을 보내는 방법을 써야하는지 조언 부탁드립니다.

dcmru의 이미지

스레드에 시그널을 보내어 스레드를 깨우는데는 성공했습니다.
하지만 select에 설정된 timeout만큼 기다린 후 스레드가 종료됩니다.
timeout을 길게 설정했을 경우 바로 종료할 수 있는 방법이 없을까요?

노력만이 살길이다.

익명 사용자의 이미지

select를 돌때마다 FDSET을 새로 설정은 하셨죠?

dcmru의 이미지


select 들어가기전에 설정을 하였습니다.
다른 체크사항이 있을까요?

노력만이 살길이다.

yhsuk의 이미지

도움이 될진 모르겠지만

1. select 바로 다음에 printf로 SELECT를 빠져 나왔는지 체크해 보세요
=> select가 아닌 다른 곳에서 블럭되어 있을 것 같은데요.
2. 추가로 pstack 명령으로 실제 프로세스가 select에서 블럭되어 있는지 살펴보세요.
3. 쓰레드 종료는 pthread_cancel을 사용하지 말고, 쓰레드에 전달된 void * 아규먼트에 구조체 인스턴스를
보내시고, 그 인스턴스 내의 bool 타입 변수로 체크해서 자연스럽게 쓰레드 함수를 빠져나오도록 하는게 좋습니다.
4. timeout은 짧게 해야 할텐데요. 쓰레드를 지정해서 시그널 보내기는 힘들지 않을까 합니다. (클라이언트 개수만큼 쓰레드가 생길텐데)
select의 timeout을 짧게 주시고, 값을 누적시켜서 한계값에 도달하거나 쓰레드 종료조건이 되면 쓰레드 함수를 빠져나가는 방법을
사용할 수도 있습니다.

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

dcmru의 이미지

답글 감사드립니다.
구현 형태가 모바일 push 서버라 timeout을 길게 설정해보았습니다.
일단 말씀하신부분을 체크해보도록 하겠습니다.
구조적으로 잘못 이해한 것이라면, 구현방법을 다른 형태로도 생각해보겠습니다.
일반적으로 timeout을 길게해서 사용하는 형태는 없나요?

노력만이 살길이다.

yhsuk의 이미지

얘기하시는 내용에 약간 모순이 있는 것 같습니다.
모바일 PUSH 서버여서 클라이언트 접속을 오래 가져가야 해서 타임아웃을 길게 설정했는데,
바로 종료할 수 있는 방법을 원하신다는 얘기가 그렇습니다.

기본적으로 접속을 오래 가져가야 하지만 네트워크 단절이 생기는 경우 빠르게 종료시키고 싶으신 건가요?
그렇다면 굳이 타임아웃을 길게 가져가서 할 필요는 없습니다.
네트워크 단절을 체크하는 방법은 몇 가지가 있습니다.

1. 클라이언트가 마지막 보낸 패킷의 시간체크를 통해 설정값을 넘기면 끊겼다고 판단한다. (클라이언트가 주기적으로 패킷을 보내야 함)
    a. 기본적으로 Recv는 thread함수내에 위치하고 루프내에 존재한다.
    b. Recv시 약간 짧은 타임아웃을 주고, 루프를 돌며 경과시간을 누적시킵니다.
    c. 누적 경과시간이 다 된 경우 설정값을 넘긴다면 끊겼다고 판단하고 사용한 자원을 반환하고 루프를 탈출합니다. (결국 쓰레드가 종료되게 됩니다)
2. 실제 패킷을 보내본다 (send시 에러나면 끊기, recv에는 에러가 리턴되지 않습니다)
3. KEEPALIVE 설정을 하는 방법이 있습니다. (TCP 스택 구현에 따라 다르나 보통 지원)

개인적인 추천순서는 1, 2, 3 순입니다.
찾아보시면 일반적으로 KEEPALIVE 설정값을 수정하는건 권장 방법이 아니라고 합니다.

추가적으로 블럭킹 소켓은 SELECT대신에 소켓fd에 setsockopt로 send timout과 recv timeout을 설정할 수 있습니다.

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

dcmru의 이미지

답글 감사드립니다. 제가 생각하는 형태가 타임아웃을 길게 가져가되
모바일 클라이언트는 3G와 Wi-Fi를 왔다 갔다하는 상황에서 말씀드린 현상들이 나타납니다.
그리고 주고 받는 패킷을 최소화 하려는 의도도 있습니다.

말씀하신 1번의 형태가 busywait 방식을 말씀하시는 것으로 생각이 듭니다.
select를 통해 블락을 하여 busywait 상태보다는 낫다는 생각이 들었었습니다.
제 생각이 틀렸을 수도 있습니다...
2번은 select 블락된 상태에서는 사용을 못할 것 같고,
timeout이 짧다는 상태에서 사용할 수 있을 것 같습니다.
그러나 주고 받는 패킷이 좀 더 많아질 수 있을 것 같은 생각이 들구요.
3번은 OS의 설정값을 변경하는 것을 말씀하시는 것으로 이해를 했습니다.

나은 프로그램 방법이 무엇인지 고민이 많아지네요...

노력만이 살길이다.

kukyakya의 이미지

select를 쓰시는거면 왜 클라이언트마다 쓰레드를 생성하시나요?

dcmru의 이미지

스레드 마다 클라이언트의 응답을 대기하도록 select를 사용하여 블락모드를 사용하였습니다.
현재 구조에 한계가 보이지만, 시간과의 싸움이라 일단 구현해보고,
epoll을 사용하는 형태로 변경해볼 생각입니다.
네트워크 프로그램을 많이 안해봐서, 막히는 부분이 많네요...

노력만이 살길이다.

익명_사용자의 이미지

각각의 쓰레드가 하나의 클라이언트만을 담당한다면, (즉, 새로운 연결이 accept()될때마다, 쓰레드를 생성시켜서 그 연결을 할당한다면)

그냥 read()/write() blocking I/O 쓰셔도 될듯한데...왜 select를 사용하시는지 모르겠네요.

어쨋든, 혹시 select() 호출하실 때, exceptfds parameter를 설정안하신건 아닌지 체크해보세요.
또한, FD_ISSET()을 이용하여 체크 할때, exceptfds를 readfds나 writefds보다 먼저 체크하는것이 안전합니다.

그런 자질구레한 버그가 아닌지 체크해보세요.

그리고 pthread_cancel()이 select()을 탈출시키지 못하는 이유는 man페이지에 잘 나와있습니다.

A thread's cancellation type, determined by pthread_setcanceltype(3),
may be either asynchronous or deferred (the default for new threads). 
Asynchronous cancel-ability means that the thread can be canceled at any time
(usually immediately, but the system does not guarantee this).
Deferred cancel-ability means that cancellation will be delayed
until the thread next calls a function that is a cancellation point.
A list of functions that are or may  be  cancellation  points  is  provided  in pthreads(7).

위에 나와 있듯이 deferred가 기본 pthread_cancel type입니다.
Deferred cancel은 취소 요청을 해도 실제 취소는 안하고 예약(??)만 해놨다가
pthreads(7)에 나와있는 cancellation point로 간주되는 함수가 호출될때 정말 해당 쓰레드를 취소하는 방식입니다.

man pthreads 7 하시면 cancellation point로 간주되는 함수목록이 나와있으며
select()는 cancellation point 목록에 없습니다.

man 페이지에 필요한 모든 설명이 나와있으니 차근차근 읽어보시면 이해가 될리라 생각합니다.

dcmru의 이미지

답글 감사드립니다.
사용한 형태가 readfds만 설정했는데, exceptfds도 설정해보고 테스트 해보겠습니다.
그리고 pthread_cancel()에 관한 부연 설명도 감사드립니다.
역시 man 페이지를 자세히 봐야하는군요!

노력만이 살길이다.

모지리의 이미지

저의 경우 SELECT를 사용해도 타임아웃을 아주 작게 사용합니다.
그리고 타임은 따로 체크합니다. 그리고 소켓은 모두다 넌블락 소켓이요.
원할때 원하는 상황에서 빠져나옵니다.

쓰레드와는 상관없는 SELECT 이슈.

dcmru의 이미지

답글 감사드립니다.
논블락 형태도 테스트를 해봐야겠네요.
그리고 말씀하신 형태가 busywait 형태를 말씀하는 것인가요?
select 머리가 아프네요;;;

노력만이 살길이다.

익명 사용자의 이미지


윗분들처럼
각 Accept마다 쓰레드를 할당하여 read/write를 한다라면
해당 쓰레드에서 왜 select를 사용하시는지 이해가 잘 가지않네요
어차피 하나의 쓰레드에 하나의 FD만 걸려나올텐데 말이죠

read 에 타임아웃을 주시면 될듯합니다.
그리고 소켓은 논블락 소켓을 사용하심이 좋으실듯합니다.

dcmru의 이미지

담글 감사드립니다.
말씀하시는 형태가 맞습니다.
스레드당 클라이언트 하나를 할당하도록 하였고,
하나의 소켓 FD만 검사하는 형태입니다.
read를 사용하는 방법도 생각해보겠습니다.
그리고 논블락을 말씀하시는 것을 보니 이를 먼저 적용해봐야겠네요.

노력만이 살길이다.

dcmru의 이미지

답글을 달아주신 분들에게 다시 한 번 감사의 말씀을 드립니다.
문제 부분이 생각했던것과 다르게 다른 부분에서 호출한 select에서 대기가 발생을 했었습니다.
역시 디버그를 잘해야하나 봅니다;;;

정리를 하자면, select를 깨우는데 있어서 kldp에서 검색했던 것과 같이,
1. timeout을 주거나
2. read 또는 write 등 이벤트를 발생하도록 하는 방법(물론 select에 해당 이벤트를 등록 해주어야겠죠)과
3. 시그널을 주는 것입니다.

저는 스레드를 사용하였기 때문에 해당 스레드에만 시그널을 날렸습니다.
해당 스레드에만 시그널을 날리려면, pthread_create를 실행할 때 만들어지는 스레드 ID를 알아야 합니다.
그리고 스레드 생성시 어떤 시그널을 받을 것인지 등록을 하고, 다른 스레드에서 해당 스레드로 pthread_kill로 시그널을 날렸습니다.

말씀해주신 다른 방법들도 적용해보겠습니다. 감사합니다.

노력만이 살길이다.

댓글 달기

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