本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/10162853

Jack:我想知道用户如何把数据发送到内核空间的?

我:你觉得哪里比较难理解呢?

Jack:一般程序员会在程序里通过socket变量获得一个文件描述符,然后通过write把定义好的字符串写入到该描述符。

我:是的。你有什么不明白的吗?

Jack:可是,我不知道这个write底层到底会做什么。

我:这个write底层会调用sock_send函数。我给你看一下这个函数的定义。

  1. static int
  2. sock_send(int fd, void * buff, int len, unsigned flags)
  3. {
  4. struct socket *sock;
  5. struct file *file;
  6.  
  7. DPRINTF((net_debug,
  8. "NET: sock_send(fd = %d, buff = %X, len = %d, flags = %X)\n",
  9. fd, buff, len, flags));
  10.  
  11. if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
  12. return(-EBADF);
  13. if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
  14.  
  15. return(sock->ops->send(sock, buff, len, (file->f_flags & O_NONBLOCK), flags));
  16. }

sock_send函数通过用户传入的socket描述符fd找到对应的struct socket结构,然后把找到的socket结构。然后把socket结构(sock),buff(这是一个逻辑地址),以及文件flag传入传输层的对应函数。

最后一个语句return调用了一个函数指针(
这就是函数指针的妙处!),这个函数指针如果对应下面的传输层协议是UDP协议,就会调用udp_sendto.

  1. static int
  2. udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,
  3.  unsigned flags)
  4. {
  5.   return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));
  6. }

其实是一个包裹函数。干活儿的是udp_sendto。

  1. static int
  2. udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,
  3.   unsigned flags, struct sockaddr_in *usin, int addr_len)
  4. {
  5.   struct sockaddr_in sin;
  6.   int tmp;
  7.   int err;
  8.  
  9.   DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags));
  10.  
  11.   /* Check the flags. */
  12.   if (flags) 
  13.   return(-EINVAL);
  14.   if (len < 0) 
  15.   return(-EINVAL);
  16.   if (len == 0) 
  17.   return(0);
  18.  
  19.   /* Get and verify the address. */
  20.   if (usin) {
  21. if (addr_len < sizeof(sin)) return(-EINVAL);
  22. err=verify_area(VERIFY_READ, usin, sizeof(sin));
  23. if(err)
  24. return err;
  25. memcpy_fromfs(&sin, usin, sizeof(sin));
  26. if (sin.sin_family && sin.sin_family != AF_INET) 
  27. return(-EINVAL);
  28. if (sin.sin_port == 0) 
  29. return(-EINVAL);
  30.   } else {
  31. if (sk->state != TCP_ESTABLISHED) return(-EINVAL);
  32. sin.sin_family = AF_INET;
  33. sin.sin_port = sk->dummy_th.dest;
  34. sin.sin_addr.s_addr = sk->daddr;
  35.   }
  36.   
  37.   if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
  38.     return -EACCES;/* Must turn broadcast on first */
  39.   sk->inuse = 1;
  40.  
  41.   /* Send the packet. */
  42.   tmp = udp_send(sk, &sin, from, len);
  43.  
  44.   /* The datagram has been sent off.  Release the socket. */
  45.   release_sock(sk);
  46.   return(tmp);
  47. }

这其实也是一个包裹函数,真正干活的是udp_send函数。

  1. static int
  2. udp_send(struct sock *sk, struct sockaddr_in *sin,
  3. unsigned char *from, int len)
  4. {
  5. struct sk_buff *skb;
  6. struct device *dev;
  7. struct udphdr *uh;
  8. unsigned char *buff;
  9. unsigned long saddr;
  10. int size, tmp;
  11. int err;
  12.  
  13. DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n",
  14. in_ntoa(sin->sin_addr.s_addr), ntohs(sin->sin_port),
  15. from, len));
  16.  
  17. err=verify_area(VERIFY_READ, from, len);
  18. if(err)
  19. return(err);
  20.  
  21. /* Allocate a copy of the packet. */
  22. size = sizeof(struct sk_buff) + sk->prot->max_header + len;
  23. skb = sk->prot->wmalloc(sk, size, 0, GFP_KERNEL);
  24. if (skb == NULL) return(-ENOMEM);
  25.  
  26. skb->mem_addr = skb;
  27. skb->mem_len = size;
  28. skb->sk = NULL; /* to avoid changing sk->saddr */
  29. skb->free = 1;
  30. skb->arp = 0;
  31.  
  32. /* Now build the IP and MAC header. */
  33. buff = skb->data;
  34. saddr = 0;
  35. dev = NULL;
  36. DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n",
  37. saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len));
  38. tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
  39. &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl);
  40. skb->sk=sk; /* So memory is freed correctly */
  41.  
  42. if (tmp < 0 ) {
  43. sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
  44. return(tmp);
  45. }
  46. buff += tmp;
  47. saddr = dev->pa_addr;
  48. DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp));
  49.  
  50. skb->len = tmp + sizeof(struct udphdr) + len; /* len + UDP + IP + MAC */
  51. skb->dev = dev;
  52. #ifdef OLD
  53. /*
  54. * This code used to hack in some form of fragmentation.
  55. * I removed that, since it didn't work anyway, and it made the
  56. * code a bad thing to read and understand. -FvK
  57. */
  58. if (len > dev->mtu) {
  59. #else
  60. if (skb->len > 4095)
  61. {
  62. #endif
  63. printk("UDP: send: length %d > mtu %d (ignored)\n", len, dev->mtu);
  64. sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
  65. return(-EMSGSIZE);
  66. }
  67.  
  68. /* Fill in the UDP header. */
  69. uh = (struct udphdr *) buff;
  70. uh->len = htons(len + sizeof(struct udphdr));
  71. uh->source = sk->dummy_th.source;
  72. uh->dest = sin->sin_port;
  73. buff = (unsigned char *) (uh + 1);
  74.  
  75. /* Copy the user data. */
  76. memcpy_fromfs(buff, from, len);
  77.  
  78. /* Set up the UDP checksum. */
  79. udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);
  80.  
  81. /* Send the datagram to the interface. */
  82. sk->prot->queue_xmit(sk, dev, skb, 1);
  83.  
  84. return(len);
  85. }

