本文分析基于内核Linux 1.2.13

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

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

作者:闫明

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

上篇博文分析了传输层从网络层获取数据包后将数据包缓存结构sk_buff挂载到特定的sock结构的接收队列中。

这里接着分析应用程序是如何从传输层获取网络数据包的。应用层要得到传输层的数据包有两种主要的方式:系统调用和文件操作。

系统调用:

Linux下用户程序是通过系统调用来从用户态到内核态,调用内核功能来完成相应的服务。

网络栈的一些功能是通过系统调用sys_socketcall来完成的

具体的代码在net/socket.c中,该文件中的函数就相当于一个桥梁,在系统调用和内核网络栈之间。

  1. /*
  2. *  System call vectors. Since I (RIB) want to rewrite sockets as streams,
  3. *  we have this level of indirection. Not a lot of overhead, since more of
  4. *  the work is done via read/write/select directly.
  5. *
  6. *  I'm now expanding this up to a higher level to separate the assorted
  7. *  kernel/user space manipulations and global assumptions from the protocol
  8. *  layers proper - AC.
  9. */
  10. asmlinkage int sys_socketcall(int call, unsigned long *args)
  11. {
  12. int er;
  13. switch(call)
  14. {
  15. case SYS_SOCKET:
  16. er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
  17. if(er)
  18. return er;
  19. return(sock_socket(get_fs_long(args+0),
  20. get_fs_long(args+1),
  21. get_fs_long(args+2)));
  22. case SYS_BIND:
  23. er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
  24. if(er)
  25. return er;
  26. return(sock_bind(get_fs_long(args+0),
  27. (struct sockaddr *)get_fs_long(args+1),
  28. get_fs_long(args+2)));
  29. case SYS_CONNECT:
  30. er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
  31. if(er)
  32. return er;
  33. return(sock_connect(get_fs_long(args+0),
  34. (struct sockaddr *)get_fs_long(args+1),
  35. get_fs_long(args+2)));
  36. case SYS_LISTEN:
  37. er=verify_area(VERIFY_READ, args, 2 * sizeof(long));
  38. if(er)
  39. return er;
  40. return(sock_listen(get_fs_long(args+0),
  41. get_fs_long(args+1)));
  42. case SYS_ACCEPT:
  43. er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
  44. if(er)
  45. return er;
  46. return(sock_accept(get_fs_long(args+0),
  47. (struct sockaddr *)get_fs_long(args+1),
  48. (int *)get_fs_long(args+2)));
  49. case SYS_GETSOCKNAME:
  50. er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
  51. if(er)
  52. return er;
  53. return(sock_getsockname(get_fs_long(args+0),
  54. (struct sockaddr *)get_fs_long(args+1),
  55. (int *)get_fs_long(args+2)));
  56. case SYS_GETPEERNAME:
  57. er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
  58. if(er)
  59. return er;
  60. return(sock_getpeername(get_fs_long(args+0),
  61. (struct sockaddr *)get_fs_long(args+1),
  62. (int *)get_fs_long(args+2)));
  63. case SYS_SOCKETPAIR:
  64. er=verify_area(VERIFY_READ, args, 4 * sizeof(long));
  65. if(er)
  66. return er;
  67. return(sock_socketpair(get_fs_long(args+0),
  68. get_fs_long(args+1),
  69. get_fs_long(args+2),
  70. (unsigned long *)get_fs_long(args+3)));
  71. case SYS_SEND:
  72. er=verify_area(VERIFY_READ, args, 4 * sizeof(unsigned long));
  73. if(er)
  74. return er;
  75. return(sock_send(get_fs_long(args+0),
  76. (void *)get_fs_long(args+1),
  77. get_fs_long(args+2),
  78. get_fs_long(args+3)));
  79. case SYS_SENDTO:
  80. er=verify_area(VERIFY_READ, args, 6 * sizeof(unsigned long));
  81. if(er)
  82. return er;
  83. return(sock_sendto(get_fs_long(args+0),
  84. (void *)get_fs_long(args+1),
  85. get_fs_long(args+2),
  86. get_fs_long(args+3),
  87. (struct sockaddr *)get_fs_long(args+4),
  88. get_fs_long(args+5)));
  89. case SYS_RECV:
  90. er=verify_area(VERIFY_READ, args, 4 * sizeof(unsigned long));
  91. if(er)
  92. return er;
  93. return(sock_recv(get_fs_long(args+0),
  94. (void *)get_fs_long(args+1),
  95. get_fs_long(args+2),
  96. get_fs_long(args+3)));
  97. case SYS_RECVFROM:
  98. er=verify_area(VERIFY_READ, args, 6 * sizeof(unsigned long));
  99. if(er)
  100. return er;
  101. return(sock_recvfrom(get_fs_long(args+0),
  102. (void *)get_fs_long(args+1),
  103. get_fs_long(args+2),
  104. get_fs_long(args+3),
  105. (struct sockaddr *)get_fs_long(args+4),
  106. (int *)get_fs_long(args+5)));
  107. case SYS_SHUTDOWN:
  108. er=verify_area(VERIFY_READ, args, 2* sizeof(unsigned long));
  109. if(er)
  110. return er;
  111. return(sock_shutdown(get_fs_long(args+0),
  112. get_fs_long(args+1)));
  113. case SYS_SETSOCKOPT:
  114. er=verify_area(VERIFY_READ, args, 5*sizeof(unsigned long));
  115. if(er)
  116. return er;
  117. return(sock_setsockopt(get_fs_long(args+0),
  118. get_fs_long(args+1),
  119. get_fs_long(args+2),
  120. (char *)get_fs_long(args+3),
  121. get_fs_long(args+4)));
  122. case SYS_GETSOCKOPT:
  123. er=verify_area(VERIFY_READ, args, 5*sizeof(unsigned long));
  124. if(er)
  125. return er;
  126. return(sock_getsockopt(get_fs_long(args+0),
  127. get_fs_long(args+1),
  128. get_fs_long(args+2),
  129. (char *)get_fs_long(args+3),
  130. (int *)get_fs_long(args+4)));
  131. default:
  132. return(-EINVAL);
  133. }
  134. }

