링크 ux 커 널 tcp 수신 데이터 구현

데 이 터 를 보 내 는 것 보다 데 이 터 를 받 는 것 이 더 복잡 하 다.데 이 터 를 받 습 니 다. 여기 와 3 층 의 인 터 페 이 스 는 tcp 입 니 다.v4_rcv (내 앞의 blog 는 3 층 과 4 층 의 인터페이스 실현 을 소개 합 니 다). 4 층 과 사용자 공간, 즉 시스템 호출 은 socket 입 니 다.recvmsg (다른 읽 기 함수 도 이 함 수 를 호출 합 니 다). 이 시스템 호출 은sock_recvmsg. 다음은 이 함수 부터 살 펴 보 겠 습 니 다.
그것 의 주요 기능 은 sock 초기 화 입 니 다.iocb, 미래 데이터 와 커 널 공간 에서 사용자 공간 으로 복사 할 수 있 습 니 다.그리고 호출
recvmsg 라 는 가상 함수 (tcp 프로 토 콜 은 tcp recvmsg) 입 니 다.

static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock,
				 struct msghdr *msg, size_t size, int flags)
{
	int err;
	struct sock_iocb *si = kiocb_to_siocb(iocb);
///   si。
	si->sock = sock;
	si->scm = NULL;
	si->msg = msg;
	si->size = size;
	si->flags = flags;

	err = security_socket_recvmsg(sock, msg, size, flags);
	if (err)
		return err;
//  tcp_recvmsg
	return sock->ops->recvmsg(iocb, sock, msg, size, flags);
}

커 널 이 데 이 터 를 받 는 것 은 2 부분 으로 나 뉘 는데, 일 부 는 사용자 가 데 이 터 를 읽 는 것 을 막 았 을 때 데이터 가 있 으 면 사용자 공간 으로 직접 복사 하 는 것 이다.다른 한편, 차단 되 지 않 으 면 수신 대기 열 에 데 이 터 를 복사 합 니 다.
커 널 에 서 는 이 대열 이 세 가지 형식 으로 나 뉜 다.각각:
1 sock 역 구조의 skbacklog 대기 열.
2 tcp_sock 의 ucopy. prequeue 대기 열 입 니 다.
3 sock 구조의 receive큐 대기 열.
우 리 는 먼저 두 개의 주요 구조 체 를 본 다음 에 이 3 각 대열 의 차 이 를 설명 한다. 먼저 ucopy 구조 이다.
이 구 조 는 사용자 공간 으로 직접 복사 할 데 이 터 를 나타 낸다.
/* Data for direct copy to user */
	struct {
///prequeue  。
		struct sk_buff_head	prequeue;
///         ,     skb    。
		struct task_struct	*task;
///   
		struct iovec		*iov;
///prequeue            
		int			memory;
///             (          ,             )
		int			len;
........................
	} ucopy;

다음은 sock 의 socklock 구조.
커 널 의 설명 이 상세 합 니 다. 이 잠 금 은 주로 소프트 인 터 럽 트 와 프로 세 스 컨 텍스트 간 에 동기 화 를 제공 하 는 데 사 용 됩 니 다.

/* This is the per-socket lock.  The spinlock provides a synchronization
 * between user contexts and software interrupt processing, whereas the
 * mini-semaphore synchronizes multiple users amongst themselves.
 */
typedef struct {
///   
	spinlock_t		slock;
///            sock  owned 1,   0
	int			owned;
///    ,    sock    ,      sock  。
	wait_queue_head_t	wq;
	/*
	 * We express the mutex-alike socket_lock semantics
	 * to the lock validator by explicitly managing
	 * the slock as a lock variant (in addition to
	 * the slock itself):
	 */
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map dep_map;
#endif
} socket_lock_t;

