TX, RX Routines in TCP Layer

John·2025년 6월 13일

system programming

목록 보기
5/11

지난 페이지에서는 TCP layer에서 socket이 어떻게 만들어지는지와, 그 구조에 대해 알아보았다.
이번 페이지에서는 socket을 이용해 데이터를 주고(TX) 받는(RX) 과정 중 TCP layer에서 어떤 일이 일어나는지 알아보겠다.

TCP TX routines

TX routine (sending routine)은 프로세스가 데이터 전송을 위해 socket에 write() syscall을 날릴 때 어떻게 작동하는지에 대한 설명이다.

Outgoing data queue : write queue

유저가 write()를 날리는 속도나 수가 socket에서 처리해서 날리는 속도보다 비교적 클 수 밖에 없다.
따라서 이를 관리하기 위해 queue가 존재한다. 이 queue가 바로 struct sock 안에 있는 write queue 이다.

write queue는 skb를 doubly linked list로 구현한 queue이다.

전송 과정을 개괄하자면, 유저가 write()를 통해 소켓에 데이터를 쓰면, segment(TCP에서의 데이터 단위)가 write queue로 위치한다.
신뢰성 있는 전송을 위해, 수신자가 보내는 ACK가 올 때까지 queue에 저장해놨다가, ACK가 오면 내보낸다.
아래 function chain 을 통해 더 자세히 알아보자.

TX Function Chain

컴퓨터 간 커넥션이 established되고 나서, 유저가 write()를 통해 데이터를 전달할 수 있다.
데이터 전송의 과정에서 호출되는 function들은 다음과 같다.

sys_write() -> vfs_write() -> sock_write_iter() -> sock_sendmsg() -> inet_sendmsg() (여기까지 Socket Layer) ->
tcp_sendmsg() -> tcp_write_xmit() -> tcp_trasmit_skb() (여기까지 TCP layer) -> ip_queue_xmit() -> ip_local_out() -> ip_output() -> ip_finish_output() -> ip_finish_output2() (여기까지 IP layer)->


*강의 ppt 재구성

socket layer에서는 처음에 vfs로 관리되는 모습을 볼 수 있다. 하지만 file의 operation에 tcp, ip operation이 hook되어있기 때문에, 관련 함수를 호출한다.

TX function 1. - tcp_sendmsg()

tcp_sendmsg()skb를 생성한 후, 유저가 보내려고 하는 데이터를 커널에 복사함으로써 TCP segment로 만드는 역할을 한다.
구체적으로는

  1. skb(sk_buff)를 하나 만들고,
  2. memcpy_from_msg를 통해 유저데이터를 커널에 만들어진 skb에 복사한 후,
  3. skb를 write queue에 push한다.

TX function 2. - tcp_write_xmit()

congestion control, flow control을 통해 잠시 전송을 중단해야할 경우가 생긴다.
이 함수 이후에 불리는 함수는 바로 skb를 writing queue에 보내는 함수이기 때문에, 이 함수를 통해 전송량을 조절한다.

TX function 3. - tcp_transmit_skb()

이 함수가 불렸다는 것은, 오류가 있지 않는 한, 바로 패킷을 write queue로 보낼 수 있다는 것을 의미한다.
보내기 전, 마지막으로 유효한지 체크하며 패킷에 TCP header을 만드는 함수이다.

  • flag, timestamp, window scaling, SACK 등을 확인
  • TCP header 생성, checksum
  • TCP statistics 증가 시키기 (얼마나 많은 데이터 보냈는지 인덱싱)

이후 ip_queue_xmit() 을 호출해 패킷을 IP layer로 보낸다.

TCP RX routines

RX routines (receive routine)은 프로세스가 L2, L3을 거쳐 전달된 세그먼트를 어떻게 받는지에 대한 동작 설명이다.

NIC가 새로운 패킷을 받으면, CPU에게 알려 interrupt를 발생시킨다. interrupt가 발생하면, interrupt handler을 작동시켜 처리한다. RX routine또한 패킷이 올때 작동하는 interrupt handler 내부에서 일어나는 동작이다.