上面系统调用的宏定义如下:

  1. #define SYS_SOCKET  1       /* sys_socket(2)        */
  2. #define SYS_BIND    2       /* sys_bind(2)          */
  3. #define SYS_CONNECT 3       /* sys_connect(2)       */
  4. #define SYS_LISTEN  4       /* sys_listen(2)        */
  5. #define SYS_ACCEPT  5       /* sys_accept(2)        */
  6. #define SYS_GETSOCKNAME 6       /* sys_getsockname(2)       */
  7. #define SYS_GETPEERNAME 7       /* sys_getpeername(2)       */
  8. #define SYS_SOCKETPAIR  8       /* sys_socketpair(2)        */
  9. #define SYS_SEND    9       /* sys_send(2)          */
  10. #define SYS_RECV    10      /* sys_recv(2)          */
  11. #define SYS_SENDTO  11      /* sys_sendto(2)        */
  12. #define SYS_RECVFROM    12      /* sys_recvfrom(2)      */
  13. #define SYS_SHUTDOWN    13      /* sys_shutdown(2)      */
  14. #define SYS_SETSOCKOPT  14      /* sys_setsockopt(2)        */
  15. #define SYS_GETSOCKOPT  15      /* sys_getsockopt(2)        */

应用层在一系列操作后就可以通过参数SYS_RECV或SYS_RECVFROM来获取数据包。由于UDP是无连接的,所以如果需要回复,必须使用recvfrom才能得知是谁发送的数据包。当然UDP也可以用recv类函数,只是它不能回复,只能接收。

这里还是以INET中UDP来举例说明。