这个函数里真正干活的是memcpy_fromfs函数,执行完了这个函数,数据就已经从用户空间拷贝到内核空间了。

之后的sk->prot->queue_xmit(sk, dev, skb, 1);通过函数指针把sk上的skb这个数据包排入发送队列。

Linux内核源代码解析——用户发送数据包的起源之sendto的更多相关文章

  1. Linux内核源代码解析之——sock's buffer参数

    本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11539695 关于socket与sock的关系再简单 ...

  2. Linux内核源代码解析——TCP状态转移图以及其实现

    本文原创为freas_1990,转载请标明出处http://blog.csdn.net/freas_1990/article/details/10223581 TCP状态转移的原理并不高深,但是处理逻 ...

  3. Linux内核源代码解析之——我与神童聊Linux内核

    本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11619609 我的朋友里,至少有2.5个神童. 有的 ...

  4. Linux内核源代码解析之TCP面向字节流

    本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11264237 大家都知道TCP是面向stream,而 ...

  5. 多队列网卡简介以及Linux通过网卡发送数据包源码解读

    http://blog.csdn.net/yanghua_kobe/article/details/7485254 首先我们看一下一个主流多队列网卡(E1000)跟多核CPU之间的关系图: 非多队列: ...

  6. Linux网络之设备接口层:发送数据包流程dev_queue_xmit

    转自:http://blog.csdn.net/wdscq1234/article/details/51926808 写在前面 本文主要是分析kernel-3.8的源代码,主要集中在Network的n ...

  7. Linux内核源代码情景分析系列

    http://blog.sina.com.cn/s/blog_6b94d5680101vfqv.html Linux内核源代码情景分析---第五章 文件系统  5.1 概述 构成一个操作系统最重要的就 ...

  8. linux内核空间与用户空间信息交互方法

    linux内核空间与用户空间信息交互方法     本文作者: 康华:计算机硕士,主要从事Linux操作系统内核.Linux技术标准.计算机安全.软件测试等领域的研究与开发工作,现就职于信息产业部软件与 ...

  9. TCP和UDP 协议发送数据包的大小

    在进行UDP编程的时候,我们最容易想到的问题就是,一次发送多少bytes好? 当然,这个没有唯一答案,相对于不同的系统,不同的要求,其得到的答案是不一样的,这里仅对像ICQ一类的发送聊天消息的情况作分 ...

随机推荐

  1. perl /m 当作多行处理

    高级用法 多行匹配: zjtest7-frontend:/root/0825# cat a2.pl print "1111111111111\n"; my $_="abc ...

  2. 为什么不能在scrollview中直接添加一个image,然后使animation.begin()??

    http://stackoverflow.com/questions/17267451/animation-cant-begin-in-scrollview-in-windows-phone 以上是我 ...

  3. Winform 窗体设计器 无法识别重复成员变量声明的问题

    打开窗体设计视图出现如下错误: 查看后台代码: ColumnHeader colHead; colHead = new ColumnHeader(); colHead.Text = "Ch& ...

  4. linux分区,文件系统,目录结构概述

    1.Linux中如何表示硬盘,分区 Linux内核读取光驱,硬盘等资源时均通过“设备文件”的形式进行,因此在linux系统中,将硬 盘和分区表示为不同的文件.具体表述形式如下: 硬盘:对于IDE接口的 ...

  5. C语言运算符的优先级

    熟悉C语言的同学都知道,C语言众多的运算符及繁琐难记的优先级总是搞得我们这些C初学者头大.那么本文就 对C语言中所有的运算符进行汇总,并对其优先级进行一定的介绍. 这里虽然对所有C运算符的优先级进行了 ...

  6. DisplayMetircs 类

    Android 可设置为随着窗口大小调整缩放比例,但即便如此,手机程序设计人员还是必须知道手机屏幕的边界,以避免缩放造成的布局变形问题. 手机的分辨率信息是手机的一项重要信息,很好的是,Android ...

  7. CentOS 6.8yum源的配置

    Centos配置163的yum源 1.首先备份当前系统的yum源 # mv /etc/yum.repo.d/Centos-Base.repo /etc/yum.repo.d/Centos-Base.r ...

  8. magento url rewrite

    Magento 设置 Rewrite Url 方法. 1.apache 要加载 Rewrite 扩展模块.2.网站根目录要有 .htaccess 文件.3.Magento 后台要设置启用 Rewrit ...

  9. mysql 1449 : The user specified as a definer ('montor'@'%') does not exist

    grant all privileges on *.* to root@"%" identified by "."; flush privileges;  

  10. Basic Concepts of Block Media Recovery

    Basic Concepts of Block Media Recovery Whenever block corruption has been automatically detected, yo ...