Linux内核--网络栈实现分析(七)--数据包的传递过程(下)

本文分析基于Linux Kernel 1.2.13

原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7545855

更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html

作者:闫明

注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析、”(下)“表示分析是从上向下分析。

在博文Linux内核--网络栈实现分析(二)--数据包的传递过程(上)中分析了数据包从网卡设备经过驱动链路层,网络层,传输层到应用层的过程。

本文就分析一下本机产生数据是如何通过传输层,网络层到达物理层的。

综述来说,数据流程图如下:

Linux内核--网络栈实现分析(七)--数据包的传递过程(下)

一、应用层

应用层可以通过系统调用或文件操作来调用内核函数,BSD层的sock_write()函数会调用INET层的inet_wirte()函数。

  1. /*
  2. *  Write data to a socket. We verify that the user area ubuf..ubuf+size-1 is
  3. *  readable by the user process.
  4. */
  5. static int sock_write(struct inode *inode, struct file *file, char *ubuf, int size)
  6. {
  7. struct socket *sock;
  8. int err;
  9. if (!(sock = socki_lookup(inode)))
  10. {
  11. printk("NET: sock_write: can't find socket for inode!\n");
  12. return(-EBADF);
  13. }
  14. if (sock->flags & SO_ACCEPTCON)
  15. return(-EINVAL);
  16. if(size<0)
  17. return -EINVAL;
  18. if(size==0)
  19. return 0;
  20. if ((err=verify_area(VERIFY_READ,ubuf,size))<0)
  21. return err;
  22. return(sock->ops->write(sock, ubuf, size,(file->f_flags & O_NONBLOCK)));
  23. }

INET层会调用具体传输层协议的write函数,该函数是通过调用本层的inet_send()函数实现功能的,inet_send()函数的UDP协议对应的函数为udp_write()

  1. static int inet_send(struct socket *sock, void *ubuf, int size, int noblock,
  2. unsigned flags)
  3. {
  4. struct sock *sk = (struct sock *) sock->data;
  5. if (sk->shutdown & SEND_SHUTDOWN)
  6. {
  7. send_sig(SIGPIPE, current, 1);
  8. return(-EPIPE);
  9. }
  10. if(sk->err)
  11. return inet_error(sk);
  12. /* We may need to bind the socket. */
  13. if(inet_autobind(sk)!=0)
  14. return(-EAGAIN);
  15. return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, flags));
  16. }
  17. static int inet_write(struct socket *sock, char *ubuf, int size, int noblock)
  18. {
  19. return inet_send(sock,ubuf,size,noblock,0);
  20. }

二、传输层

在传输层udp_write()函数调用本层的udp_sendto()函数完成功能。

  1. /*
  2. *  In BSD SOCK_DGRAM a write is just like a send.
  3. */
  4. static int udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,
  5. unsigned flags)
  6. {
  7. return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));
  8. }

udp_send()函数完成sk_buff结构相应的设置和报头的填写后会调用udp_send()来发送数据。具体的实现过程后面会详细分析。

而在udp_send()函数中,最后会调用ip_queue_xmit()函数,将数据包下放的网络层。

下面是udp_prot定义:

  1. struct proto udp_prot = {
  2. sock_wmalloc,
  3. sock_rmalloc,
  4. sock_wfree,
  5. sock_rfree,
  6. sock_rspace,
  7. sock_wspace,
  8. udp_close,
  9. udp_read,
  10. udp_write,
  11. udp_sendto,
  12. udp_recvfrom,
  13. ip_build_header,
  14. udp_connect,
  15. NULL,
  16. ip_queue_xmit,
  17. NULL,
  18. NULL,
  19. NULL,
  20. udp_rcv,
  21. datagram_select,
  22. udp_ioctl,
  23. NULL,
  24. NULL,
  25. ip_setsockopt,
  26. ip_getsockopt,
  27. 128,
  28. 0,
  29. {NULL,},
  30. "UDP",
  31. 0, 0
  32. };
  1. static int udp_send(struct sock *sk, struct sockaddr_in *sin,
  2. unsigned char *from, int len, int rt)
  3. {
  4. struct sk_buff *skb;
  5. struct device *dev;
  6. struct udphdr *uh;
  7. unsigned char *buff;
  8. unsigned long saddr;
  9. int size, tmp;
  10. int ttl;
  11. /*
  12. *  Allocate an sk_buff copy of the packet.
  13. */
  14. ........................
  15. /*
  16. *  Now build the IP and MAC header.
  17. */
  18. ..........................
  19. /*
  20. *  Fill in the UDP header.
  21. */
  22. ..............................
  23. /*
  24. *  Copy the user data.
  25. */
  26. memcpy_fromfs(buff, from, len);
  27. /*
  28. *  Set up the UDP checksum.
  29. */
  30. udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);
  31. /*
  32. *  Send the datagram to the interface.
  33. */
  34. udp_statistics.UdpOutDatagrams++;
  35. sk->prot->queue_xmit(sk, dev, skb, 1);
  36. return(len);
  37. }

三、网络层