cf)

  • 가능한 문제상황으로는, 패킷이 올 때마다 interrupt가 발생할 수 있어 만약 많은 패킷이 오게 된다면, 많은 context switching이 일어나 CPU utilization이 떨어질 수 있다는 것이다.
    해결 방안으로는 패킷 여러개를 모아놨다가 batch(묶음)처리해서 한번의 interrupt에 batch단위로 처리하는 방법이 있다.
  • 또 다른 문제로는, 패킷이 올 때 interrupt handler가 바로 발생해 기존의 프로세스 처리를 일시 중지하는데, 이렇게 되면 프로세스 처리에 있어 unfair한 상황이 발생ex) process B시간에만 계속 interrupt 처리하면 B는 처리가 매우 느려짐. )할 수도 있다.
    해결 방안으로는, 최대한 interrupt 시간을 줄이기 위해서, 패킷이 도착했을 떄 수행해야 하는 필수적인 동작들(ex. NIC에 있는 데이터 복사)만 우선 최소한으로 수행 한 후, 나머지 다른 동작들은 스케줄링으로 처리한다.

Incoming data queue : receive queue, backlog queue

패킷이 도착했을 때, 두가지 시나리오가 있을 수 있다.

  1. 프로세스가 read() syscall 하면서 기다리고 있을 때
    -> *read()는 blocking syscall이어서, 데이터가 다 올 때까지 기다리고 있음.
    -> user buffer로 세그먼트 복사.

  2. 아직 패킷에 맞는 read() syscall이 호출 안되었을 때(아직 프로세스가 안 읽었을 때)
    -> receive queue에서 대기
    -> L2, L3, L4까지의 관련 동작들이 모두 처리 되고 난 뒤에 올라와서 기다리고 있는 데이터(세그먼트)이기 때문에, 프로세스가 read()로 소켓 만들면 바로 해당 세그먼트 copy해 감.

  • what if... (use of backlog queue)
    user 프로세스가 copy하는 동안 새로운 패킷이 소켓에 도착한다면 receive queue에 바로 올려도 될까?
    -> 바로 receive queue에 올리면 inconsistency 문제가 발생할 수 있어, 문제가 될 수 있음.
    -> 따라서, 소켓에 의해 receive queue가 사용되고 있다면, 그동안 소켓에 들어온 패킷은 backlog queue에 임시저장.
    ->backlog queue에 있는 패킷들은 아직 TCP layer operation은 처리가 덜된 패킷이다. L3 까지 처리되고 TCP layer에 올라오면서 queue가 사용중인지 확인하기 때문에, 아직 TCP ops의 전부는 처리 안되어있음.

cf)

  • out-of-order queue : 순서대로 안온 패킷 임시저장 ex) 1 2 3 5 -> 5를 out-of-order queue에 저장
  • SYN backlog queue : SACK과 같이, client가 요청한 패킷을 소켓에 저장해놓는 queue.

slow path, fast path in RX routine

TCP RX operation 방식은 크게 두가지로 나눌 수 있다.
1. slow path
2. fast path
말 그대로, 더 빠른 ops가 이루어질 수 있는지 아닌지에 따라 나눈 것이다.

slow path

  • 추가 처리가 필요한 경우
    ex) header을 보니 처리해야 할 option들이 있는 경우, out-of-order 세그먼트인 경우. 하나라도 뭔가 특이사항 있으면 slow path

fast path

  • 추가적인 TCP operation이 굳이 필요없는 경우
    ex) 순서에 맞게 잘 패킷이 도착해서 추가적인 처리가 필요 없을 때
  • 불필요한 체킹 줄이고 바로 skb를 receive queue에 올림

Criteria for Slow and Fast Path

Header prediction : 헤더(메타데이터)를 보고 추가적인 TCP operation이 필요한지를 판단. 만약 특이사항 없으면 fast path로 직행. (*예측 X)
ex)
ACK 값이 일치할 경우. (내가 보낸게 제대로 전달 됨)
sequence # 가 맞을 경우. (순서가 맞음)
optional flag가 없는 경우 (SYN, URG, FIN, RST와 같이 특이사항 없음)
window size 그대로인 경우 (capacity 그대로)
--> fast path

However...
ACK 값이나 sequence #이 이상한 경우
optinal flag가 enabled된 경우
--> slow path

