内核中的TCP的追踪分析-19-TCP(IPV4)的服务器端数据的接收
static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags)
{
。。。。。。
return sock->ops->recvmsg(iocb, sock, msg, size, flags);
}
const struct proto_ops inet_stream_ops = {
。。。。。。
.recvmsg = sock_common_recvmsg,
。。。。。。
}
很显然这里的要执行的钩子函数是sock_common_recvmsg,这个函数在/net/core/sock.c中的1852行处
int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags)
{
struct sock *sk = sock->sk;
int addr_len = 0;
int err;
err = sk->sk_prot->recvmsg(iocb, sk, msg, size, flags & MSG_DONTWAIT,
flags & ~MSG_DONTWAIT, &addr_len);
if (err >= 0)
msg->msg_namelen = addr_len;
return err;
}
同样我们会通过socket得到sock结构,进而执行sock中的钩子结构变量所挂入的函数,这个sock的钩子结构是在
http://blog.chinaunix.net/u2/64681/showart.php?id=1360583
那节中讲述了设置成了tcp_prot结构变量,并且在那里也列出该变量的详细内容,我们再列也与上面函数相关的部分
struct proto tcp_prot = {
。。。。。。
.recvmsg = tcp_recvmsg,
。。。。。。
}
所以上面的函数进入了tcp_recvmsg(),这个函数在/net/ipv4/tcp.c中的1271行处
int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int nonblock, int flags, int *addr_len)
{
struct tcp_sock *tp = tcp_sk(sk);
int copied = 0;
u32 peek_seq;
u32 *seq;
unsigned long used;
int err;
int target; /* Read at least this many bytes */
long timeo;
struct task_struct *user_recv = NULL;
int copied_early = 0;
struct sk_buff *skb;
lock_sock(sk);
TCP_CHECK_TIMER(sk);
err = -ENOTCONN;
if (sk->sk_state == TCP_LISTEN)
goto out;
timeo = sock_rcvtimeo(sk, nonblock);
/* Urgent data needs to be handled specially. */
if (flags & MSG_OOB)
goto recv_urg;
seq = &tp->copied_seq;
if (flags & MSG_PEEK) {
peek_seq = tp->copied_seq;
seq = &peek_seq;
}
target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
函数非常的长,我们看到上面设置了一下定时器,并且检查了一下socket的状态,如果还是监听状态就说明出错了,因为我们接收是已经在监听成功并且已经与客户端建立连接后进行的,这些过程我们前边的章节都说过了。
#ifdef CONFIG_NET_DMA
tp->ucopy.dma_chan = NULL;
preempt_disable();
skb = skb_peek_tail(&sk->sk_receive_queue);
{
int available = 0;
if (skb)
available = TCP_SKB_CB(skb)->seq + skb->len - (*seq);
if ((available target) &&
(len > sysctl_tcp_dma_copybreak) && !(flags & MSG_PEEK) &&
!sysctl_tcp_low_latency &&
__get_cpu_var(softnet_data).net_dma) {
preempt_enable_no_resched();
tp->ucopy.pinned_list =
dma_pin_iovec_pages(msg->msg_iov, len);
} else {
preempt_enable_no_resched();
}
}
#endif
do {
u32 offset;
/* Are we at urgent data? Stop if we have read anything or have SIGURG pending. */
if (tp->urg_data && tp->urg_seq == *seq) {
if (copied)
break;
if (signal_pending(current)) {
copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
break;
}
}
/* Next get a buffer. */
skb = skb_peek(&sk->sk_receive_queue);
do {
if (!skb)
break;
/* Now that we have two receive queues this
* shouldn't happen.
*/
if (before(*seq, TCP_SKB_CB(skb)->seq)) {
printk(KERN_INFO "recvmsg bug: copied %X "
"seq %X\n", *seq, TCP_SKB_CB(skb)->seq);
break;
}
offset = *seq - TCP_SKB_CB(skb)->seq;
if (tcp_hdr(skb)->syn)
offset--;
if (offset skb->len)
goto found_ok_skb;
if (tcp_hdr(skb)->fin)
goto found_fin_ok;
BUG_TRAP(flags & MSG_PEEK);
skb = skb->next;
} while (skb != (struct sk_buff *)&sk->sk_receive_queue);
如果内核打开了网络的DMA功能则会直接从sock结构中的sk_receive_queue接受队列中摘取数据了,我们将在后边的客户端的数据发送中看到如何链入到这个队列的,这里我们看到如果支持网络的DMA的话就会直接从接受队列中摘取sk_buff数据包,如果不支持DMA的话就会执行代码中的do-while循环从接受队列中计算数据包的序列号直到挑选符合顺序的数据包。
文章来源CU社区:内核中的TCP的追踪分析-19-TCP(IPV4)的服务器端数据的接收
相关文章