그리고 세 대열 의 차 이 를 살 펴 보 자.
우선 skbacklog 대기 열 은 현재 sock 이 프로 세 스 컨 텍스트 에서 사 용 될 때 데이터 가 오 면 sk 로 데 이 터 를 복사 합 니 다.backlog.
prequeue 는 데이터 buffer 의 첫 번 째 역 입 니 다. prequeue 가 가득 차 면 receive 로 데 이 터 를 복사 합 니 다.queue 대기 열 종.
마지막 receivequue 는 프로 세 스 컨 텍스트 에서 첫 번 째 로 buffer 를 가 져 오 는 대기 열 입 니 다.(tcp recvmsg 를 소개 할 때 이 세 개의 대기 열 을 소개 합 니 다).
여기에 왜 prequeue 가 있어 야 합 니까? receive 에 직접 넣 으 세 요.quue 면 되 잖 아 요. 여 기 는 receive 입 니 다.quue 의 처 리 는 비교적 번 거 롭 습 니 다.
마지막 으로 tcp 분석v4_rcv 와 tcprecvmsg 전에 tcpv4_rcv 는 소프트 인 터 럽 트 컨 텍스트 에 있 고 tcprecvmsg 는 프로 세 스 컨 텍스트 에 있 기 때문에 socketlock_t 만 owned 를 제공 하여 대응 하 는 sock 을 잠 글 수 있 습 니 다.소프트 인 터 럽 트 컨 텍스트 와 프로 세 스 컨 텍스트 간 의 통신 을 위해 서 는 이 세 개의 대기 열 이 필요 하 다.최종 적 으로 데이터 가 대응 하 는 대기 열 로 복사 되면 호출 을 중단 하고 되 돌려 줍 니 다.여기 서 주의해 야 할 것 은 같은 함수 가 소프트 인 터 럽 트 컨 텍스트 와 프로 세 스 상하 문 종 호출 이 다 릅 니 다. 아래 에서 볼 수 있 습 니 다 (예 를 들 어 tcp rcv established 함수)
ok, 이제 tcpv4_rcv 의 원본 코드.이 함 수 는 소프트 인 터 럽 트 컨 텍스트 에서 호출 되 었 습 니 다. 코드 세 션 을 살 펴 보 겠 습 니 다.
	int tcp_v4_rcv(struct sk_buff *skb)
{
///       
	const struct iphdr *iph;
	struct tcphdr *th;
	struct sock *sk;
	int ret;
	struct net *net = dev_net(skb->dev);
............................

//          sock。
	sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
	if (!sk)
		goto no_tcp_socket;

process:

///   time_wait  ,       (      time_wait  ,    tcp        ,       ).
	if (sk->sk_state == TCP_TIME_WAIT)
		goto do_time_wait;
.................................
///      
	bh_lock_sock_nested(sk);
	ret = 0;
///          (sk)->sk_lock.owned.              sock  1.
	if (!sock_owned_by_user(sk)) {
。........................
		{
///  buffer  prequeue   。       1.
			if (!tcp_prequeue(sk, skb))
///    ,     tcp_v4_do_rcv    skb(         receive_queue ).
				ret = tcp_v4_do_rcv(sk, skb);
		}
	} else
///         sock  buf sk_backlog 。
		sk_add_backlog(sk, skb);
//  。
	bh_unlock_sock(sk);

	sock_put(sk);

	return ret;
...................................................


위의 절 차 는 매우 간단 합 니 다. 우 리 는 다음 에 건 너 뛰 는 함수 몇 개 를 보 겠 습 니 다. 첫 번 째 는 tcp 입 니 다.prequeue。
여기 서 sysctl 을 볼 수 있 습 니 다.tcp_low_latency 는 우리 가 prequeue 대기 열 을 사용 할 지 여 부 를 결정 할 수 있 습 니 다.


static inline int tcp_prequeue(struct sock *sk, struct sk_buff *skb)
{
	struct tcp_sock *tp = tcp_sk(sk);

///    tcp_low_latency  ucopy.task     0.ucopy.task                 sock      ,             receive  。    。
	if (sysctl_tcp_low_latency || !tp->ucopy.task)
		return 0;
///     prequeue  。
	__skb_queue_tail(&tp->ucopy.prequeue, skb);
///update    。
	tp->ucopy.memory += skb->truesize;
///  prequeue  ,    prequeue  。
	if (tp->ucopy.memory > sk->sk_rcvbuf) {
		struct sk_buff *skb1;

		BUG_ON(sock_owned_by_user(sk));
///  prequeue  。
		while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) {
///          tcp_v4_do_rcv(      receive   ).
			sk_backlog_rcv(sk, skb1);
			NET_INC_STATS_BH(sock_net(sk),
					 LINUX_MIB_TCPPREQUEUEDROPPED);
		}
///    。
		tp->ucopy.memory = 0;
	} else if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
///          prequeue     。        。
		wake_up_interruptible_poll(sk->sk_sleep,
					   POLLIN | POLLRDNORM | POLLRDBAND);

///             。
		if (!inet_csk_ack_scheduled(sk))
			inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
						  (3 * tcp_rto_min(sk)) / 4,
						  TCP_RTO_MAX);
	}
	return 1;
}