如果系统调用参数是SYS_RECVFROM,则会进行内存校验后执行函数socket_recvform()函数。

  1. /*
  2. *  Receive a frame from the socket and optionally record the address of the
  3. *  sender. We verify the buffers are writable and if needed move the
  4. *  sender address from kernel to user space.
  5. */
  6. static int sock_recvfrom(int fd, void * buff, int len, unsigned flags,
  7. struct sockaddr *addr, int *addr_len)
  8. {
  9. struct socket *sock;
  10. struct file *file;
  11. char address[MAX_SOCK_ADDR];
  12. int err;
  13. int alen;
  14. if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
  15. return(-EBADF);
  16. if (!(sock = sockfd_lookup(fd, NULL)))
  17. return(-ENOTSOCK);
  18. if(len<0)
  19. return -EINVAL;
  20. if(len==0)
  21. return 0;
  22. err=verify_area(VERIFY_WRITE,buff,len);
  23. if(err)
  24. return err;
  25. //进行相应检查后调用下层函数,INET域则为inet_recvfrom()函数
  26. len=sock->ops->recvfrom(sock, buff, len, (file->f_flags & O_NONBLOCK),
  27. flags, (struct sockaddr *)address, &alen);
  28. if(len<0)
  29. return len;
  30. if(addr!=NULL && (err=move_addr_to_user(address,alen, addr, addr_len))<0)//将发送发地址从内核空间COPY到用户空间
  31. return err;
  32. return len;
  33. }

在inet_recvfrom()函数中会调用具体的协议操作函数。UDP的协议操作函数定义如下:

  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. };

可以看到,其对应的函数对udp_recvfrom()

  1. /*
  2. *  This should be easy, if there is something there we\
  3. *  return it, otherwise we block.
  4. */
  5. int udp_recvfrom(struct sock *sk, unsigned char *to, int len,
  6. int noblock, unsigned flags, struct sockaddr_in *sin,
  7. int *addr_len)
  8. {
  9. int copied = 0;
  10. int truesize;
  11. struct sk_buff *skb;
  12. int er;
  13. /*
  14. *  Check any passed addresses
  15. */
  16. if (addr_len)
  17. *addr_len=sizeof(*sin);
  18. /*
  19. *  From here the generic datagram does a lot of the work. Come
  20. *  the finished NET3, it will do _ALL_ the work!
  21. */
  22. skb=skb_recv_datagram(sk,flags,noblock,&er);
  23. if(skb==NULL)
  24. return er;
  25. truesize = skb->len;
  26. copied = min(len, truesize);
  27. /*
  28. *  FIXME : should use udp header size info value
  29. */
  30. skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);//从sk_buff结构中取出数据部分
  31. sk->stamp=skb->stamp;
  32. /* Copy the address. */
  33. if (sin)
  34. {
  35. sin->sin_family = AF_INET;
  36. sin->sin_port = skb->h.uh->source;
  37. sin->sin_addr.s_addr = skb->daddr;
  38. }
  39. skb_free_datagram(skb);
  40. release_sock(sk);
  41. return(truesize);
  42. }

这样数据就到达了用户空间。

普通文件操作函数接口

最主要的函数就是读写函数:sock_read和sock_write,可以通过文件操作来完成网络数据的读写。谈到文件,就得有文件描述符,文件描述符中的f_inode指针指向文件的存储结点结构。

文件操作集定义如下:

  1. static struct file_operations socket_file_ops = {
  2. sock_lseek,
  3. sock_read,
  4. sock_write,
  5. sock_readdir,
  6. sock_select,
  7. sock_ioctl,
  8. NULL,           /* mmap */
  9. NULL,           /* no special open code... */
  10. sock_close,
  11. NULL,           /* no fsync */
  12. sock_fasync
  13. };

read函数和write函数与recvfrom和send类似,这里列出函数,方便查看。

  1. /*
  2. *  Read data from a socket. ubuf is a user mode pointer. We make sure the user
  3. *  area ubuf...ubuf+size-1 is writable before asking the protocol.
  4. */
  5. static int sock_read(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_read: 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_WRITE,ubuf,size))<0)
  21. return err;
  22. return(sock->ops->read(sock, ubuf, size, (file->f_flags & O_NONBLOCK)));//和recvfrom函数类似,调用INET域相应函数
  23. }

