Linux内核中网络数据的传输

分析网络数据的传输,首先需要了解TCP/IP模型:
APPLICATION <=> SOCKET <=>TCP/UDP <=> IPv4/IPv6 <=> MAC <=> PHY


SOCKET: net/socket.c
熟悉socket编程的人都知道网络数据通过sock进行传输,比如write/sendmsg(sock, buff, sizeof(buff)),
那么Linux内核是如何处理网络数据呢? 本文带着这个问题对linux的源码进行分析:
 
通信前应用程序会先创建一个sock,sock = socket(AF_INET, SOCK_STREAM, 0),那么socket()的参数代表着什么意思?
内核又是如何创建socket的呢? 想弄明白这个问题就需要对linux是如何创建socket进行分析:


sock = socket(AF_INET, SOCK_STREAM, 0)
->SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
->sock_create(family, type, protocol, &sock);
->pf = rcu_dereference(net_families[family]);
{
应用程序中sock = socket(AF_INET, SOCK_STREAM, 0)中的第一个参数为family参数,family是PF_INET。
那么pf是什么东西? 要搞懂这个问题就得从fs_initcall(inet_init)开始分析:
sock_register(&inet_family_ops)
{
static const struct net_proto_family inet_family_ops = 
{
.family = AF_INET,
.create = inet_create,
.owner  = THIS_MODULE,
}

net_families[inet_family_ops->family] = inet_family_ops;
}

因此net_families[family]对应的net_proto_family为inet_family_ops,也就是说pf为inet_family_ops
};
->pf->create(net, sock, protocol, kern);
->inet_create

分析到这里可知应用程序中的sock = socket()最终会调到inet_create():
inet_create() net/ipv4/af_inet.c
{
1. 根据protocol参数在inetsw[sock->type]数组中查找对应的inet_protosw, protocol参数是由sock = socket()
中的第二个参数, 参数类型有SOCK_STREAM,SOCK_DGRAM,SOCK_RAW。对应的code为: 
list_for_each_entry_rcu(answer, &inetsw[sock->type], list)
if (protocol == answer->protocol)
break;

这里出现inetsw如下,代表了IPV4不同的协议簇, 对应的文件为net/ipv4/af_inet.c中
static struct inet_protosw inetsw_array[] =  
{
{
.type SOCK_STREAM,
.protocol = IPPROTO_TCP,
.prot =     &tcp_prot,
.ops =      &inet_stream_ops,
},
{
.type =     SOCK_DGRAM,
.protocol = IPPROTO_UDP,
.prot =     &udp_prot,
.ops =      &inet_dgram_ops,
},
..............................
}

2. sock->ops = answer->ops = inet_stream_ops;

3. sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot, kern);
sk->sk_prot = answer->prot = tcp_prot;
}

上文中推出来的结论:sock->ops->sendmsg = inet_sendmsg()和sk->sk_prot = tcp_prot在下文中使用。
write/sendmsg()
->SYSCALL_DEFINE3(sendmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags)
->___sys_sendmsg
->sock_sendmsg(sock, msg_sys);
->sock_sendmsg_nosec(sock, msg);
->sock->ops->sendmsg(sock, msg, msg_data_left(msg));
->inet_sendmsg()
->sk->sk_prot->sendmsg(sk, msg, size);
->tcp_prot.tcp_sendmsg

->tcp_sendmsg 


分析到这里可知网络数据已经从sock层过渡到TCP/UDP协议层
TCP/UDP:tcp_sendmsg net/ipv4/tcp.c
->tcp_push
->__tcp_push_pending_frames();
->tcp_write_xmit net/ipv4/tcp_output.c
->tcp_transmit_skb(sk, skb, 1, gfp)
{
1. 首先需要克隆或者复制skb,因为网络的framework会在成功发送成功之后释放SKB,发送成功
并不意味这我握手成功,因此TCP层需要保留SKB,再收到ACK之后释放SKB才能释放skb
{
1.1 if (unlikely(skb_cloned(skb))) 如果skb已经是复制过的skb,则调用pskb_copy
skb = pskb_copy(skb, gfp_mask);
else
skb = skb_clone(skb, gfp_mask);

1.2.  在分析skb_clone之前先分析__alloc_skb
{
内核会从两个已经分配好的内存中分配sk_buff,分别为skbuff_fclone_cache和skbuff_head_cache,
对应的code如下,决定因素在于flags:
cache = (flags & SKB_ALLOC_FCLONE) ? skbuff_fclone_cache : skbuff_head_cache;
skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);

那么skbuff_fclone_cache和skbuff_head_cache这两个内存空间是什么?有什么区别?
由code可知skbuff_head_cache的内存单位为sizeof(struct sk_buff),即每次只能分配
一个sk_buff,而skbuff_fclone_cache的内存单位为struct sk_buff_fclones, 每次可分配两个sk_buff, 分别为skb1和skb2:
skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
   sizeof(struct sk_buff), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache",
   sizeof(struct sk_buff_fclones), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);

分配完还要在DMA memory中分配data的内存空间
d
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值