우 리 는 TCP 에 만 관심 이 있다.ESTABLISHED 상태, tcpv4_do_rcv: tcp 상 태 를 판단 하여 관련 처리 함수 에 들 어 갑 니 다.
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
	struct sock *rsk;
...................................
	if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
		TCP_CHECK_TIMER(sk);
///     。
		if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
			rsk = sk;
			goto reset;
		}
		TCP_CHECK_TIMER(sk);
		return 0;
	}

	........................................
}

그래서 여기 서 중요 하 게 볼 함 수 는 tcp 입 니 다.rcv_settings, 소프트 인 터 럽 트 컨 텍스트 에서 호출 될 때, 주요 목적 은 skb 를 receive 에 추가 하 는 것 입 니 다.queue 대기 열 중.그래서 여기 서 우 리 는 이 부분 만 보고 tcp 를 분석 합 니 다.recvmsg 를 볼 때, 우 리 는 프로 세 스 상하 문 에서 처리 할 수 있 는 일부분 을 다시 봅 니 다.

///        ,     tcp_recvmsg      tcp_rcv_established,         。
if (!eaten) {
///  checksum
	if (tcp_checksum_complete_user(sk, skb))
	
				goto csum_error;
..................................................

				__skb_pull(skb, tcp_header_len);

///       ,         skb   sk_receive   。
			__skb_queue_tail(&sk->sk_receive_queue, skb);
				skb_set_owner_r(skb, sk);
///  rcv_nxt,               。
				tp->rcv_nxt =             TCP_SKB_CB(skb)->end_seq;
			}
............................................

다음은 tcprcvmsg 함수.
위 를 통 해 우 리 는 skbuf 를 얻 을 수 있 는 대기 열 을 찾 을 수 있다 는 것 을 알 고 있 습 니 다. 그러면 구체 적 인 순 서 는 무엇 입 니까? 저 는 여기 서 커 널 의 주석 을 베 꼈 습 니 다. 그것 은 매우 명확 하 게 말 합 니 다.
인용 하 다.
Look: we have the following (pseudo)queues:
1. packets in flight
2. backlog
3. prequeue
4. receive_queue
Each queue can be processed only if the next ones are empty. At this point we have empty receive_queue.But prequeue _can_ be not empty after 2nd iteration, when we jumped to start of loop because backlog
processing added something to receive_queue. We cannot release_sock(), because backlog containd packets arrived _after_ prequeued ones.
Shortly, algorithm is clear --- to process all the queues in order. We could make it more directly,requeueing packets from backlog to prequeue, if is not empty. It is more elegant, but eats cycles,
이 함수 가 비교적 복잡 하기 때문에 우 리 는 단락 을 나 누 어 이 함 수 를 분석한다.
우선 가방 을 처리 하기 전의 합 법성 판단 과 유용 한 가 치 를 얻 는 것 이다.

