Conversation
| struct socket { // the kernel representation of a BSD socket | ||
| socket_state state; | ||
| short type; | ||
| unsigned long flags; | ||
| struct file *file; // File Descriptor와 직접 연결 | ||
| struct sock *sk; // Transport-level state - 실제 네트워킹 상태 | ||
| ... | ||
| }; |
There was a problem hiding this comment.
"Everything is a file"이라는 철학에 기반한다면 커널 입장에서는 일반 텍스트 파일을 write() 하는 것과 소켓에 send() 하는 것이 동일한 과정인가요?
| - L4는 이 패킷들을 애플리케이션이 소비 가능한 형태로 재구성 | ||
| - `read()`/`recv()` 시스템 콜을 호출하여 소비함. | ||
| #### 4.2.1. TCP: Data Stream 추상화 | ||
| - 수신한 여러 패킷을 내부적으로 처리하여 연속된 바이트 스트림을 제공 |
There was a problem hiding this comment.
TCP는 패킷 경계가 없는 'Byte Stream'이라면, 예를 들어 send()를 3번 나눠서 호출해도 수신 측 recv()에서는 한 번에 뭉쳐서 들어올 수 있다는 뜻인가요? 애플리케이션 헤더를 정의해 이런 '패킷 뭉침(Sticky Packet)' 현상을 구분할 수 있게 한다고 하는데 이에 대해서 설명해주실 수 있을까요?
| #### 2.1.2. Port의 의미 | ||
| - Port는 커널이 관리하는 식별자 네임스페이스 | ||
| - 여러 네트워크 수신 지점이 동일한 네트워크 네임스페이스 내의 IP 공간에서 충돌하지 않도록 함. | ||
| - 커널은 Port를 이용해 수신 데이터를 적절한 소켓으로 demultiplex하며, 해당 소켓은 특정 프로세스에 귀속됨. |
There was a problem hiding this comment.
열려 있는 소켓이 수천 개일 때, 커널은 어떻게 많은 소켓 중에서 해당하는 소켓을 빠르게 찾아내는지 방법이 있나요?
| #### 2.3.3. `TIME_WAIT`: Port와 Connection 상태 결합 | ||
| - 연결이 종료되더라도, 커널은 Connection을 즉시 제거하지 않고 일정 시간 동안 `TIME_WAIT` 상태를 유지함. | ||
| ```c | ||
| close(fd); | ||
| // -> connection enters TIME_WAIT | ||
| ``` | ||
| - 이 동안 Connection 상태가 유지되고, Port는 OS 자원으로 묶여 있어 일정 시간 동안 재사용이 제한됨. | ||
| #### 2.3.4. 포트 고갈: Port 자원과 Connection 상태의 결과 | ||
| - 클라이언트는 연결마다 ephemeral port를 소모함. | ||
| - 다수의 짧은 연결이 생성, 종료되면 | ||
| - `TIME_WAIT` 상태 누적 | ||
| - 재사용 가능한 포트가 줄어 포트 고갈 발생 | ||
| ```c | ||
|
|
||
| ``` | ||
| - Port + Connection 상태를 커널이 보유하고 있기 때문에 발생하는 자원 문제 |
There was a problem hiding this comment.
글에서 Port를 커널이 관리하는 네임스페이스로 설명한 부분이 인상 깊었습니다.
특히 TIME_WAIT 상태에서 포트 재사용이 제한되는 현상은
패킷 전달 문제가 아니라 커널이 connection 상태와 포트를 함께 보유하기 때문에 발생하는 자원 문제라는 점이 명확해졌네요.
| - 소켓은 파일 디스크리버로 표현되지만 | ||
| - 실제 동작은 커널 내부의 Transport Layer 로직에 의해 수행됨. | ||
| ### 3.2. Kernel Space 내부 | ||
| - 커널은 `send()` 호출이 들어왔다고 해서 즉시 패킷을 보내지 않고 | ||
| - 다음을 판단함. | ||
| - 현재 연결 상태가 전송 가능한 상태인가? (e.g., TCP state가 `ESTABLISHED`) | ||
| - 수신 측 윈도우 및 로컬 송신 윈도우에 여유가 있는가? | ||
| - 아직 ACK되지 않은 재전송 대기 데이터가 존재하는가? | ||
| - 지금 전송해도 혼잡 제어 정책상 문제가 없는가? | ||
| ```c |
There was a problem hiding this comment.
send() 호출이 실제 패킷 전송과 1:1로 대응되지 않는다는 설명 덕분에,
TCP가 전송 타이밍과 크기를 애플리케이션이 아니라 커널이 결정한다는 점이 잘 이해되었습니다.
이 관점이 없으면 send가 곧 네트워크 송신이라고 오해하기 쉬울 것 같네요!
Gusionling
left a comment
There was a problem hiding this comment.
저번 주차에 이어 코드에서 구조체까지 보여주셨군요
수고하셨습니다~~
| #### 2.1.2. Port의 의미 | ||
| - Port는 커널이 관리하는 식별자 네임스페이스 | ||
| - 여러 네트워크 수신 지점이 동일한 네트워크 네임스페이스 내의 IP 공간에서 충돌하지 않도록 함. | ||
| - 커널은 Port를 이용해 수신 데이터를 적절한 소켓으로 demultiplex하며, 해당 소켓은 특정 프로세스에 귀속됨. |
There was a problem hiding this comment.
위에 질문과도 연결되는 점 같은데 커널은 bind 시 inet_hashinfo 같은 해시 테이블에 포트 번호를 등록해 둔다네용 해시 기반이기 때문에 탐색은 금방해서 5-tuple로 소켓을 금방 찾을 수 있을 것 같습니다.
| - 소켓은 파일 디스크리버로 표현되지만 | ||
| - 실제 동작은 커널 내부의 Transport Layer 로직에 의해 수행됨. | ||
| ### 3.2. Kernel Space 내부 | ||
| - 커널은 `send()` 호출이 들어왔다고 해서 즉시 패킷을 보내지 않고 |
There was a problem hiding this comment.
커널은 send() 호출 시 데이터를 즉시 패킷으로 변환하지 않고, 먼저 송신 버퍼에 복사를 합니다. 이 과정에서 Backpressure가 발생하는데요
send()가 성공했다는 것은 데이터가 "네트워크로 나갔음"이 아니라 "커널의 송신 버퍼에 안전하게 복사되었음"을 의미합니다.
흐름제어에 의해 데이터가 버퍼에 쌓이게 되면 send()는 성공했지만 송신버퍼가 꽉차게 되는데요 이때 어플리케이션에게도 알려야하기 때문에 BackPressure가 생깁니다.
이때 소켓은 2가지 결정을 할 수 있습니다.
블로킹으로 처리할 것이냐 아니면 논블로킹으로 처리할 것이냐
블로킹은 기본 값이기 때문에 논블로킹을 설명하지만
backpressure가 발생했을 때 논블로킹으로 처리하게 되면 어플리케이션은 제어권을 즉시 돌려받지만 전송되지 못한 데이터에 대한 재시도 로직을 직접 관리해야 합니다.
앞서
"어플리케이션은 네트워크에 직접 접근하지 않는다"라는 설명이 이와 관련되어 있습니다.
애플리케이션은 그저 send()를 호출할 뿐이지만 , 실제 실행 속도는 L4가 관리하는 TCP 윈도우 크기, 재전송 타이머, 혼잡제어 알고리즘에 의해 물리적으로 제약을 받게 됩니다.
📝 Description
Transport Layer를 OS 커널 관점에서 정리하였음.
Port와 Connection을 커널 자원/상태로 보고, process-to-process 통신이 어떻게 구현되는지 확인함.
🚀 Key Learning
🛠 Keywords
#Transport_Layer#Port#Connection#Socket#send#recv