上面会调用inet_read()函数,inet_read()函数会调用udp_read()函数,而udp_read()是通过调用udp_recvfrom()完成功能的。
这两种方式是内核网络栈对用户的接口。

Linux内核--网络栈实现分析(六)--应用层获取数据包(上)的更多相关文章

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

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7545855 更多请查看专栏,地 ...

  2. Linux内核--网络栈实现分析(二)--数据包的传递过程--转

    转载地址http://blog.csdn.net/yming0221/article/details/7492423 作者:闫明 本文分析基于Linux Kernel 1.2.13 注:标题中的”(上 ...

  3. Linux内核--网络栈实现分析(二)--数据包的传递过程(上)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7492423 更多请看专栏,地址 ...

  4. Linux内核--网络栈实现分析(十一)--驱动程序层(下)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7555870 更多请查看专栏,地 ...

  5. Linux内核--网络栈实现分析(一)--网络栈初始化

    本文分析基于内核Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7488828 更多请看专栏, ...

  6. Linux内核--网络栈实现分析(一)--网络栈初始化--转

    转载地址 http://blog.csdn.net/yming0221/article/details/7488828 作者:闫明 本文分析基于内核Linux Kernel 1.2.13 以后的系列博 ...

  7. Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7497260 更多请看专栏,地址 ...

  8. Linux内核--网络栈实现分析(八)--应用层发送数据(下)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7547826 更多请查看专栏,地 ...

  9. Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/7532512 更多请看专栏, ...

随机推荐

  1. DOCTYPE的重要性

    <!DOCTYPE>是文档类型声明: 声明必须是 HTML 文档的第一行,位于 <html> 标签之前.明不是 HTML 标签:它是指示 web 浏览器关于页面使用哪个 HTM ...

  2. JS中变量名作为if条件的 true/flase

    在Javascript中,可以直接将变量名放到if条件中, var a;//甚至不定义 if (a){ //... } 以下情况被认为是flase: 1.''空的字符串 2.数字0 3.对象null ...

  3. curl模拟登录新浪微博

     这几天要做个获取新浪微博@我的信息, 又不用第三方登录,所以只能通过模拟登录来获取信息,研究的一下发现直接模拟登录微博比较困难,验证的算法比较复杂,于是绕道通过登录新浪通行证后来获取cookie 来 ...

  4. 最简单的访问google的办法

    我用的是猎豹浏览器,在工具下面的猎豹应用市场里面,搜索红杏,安装即可. 打开google产品地址时,如果地址栏里面右边的杏是绿色的,代表正常,如果是红色的,代表不正常. 可能是装了其他代理软件,如Sw ...

  5. 激活神器 KMSAuto Net 2015 v1.3.8

    KMSAuto Net – Windows 操作系统 KMS 自动激活工具!支持 Windows Vista,7,8,8.1,10, Server 2008,2008 R2,2012,2012 R2, ...

  6. C# 序列化反序列化

      序列化,就是格式化,是把一个对象以某种格式进行呈现.主要有三种,1.二进制序列化,2.XML序列化,3.JavaScript序列化. 下面讲一下二进制序列化的过程 1.在需要序列化的类的前面,标记 ...

  7. springAOP实现基于注解的数据源动态切换

    需求 代码实现读写数据库分离 武器 spring3.0以上版本 实现思路 1.继承org.springframework.jdbc.datasource.lookup.AbstractRoutingD ...

  8. Python 第五天 递归,计算器(2)

    利用函数编写如下数列: 斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584, ...

  9. 记一次FTP上传文件总是超时的解决过程

    好久没写博,还是重拾记录一下吧. 背景:买了一个阿里云的云虚拟机用来搭建网站(起初不了解云虚拟主机和云服务器的区别,以为都是有SSH功能的,后来发现不是这样样子啊,云虚拟机就是FTP上传网页+MySQ ...

  10. .net core Entity Framework Core Code First 框架 分层开发

    由于之前苦于无法把 Entityframework 跟Web层剥离.找了很久..找到了这个框架..分享给大家..  GitHub 地址:https://github.com/chsakell/dotn ...