int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
		size_t len, int nonblock, int flags, int *addr_len)
{
...................................

///     socket。
	lock_sock(sk);

	TCP_CHECK_TIMER(sk);

	err = -ENOTCONN;
	if (sk->sk_state == TCP_LISTEN)
		goto out;


///      (        ).       0.
	timeo = sock_rcvtimeo(sk, nonblock);

	/* Urgent data needs to be handled specially. */
	if (flags & MSG_OOB)
		goto recv_urg;

///    tcp               。
	seq = &tp->copied_seq;
	if (flags & MSG_PEEK) {
		peek_seq = tp->copied_seq;
		seq = &peek_seq;
	}

///       MSG_WAITALL     。                        。
	target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
}

위 에서 우 리 는 lock 를 보 았 다.sock, 이 함 수 는 현재 sock 을 잠 그 는 데 사 용 됩 니 다. 자세 한 실현 을 보 겠 습 니 다. lock 을 호출 합 니 다.sock_nested:


void lock_sock_nested(struct sock *sk, int subclass)
{
	might_sleep();
///    。
	spin_lock_bh(&sk->sk_lock.slock);
///  owned 1,             sock。    __lock_sock(          ,      )。
	if (sk->sk_lock.owned)
		__lock_sock(sk);
/// sock     ,   owned 1,          。
	sk->sk_lock.owned = 1;
///  。
	spin_unlock(&sk->sk_lock.slock);
	/*
	 * The sk_lock has mutex_lock() semantics here:
	 */
	mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_);
	local_bh_enable();
}

우리 다시 보 자lock_sock 어떻게 처 리 했 어 요?
static void __lock_sock(struct sock *sk)
{
	DEFINE_WAIT(wait);

	for (;;) {
///      ,            sl_lock.wq,           。                 。
		prepare_to_wait_exclusive(&sk->sk_lock.wq, &wait,
					TASK_UNINTERRUPTIBLE);
///  。
		spin_unlock_bh(&sk->sk_lock.slock);
///  cpu,    。
		schedule();
		spin_lock_bh(&sk->sk_lock.slock);
///          sock,     。
		if (!sock_owned_by_user(sk))
			break;
	}
	finish_wait(&sk->sk_lock.wq, &wait);
}

ok, 다시 tcp 로 돌아 가기recvmsg. 다음은 패 킷 을 어떻게 처리 하 는 지 보 겠 습 니 다.
다음 단락 은 receive 대기 열 에서 데 이 터 를 읽 는 데 사 용 됩 니 다.


