오늘 우리 서비스중이던 프로세스의 모든 스레드가 행이 걸리는 문제가 발생 했었어.
release 빌드된 바이너리가 리모트 장비에서 도는 상황에서 발생한 문제라,
스택 덤프 뜨고 분석하는데 까지 사이시간동안 회사 선임하고 문제 원인이 뭘까 논의를 했었는데,
일단 둘다 IOCP 내부 구현 코드를 본적은 없고 듣고 읽은 내용이 전부라 막 소설을 쓰고 있었어.
근데 의외의 논쟁거리가 하나 생겼는데, 아래와 같은 내용이야.
해당 시점까지 수집된 미량 정보 : 통신용 스레드 풀 내의 모든 스레드가 행이 걸린 상태. recv용 코드 근처 어딘가에 문제가 있다.
가설 : 연결되어있던 클라이언트 중, TCP 패킷을 일정량 보내겠다고 약속 한 후,
버그 혹은 고의로 패킷을 보내지 않아 서버측의 스레드가 recv 대기에 계속해서 물려있는 상태가 아닐까?
주장 1. IOCP는 코드 작성자에게, 절대적인 비동기 환경을 제공한다.
Queued 된 데이터는 이미 해당 프로세스에 소속된 메모리에 복사가 완료된 상태다.
해당 프로세스에 소속된 그 어떤 스레드도 receive/send를 위해 대기를 하지 않는다.
(단, 접속과 데이터 recv 시작을 위한 대기는 있을 수 있다.)
실제 NIC buffer로부터 user buffer까지의 복사 과정은 OS가 수행한다.
OS가 수행했다는 의미는, OS에 소켓처리를 위한 별도의 스레드가 존재한다는 말과 동치이며,
결국 하고싶은 말은, IOCP를 사용 할 경우, 프로세스 소속으로 표기되는 그 어떤 스레드도 recv 상황 중 행이 걸릴 수 없다는 말이다.
주장 2. 앞선 주장의 대부분 내용에 동의 하지만,
특정 프로세스를 위해 커널레이어에 스레드가 생겼다 없어졌다 한다는 부분이 납득이 되지 않는다.
CreateIoCompletionPort initialize 과정에서 생성되는 thread들이 io 수행 주체가 되지 않을까 싶다.
CreateIoCompletionPort initialize 과정에서 생성되는 thread는 해당 프로세스 소속이다.
스택 트레이스에서 발견된 스레드의 상태는 IOCP로부터 비롯될 가능성이 있다.
위와같이 두가지 소설로 논쟁이 붙었는데,
뭐 결국 덤프 확인해보니, 사용한 라이브러리에 포함되어 있던 lock부분이 문제였고.. 라이브러리 버전 업그레이드로 허무하게 문제가 종결됨..
근데 앞에서 여러가지로 소설을 썼던 내용이, 아무래도 궁금한거지...
앞선 설명이 좀 길어서, 간단히 다시 요약을 좀 해보면,
IOCP의 개념이 어떻게 되었건 간에, NIC 버퍼에서 유저 프로세스 버퍼로 데이터를 옮기는 행위를 하는 주체가 있을거잖아?
이 주체가 결과적으로 스레드일까? 그리고 이 스레드는 특정 프로세스 소속 스레드일까? 아니면 OS 커널에서 관리하는 스레드일까?
이런게 좀 알고싶어져서..
그리고 추가로, TCP 클라이언트가 장난을 치고있는 상황에서
(예를들어 three way handshake중에 ACK/SIN 패킷만 안보낸다던가, 앞에서 잠깐 언급한것처럼 1300바이트 보낼께 해놓고 250바이트만 보내고 나머지 바이트는 고의로 전송을 하지 않는다던가)
스레드가 행이 걸린다고 하면, 이 행걸린 스레드는 해당 프로세스 소속일까? 아니면 OS 소속 스레드일까?
아는사람 있는가?
웬지 OS는 디바이스 인터럽트를 기준으로 핸들러를 동작시키므로, 그 어떤곳에서도 행이 걸리지 않는다. 다만 해당 세션을 위해 생성된 스트럭트가 타임아웃동안 살아있을 수 있다. 빠른시간동안 앞선 동작이 반복되면 메모리 부하로 인해 서버가 동작 불가 상태로 빠질 수 있다... 라는 답변이 올라오려나..
근데, 이 말은 결국 수행체를 OS가 들고있다는 말이되네? 좀더 이어나가면 IOCP 처리를 위한 모든 수행체는 OS로부터 비롯되며 이는 특정 프로세스에 소속되지 않는다 라는건가?
아... 소설을 넘어서 플라나리아가 신화창조하는소리하는거같다.. 아는사람 답변좀...
공상이 도가지나쳐서 댓이 달리지 않는가...
뭐 대충 맞음 이번에 윈 10 소스 안봐서 모르긴 한데 IOCP는 전달받는 WSABUF 포인터를 커널에서 관리함
소켓도 IOCP 인자를 줘서 만들어주면 낮은 메모리 번지를 찾아줘서 가상메모리 가급적 안쓰게하고
그냥 커널지원이 있다고 생각하면 될듯
오, 답변 ㄳㄳ. 일단 그럼 결론은, IOCP 사용하는 환경에서 L4 이하에서 발생하는 이슈들이 프로세스까지 넘어오지 않는다고 이해를 해도 되려나?
리눅스하고 윈도랑 맥밖에 안써봤는데 로드벨런싱이랑 메시지 전달같은걸 응용프로그램 환경에서 하는 극단적인 상황이있ㅇㅁ??
오래되어서 보실지는 모르지만 닉버퍼에서 유저프로세스버퍼로 데이터를 옮기는건 그냥 인터럽트가 걸린순간 그 인터럽트를 가로챈 코어에 바인딩된 스레드에 대응되는 커널스레드입니다 - dc App - dc App
WSASend나 Recv로 인해서 스레드가 데드락에빠질수는없습니다. 무조건GQCS로 성공이던 실패던 완료통지가 옵니다 - dc App