RX Function Chain

NIC를 통해 패킷이 들어오면, L2, L3를 지나 TCP layer에 들어오게 된다.

L3, 즉 IP layer부터 TCP layer로 들어오는 과정에서 사용되는 핵심 과정과 함수는 다음과 같다.

ip_rcv() -> ip_rcv_finish() -> ip_local_deliver() -> ip_local_deliver_finish() (여기까지 ip layer) ->
tcp_v4_rcv() -> tcp_v4_do_rcv() -> tcp_rcv_established() -> if slow path, tcp_data_queue(), if fast path tcp_queue_rcv() (여기까지 tcp layer) ->
이후 user의 read()가 호출하는 tcp_recvmsg() 가 데이터 복사할 때까지 receive queue에서 기다리기


*강의 ppt 재구성

RX function 1. - tcp_v4_rcv()

TCP layer에서 처음 불러와지는 함수. 이 함수에서는

  1. TCP header을 parse한다.
  2. IP주소와 port번호를 가지고 알맞은 TCP 소켓을 찾는다.
    만약 소켓이 사용중이면 (유저가 read()로 소켓 안의 skb를 복사하고 있으면) -> backlog queue로 이동 (backlog queue 안에 있는 패킷은 tcp ops 처리가 덜 됐다고 하는 이유)
    만약 사용 안하고 있으면, tcp_v4_do_rcv() 호출

RX function 2. - tcp_v4_do_rcv()

모든 전송은 TCP_ESTABLISHED상태일 때만 가능하다. 따라서 이 함수에서 ESTABLISHED 상태인지 체크한다.

만약 ESTABLISHED 상태이면, tcp_rcv_established() 호출
아니라면, tcp_rcv_state_process() 호출해서 처리. (다른 상태의 처리는 이 강의에서는 다루지 않을 예정)

RX function 3. - tcp_v4_established()

fast path로 갈 지, slow path로 갈 지를 결정하기 위해 header prediction 진행한다.

fast path -> tcp_queue_rcv() 호출
slow path -> tcp_data_queue() 호출

RX function 4-1. - tcp_queue_rcv()

바로 receive queue에 enqueue 하는 함수. 유저에게 전달될 수 있는 상태.

RX function 4-2. - tcp_data_queue()

추가적인 option, flag 수행.
order가 섞여서 온 패킷은 잠깐 out-of-order queue에 넣어놨다가, 중간에 빠진게 왔으면 reordering.
이상 없는거 다 체크 되면 tcp_queue_rcv() 호출(fast path에서 바로 호출한 것과 동일)

From User's Side (user calls read())

유저가 read() (또는 recv() )syscall을 할 경우, 다음과 같은 function chain을 따른다.

recvmsg() -> __sys_recvfrom() -> sock_recvmsg() -> tcp_prot.recvmsg -> tcp_recvmsg() -> 데이터 유저 buffer로 복사

  • read()를 호출하면, 일단 backlog queue에 있는 것을 모두 꺼내 나머지 ops (tcp_v4_established() 부터) 수행시켜 receive queue나 out-of-order queue로 옮겨 read()진행.
  • tcp_recvmsg() 가 호출되면, lock_sock()이 호출되어 소켓이 사용중임을 나타낸다. 그동안 패킷은 receive queue가 아니라 backlog queue로 간다.
  • 만약 데이터 수신에 필요한 모든 패킷이 아직 도착 안했으면 다 올때까지 기다림 (blocking).

TCP를 통한 데이터 복사가 다 끝나면
tcp_send_ack를 통해 잘 받았다고 전달.

FYI) flow control, congestion control

TX routine에 있어 sending rate를 고려해야 함.
tcp_write_xmit()으로 제어.

  • flow control
    tp -> snd_wnd 로 window size 확인 가능
    window size : receiver가 보낸 값
  • congestion control
    cnwd (congestion window)값이 넘는 전송의 경우, 일시정지.
    cnwd : sender 가 예상한 값
    많은 알고리즘(ex Tahoe, CUBIC 등)으로 계산, linux 모듈 형태로 구현.
profile
¡Salid y Disfrutad!

0개의 댓글