在网络层,函数ip_queue_xmit()的功能是将数据包进行一系列复杂的操作,比如是检查数据包是否需要分片,是否是多播等一系列检查,最后调用dev_queue_xmit()函数发送数据。

  1. /*
  2. * Queues a packet to be sent, and starts the transmitter
  3. * if necessary.  if free = 1 then we free the block after
  4. * transmit, otherwise we don't. If free==2 we not only
  5. * free the block but also don't assign a new ip seq number.
  6. * This routine also needs to put in the total length,
  7. * and compute the checksum
  8. */
  9. void ip_queue_xmit(struct sock *sk, struct device *dev,
  10. struct sk_buff *skb, int free)
  11. {
  12. struct iphdr *iph;
  13. unsigned char *ptr;
  14. /* Sanity check */
  15. ............
  16. /*
  17. *  Do some book-keeping in the packet for later
  18. */
  19. ...........
  20. /*
  21. *  Find the IP header and set the length. This is bad
  22. *  but once we get the skb data handling code in the
  23. *  hardware will push its header sensibly and we will
  24. *  set skb->ip_hdr to avoid this mess and the fixed
  25. *  header length problem
  26. */
  27. ..............
  28. /*
  29. *  No reassigning numbers to fragments...
  30. */
  31. if(free!=2)
  32. iph->id      = htons(ip_id_count++);
  33. else
  34. free=1;
  35. /* All buffers without an owner socket get freed */
  36. if (sk == NULL)
  37. free = 1;
  38. skb->free = free;
  39. /*
  40. *  Do we need to fragment. Again this is inefficient.
  41. *  We need to somehow lock the original buffer and use
  42. *  bits of it.
  43. */
  44. ................
  45. /*
  46. *  Add an IP checksum
  47. */
  48. ip_send_check(iph);
  49. /*
  50. *  Print the frame when debugging
  51. */
  52. /*
  53. *  More debugging. You cannot queue a packet already on a list
  54. *  Spot this and moan loudly.
  55. */
  56. .......................
  57. /*
  58. *  If a sender wishes the packet to remain unfreed
  59. *  we add it to his send queue. This arguably belongs
  60. *  in the TCP level since nobody else uses it. BUT
  61. *  remember IPng might change all the rules.
  62. */
  63. ......................
  64. /*
  65. *  If the indicated interface is up and running, send the packet.
  66. */
  67. ip_statistics.IpOutRequests++;
  68. .............................
  69. .............................
  70. if((dev->flags&IFF_BROADCAST) && iph->daddr==dev->pa_brdaddr && !(dev->flags&IFF_LOOPBACK))
  71. ip_loopback(dev,skb);
  72. if (dev->flags & IFF_UP)
  73. {
  74. /*
  75. *  If we have an owner use its priority setting,
  76. *  otherwise use NORMAL
  77. */
  78. if (sk != NULL)
  79. {
  80. dev_queue_xmit(skb, dev, sk->priority);
  81. }
  82. else
  83. {
  84. dev_queue_xmit(skb, dev, SOPRI_NORMAL);
  85. }
  86. }
  87. else
  88. {
  89. ip_statistics.IpOutDiscards++;
  90. if (free)
  91. kfree_skb(skb, FREE_WRITE);
  92. }
  93. }

四、驱动层(链路层)

在函数中,函数调用会调用具体设备的发送函数来发送数据包

dev->hard_start_xmit(skb, dev);

具体设备的发送函数在网络初始化的时候已经设置了。

这里以8390网卡为例来说明驱动层的工作原理,在net/drivers/8390.c中函数ethdev_init()函数中设置如下:

  1. /* Initialize the rest of the 8390 device structure. */
  2. int ethdev_init(struct device *dev)
  3. {
  4. if (ei_debug > 1)
  5. printk(version);
  6. if (dev->priv == NULL) {//申请私有空间
  7. struct ei_device *ei_local;//8390网卡设备的结构体
  8. dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL);//申请内核内存空间
  9. memset(dev->priv, 0, sizeof(struct ei_device));
  10. ei_local = (struct ei_device *)dev->priv;
  11. #ifndef NO_PINGPONG
  12. ei_local->pingpong = 1;
  13. #endif
  14. }
  15. /* The open call may be overridden by the card-specific code. */
  16. if (dev->open == NULL)
  17. dev->open = &ei_open;//设备的打开函数
  18. /* We should have a dev->stop entry also. */
  19. dev->hard_start_xmit = &ei_start_xmit;//设备的发送函数,定义在8390.c中
  20. dev->get_stats   = get_stats;
  21. #ifdef HAVE_MULTICAST
  22. dev->set_multicast_list = &set_multicast_list;
  23. #endif
  24. ether_setup(dev);
  25. return 0;
  26. }

驱动中的发送函数比较复杂,和硬件关系紧密,这里不再详细分析。

这样就大体分析了下网络数据从应用层到物理层的数据通路,后面会详细分析。

上一篇:一张图讲解最少机器搭建FastDFS高可用分布式集群安装说明


下一篇:[转]亿级Web系统搭建:单机到分布式集群