do {
		u32 offset;

///   urgent  ,                  sigurg  ,       。
		if (tp->urg_data && tp->urg_seq == *seq) {
			if (copied)
				break;
			if (signal_pending(current)) {
				copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
				break;
			}
		}


///    buf,    receive     buf。
		skb_queue_walk(&sk->sk_receive_queue, skb) {
///    receive_queue.
			if (before(*seq, TCP_SKB_CB(skb)->seq)) {
				printk(KERN_INFO "recvmsg bug: copied %X "
				       "seq %X
", *seq, TCP_SKB_CB(skb)->seq); break; } /// tcp , , , seq , sock , seq skb 。 skb->len, , skb skb( ). offset = *seq - TCP_SKB_CB(skb)->seq; /// syn。 if (tcp_hdr(skb)->syn) offset--; /// skb。 if (offset < skb->len) goto found_ok_skb; if (tcp_hdr(skb)->fin) goto found_fin_ok; WARN_ON(!(flags & MSG_PEEK)); } .................................... }while(len > 0)

다음은 tcp 상태 에 대한 검증 입 니 다.copied 는 사용자 공간 으로 복 사 된 skb 의 크기 를 표시 합 니 다.렌 은 데 이 터 를 얼마나 더 복사 해 야 하 는 지 를 밝 혔 다.


///                ,  sk_backlog  ,     。              ,    sk_backlog       receive   。
if (copied >= target && !sk->sk_backlog.tail)
			break;

		if (copied) {
			if (sk->sk_err ||
			    sk->sk_state == TCP_CLOSE ||
			    (sk->sk_shutdown & RCV_SHUTDOWN) ||
			    !timeo ||
			    signal_pending(current))
				break;
		} else {

///        (   receive  ),          。                。
			if (sock_flag(sk, SOCK_DONE))
				break;

			if (sk->sk_err) {
				copied = sock_error(sk);
				break;
			}

			if (sk->sk_shutdown & RCV_SHUTDOWN)
				break;

			if (sk->sk_state == TCP_CLOSE) {
				if (!sock_flag(sk, SOCK_DONE)) {
					copied = -ENOTCONN;
					break;
				}
				break;
			}

			if (!timeo) {
				copied = -EAGAIN;
				break;
			}

			if (signal_pending(current)) {
				copied = sock_intr_errno(timeo);
				break;
			}
		}


그 다음 에 복 사 된 데이터 크기 에 따라 receive 대기 열 에 있 는 데 이 터 를 정리 하고 ACK 를 상대방 에 게 보 냅 니 다.그리고 tcpsocket 의 ucopy 역 할당 값 은 주로 iov 역 과 task 역 입 니 다.하 나 는 데이터 영역 이 고 하 나 는 현재 종속 프로 세 스 입 니 다.


tcp_cleanup_rbuf(sk, copied);

		if (!sysctl_tcp_low_latency && tp->ucopy.task == user_recv) {

///        user_recv  ,   ucopy      。
			if (!user_recv && !(flags & (MSG_TRUNC | MSG_PEEK))) {
//       。
				user_recv = current;
				tp->ucopy.task = user_recv;
				tp->ucopy.iov = msg->msg_iov;
			}
///             。
			tp->ucopy.len = len;

			WARN_ON(tp->copied_seq != tp->rcv_nxt &&
				!(flags & (MSG_PEEK | MSG_TRUNC)));
///  prequeue       do_prequeue,  backlog  。
			if (!skb_queue_empty(&tp->ucopy.prequeue))
				goto do_prequeue;

			/* __ Set realtime policy in scheduler __ */
		}
///      ,     back_log   receive  。
		if (copied >= target) {
			/* Do not sleep, just process backlog. */
			release_sock(sk);
			lock_sock(sk);
		} else
///      ,       。
			sk_wait_data(sk, &timeo);

위의 분석 에는 release 가 있 습 니 다.sock 함수, 이 함 수 는 release 이 sock, 즉 이 sock 에 대한 잠 금 해제 에 사 용 됩 니 다.대기 열 을 깨 워 라.
여기 주의 하 세 요. sock 은 모두 두 개의 대기 행렬 이 있 습 니 다. 하 나 는 sock 의 sk 입 니 다.sleep 대기 열, 이 대기 열 은 데이터 가 오 기 를 기다 리 는 데 사 용 됩 니 다.하 나 는 ucopy 역 의 대기 열 wq 입 니 다. 이것 은 이 sock 을 사용 하 기 를 기다 리 는 것 을 의미 합 니 다.
void release_sock(struct sock *sk)
{

	mutex_release(&sk->sk_lock.dep_map, 1, _RET_IP_);

	spin_lock_bh(&sk->sk_lock.slock);
///  backlog     ,   __release_sock  
	if (sk->sk_backlog.tail)
		__release_sock(sk);
///      owened   0.     sock   。
	sk->sk_lock.owned = 0;
///  wq      。
	if (waitqueue_active(&sk->sk_lock.wq))
		wake_up(&sk->sk_lock.wq);
	spin_unlock_bh(&sk->sk_lock.slock);
}

그리고 주요 처리 함수release_sock, 주로 backlog 대기 열 을 옮 겨 다 니 며 skb 를 처리 합 니 다.여기 에는 두 개의 순환 이 있 습 니 다. 외부 순환 은 backlog 를 옮 겨 다 니 는 것 이 고 내부 순환 은 skb (즉 데이터) 를 옮 겨 다 니 는 것 입 니 다.
static void __release_sock(struct sock *sk)
{
	struct sk_buff *skb = sk->sk_backlog.head;

///  backlog  。
	do {
		sk->sk_backlog.head = sk->sk_backlog.tail = NULL;
		bh_unlock_sock(sk);

		do {
			struct sk_buff *next = skb->next;

			skb->next = NULL;

///            tcp_v4_do_rcv.  tcp_v4_do_rcv ,       receive_queue   。
			sk_backlog_rcv(sk, skb);
			cond_resched_softirq();

			skb = next;
		} while (skb != NULL);

		bh_lock_sock(sk);
	} while ((skb = sk->sk_backlog.head) != NULL);
}

데이터 tp - > ucopy. prequeue 가 비어 있 고 복 사 된 데이터 가 원 하 는 값 에 도달 하지 못 할 때 sk 에 들 어 갑 니 다.wait_데이터 가 오 기 를 기다 리 고 있 습 니 다.

#define sk_wait_event(__sk, __timeo, condition)			\
	({int  __rc;						\
///          ,       receive-queue  。
	release_sock(__sk);					\
	__rc = condition;	
/// sk_wait_data   ,rc     receive_queue     ,			\
	if (!__rc) {	
///          ,sk_sleep       。					\
		*(__timeo) = schedule_timeout(*(__timeo));	\
		}							\
		lock_sock(__sk);					\
__rc = condition;				\
		__rc;							\
	})



int sk_wait_data(struct sock *sk, long *timeo)
{
	int rc;
	DEFINE_WAIT(wait);
///  sk_sleep     
	prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
	set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
///    
	rc = sk_wait_event(sk, timeo, !skb_queue_empty(&sk->sk_receive_queue));
	clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
	finish_wait(sk->sk_sleep, &wait);
	return rc;
}

다음은 도 메 인 업데이트 및 prequeue 대기 열 처리:
		

if (user_recv) {
			int chunk;

			/* __ Restore normal policy in scheduler __ */
///          release_sock ,                 。        len  copied 。
			if ((chunk = len - tp->ucopy.len) != 0) {
				NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG, chunk);
				len -= chunk;
				copied += chunk;
			}
///tp->rcv_nxt == tp->copied_seq        receive            (        )。
			if (tp->rcv_nxt == tp->copied_seq &&
			    !skb_queue_empty(&tp->ucopy.prequeue)) {
do_prequeue:
///  prequeue
				tcp_prequeue_process(sk);

///     ,  len cpoied 。
				if ((chunk = len - tp->ucopy.len) != 0) {
					NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, chunk);
					len -= chunk;
					copied += chunk;
				}
			}
		}
...................................
		continue;

tcp 분석 중prequeue_process 전에 어떤 상황 에서 releasesock 은 사용자 공간 으로 데 이 터 를 직접 복사 합 니 다.우 리 는 그것 이 결국 tcp 를 호출 할 것 이라는 것 을 안다.rcv_established 함수, 그래서 tcprcv_settings 코드 세 션
커 널 이 받 은 패 킷 은 정상 적 인 것 이 아 닐 수도 있 지만 커 널 이 사용자 공간 에 전달 하 는 데 이 터 는 반드시 정상 적 인 것 이 어야 합 니 다. 그래 야 사용자 공간 에 복사 할 수 있 습 니 다.


else {
			int eaten = 0;
			int copied_early = 0;
///       。copied_seq     skb    。 rcv_nxt                  。              。      len - tcp_header_len <= tp->ucopy.len                 。        ,       receive_queue  。
		if (tp->copied_seq == tp->rcv_nxt &&
			   len - tcp_header_len <= tp->ucopy.len) {
.........................

///                   。          。
	if (tp->ucopy.task == current &&
		sock_owned_by_user(sk) && !copied_early) {
					__set_current_state(TASK_RUNNING);
///           。
		if (!tcp_copy_to_iovec(sk, skb, tcp_header_len))
						eaten = 1;
				}
	
...............................


위의 판단 조건 을 통 해 우 리 는 앞에서 release 를 호출 하 는 것 을 쉽게 알 수 있다.sock, 왜 사용자 공간 에 데 이 터 를 복사 하고 receive 대기 열 에 복사 합 니까?
ok, 마지막 으로 tcpprequeue_프로 세 스 의 실현.그것 의 실현 은 매우 간단 하 다. 바로 prequeue 를 옮 겨 다 니 며 buf 를 처리 하 는 것 이다.모든 prequeue 를 처리 합 니 다. 즉, prequeue 를 비 웁 니 다.
static void tcp_prequeue_process(struct sock *sk)
{
	struct sk_buff *skb;
	struct tcp_sock *tp = tcp_sk(sk);

	NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPPREQUEUED);

	/* RX process wants to run with disabled BHs, though it is not
	 * necessary */
	local_bh_disable();
///     skb。
	while ((skb = __skb_dequeue(&tp->ucopy.prequeue)) != NULL)
///     tcp_rcv_established.
		sk_backlog_rcv(sk, skb);
	local_bh_enable();
///     0.
	tp->ucopy.memory = 0;
}

마지막 으로 데 이 터 를 사용자 공간 으로 어떻게 복사 하 는 지 간략하게 분석 하 세 요.이곳 의 주요 함 수 는 skb 입 니 다.copy_datagram_iovec。결국 이 함 수 를 통 해 사용자 공간 으로 복 사 됩 니 다.
커 널 저장 데 이 터 는 S / G IO 의 네트워크 카드 를 지원 하면 skb 에 저 장 됩 니 다.shinfo (skb) - > frags (앞의 blog 참조), 그렇지 않 으 면 skb 의 data 구역 에 저 장 됩 니 다.
그래서 여기 도 두 부분 으로 나 뉘 어 처리 된다.
또 하 나 는 여기 서 frags 를 옮 겨 다 니 는 것 도 두 번 이 고 첫 번 째 는 찾 는 것 이 좋 습 니 다.
int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
			    struct iovec *to, int len)
{
	int start = skb_headlen(skb);
	int i, copy = start - offset;
	struct sk_buff *frag_iter;
///  S/G IO   ,           data   。
	if (copy > 0) {
		if (copy > len)
			copy = len;
///  data 。
		if (memcpy_toiovec(to, skb->data + offset, copy))
			goto fault;
		if ((len -= copy) == 0)
			return 0;
		offset += copy;
	}

///  frags,      。
	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
		int end;

		WARN_ON(start > offset + len);
///        
		end = start + skb_shinfo(skb)->frags[i].size;
///              
		if ((copy = end - offset) > 0) {
			int err;
			u8  *vaddr;
			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
			struct page *page = frag->page;
///           ,          。
			if (copy > len)
				copy = len;
///           。
			vaddr = kmap(page);
///    。
			err = memcpy_toiovec(to, vaddr + frag->page_offset +
					     offset - start, copy);
			kunmap(page);
			if (err)
				goto fault;
///         0
			if (!(len -= copy))
				return 0;
///  offset 。
			offset += copy;
		}
//  start 。
		start = end;
	}

///               ,              。         offset 。
	skb_walk_frags(skb, frag_iter) {
		int end;

		WARN_ON(start > offset + len);

		end = start + frag_iter->len;
		if ((copy = end - offset) > 0) {
			if (copy > len)
				copy = len;
///  offset  offset-start      。
			if (skb_copy_datagram_iovec(frag_iter,
						    offset - start,
						    to, copy))
				goto fault;
			if ((len -= copy) == 0)
				return 0;
			offset += copy;
		}
		start = end;
	}
	if (!len)
		return 0;

fault:
	return -EFAULT;
}

좋은 웹페이지 즐겨찾기