2017-07-06


上节主讲了用户层通过netlink和内核交互的详细过程,本节分析下用户层接收数据的过程……

有了之前基础知识的介绍,用户层接收数据只涉及到一个核心调用readmsg(),

其他的就不多介绍了,不太明白的请参考之前的文章,我们还是重点看下内核究竟在背后做了什么!该函数在内核对应于read_msg系统调用

  1. SYSCALL_DEFINE3(recvmsg, int, fd, struct msghdr __user *, msg,
  2. unsigned int, flags)
  3. {
  4. if (flags & MSG_CMSG_COMPAT)
  5. return -EINVAL;
  6. return __sys_recvmsg(fd, msg, flags);
  7. }

没什么特殊的,调用了__sys_recvmsg

  1. long __sys_recvmsg(int fd, struct msghdr __user *msg, unsigned flags)
  2. {
  3. int fput_needed, err;
  4. struct msghdr msg_sys;
  5. struct socket *sock;
  6. /*根据找到对应socket结构*/
  7. sock = sockfd_lookup_light(fd, &err, &fput_needed);
  8. if (!sock)
  9. goto out;
  10.  
  11. err = ___sys_recvmsg(sock, msg, &msg_sys, flags, );
  12.  
  13. fput_light(sock->file, fput_needed);
  14. out:
  15. return err;
  16. }

这里主要分为两部分1、根据传递进来的fd找到内核中的socket数据结构。2、调用___sys_recvmsg执行剩余的工作,前者在发送数据的时候已经进行过介绍,这里就不再赘述。直接看___sys_recvmsg,该函数比较长我们还是分部来看。

  1. if (MSG_CMSG_COMPAT & flags) {
  2. if (get_compat_msghdr(msg_sys, msg_compat))
  3. return -EFAULT;
  4. } else if (copy_from_user(msg_sys, msg, sizeof(struct msghdr)))
  5. return -EFAULT;
  6.  
  7. if (msg_sys->msg_iovlen > UIO_FASTIOV) {
  8. err = -EMSGSIZE;
  9. if (msg_sys->msg_iovlen > UIO_MAXIOV)
  10. goto out;
  11. err = -ENOMEM;
  12. iov = kmalloc(msg_sys->msg_iovlen * sizeof(struct iovec),
  13. GFP_KERNEL);
  14. if (!iov)
  15. goto out;
  16. }

首先自然是获取头部msghdr信息,注意参数中的msghdr指针为用户空间的地址,所以我们需要在内核中构建一个msghdr,把用户空间结构的内容拷贝到内核中,如果flag字段有MSG_CMSG_COMPAT,则直接使用内核指针访问用户空间的msghdr的属性字段。否则使用copy_from_user把整个结构复制过来。前面文章介绍过msghdr并不直接管理数据,而是通过iov向量。这里如果msg_sys->msg_iovlen超过UIO_FASTIOV,这种情况还有回旋的余地,如果没有超过UIO_MAXIOV,则在内核分配多出UIO_FASTIOV的iov向量,因为前面已经在内核栈中分配好了UIO_FASTIOV数量的内核iov,接下来的工作就比较简单,需要把用户空间iov里记录的信息复制到内核空间的iov中,代码如下,

  1. uaddr = (__force void __user *)msg_sys->msg_name;
  2. uaddr_len = COMPAT_NAMELEN(msg);
  3. /*修改iov信息*/
  4. if (MSG_CMSG_COMPAT & flags) {
  5. err = verify_compat_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
  6. } else
  7. err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
  8. if (err < )
  9. goto out_freeiov;
  10. total_len = err;

接下来略过一些控制检查就该处理数据了,

  1. cmsg_ptr = (unsigned long)msg_sys->msg_control;
  2. msg_sys->msg_flags = flags & (MSG_CMSG_CLOEXEC|MSG_CMSG_COMPAT);
  3.  
  4. if (sock->file->f_flags & O_NONBLOCK)
  5. flags |= MSG_DONTWAIT;
  6. err = (nosec ? sock_recvmsg_nosec : sock_recvmsg)(sock, msg_sys,
  7. total_len, flags);

我们分析sock_recvmsg,在快速处理的时候会使用sock_recvmsg_nosec,回想下在发送数据的时候 有个used_address,即如果当前地址和上次发送使用的地址一样,则调用快速处理函数。这里也是同样的道理。在sock_recvmsg中主要调用了__sock_recvmsg继而调用了__sock_recvmsg_nosec,最终调用到sock->ops->recvmsg,针对netlink,这里就是netlink_recvmsg,针对该函数抛开现象看本质的话还是挺清晰的,主要分为两步

1、从指定的sock接收队列中取出一个skb

2、把skb中的数据复制到用户空间的内存中

前者由skb_recv_datagram函数完成,该函数又调用了__skb_recv_datagram,在该函数中涉及到一个MSG_PEEK标志,如果flags字段设置了该标志,则从接收队列中取出skb后不会把skb从队列中删除,所以这样就需要另一个参数off来确定本次需要的是哪个skb.如果没有设置,则得到一个skb后会把skb从队列中删除,这样实际上每次取队列中的首个就可以了,核心代码如下。

  1. struct sk_buff_head *queue = &sk->sk_receive_queue;
  2. int _off = *off;
  3.  
  4. last = (struct sk_buff *)queue;
  5. spin_lock_irqsave(&queue->lock, cpu_flags);
  6. /*遍历循环双链表*/
  7. skb_queue_walk(queue, skb) {
  8. last = skb;
  9. *peeked = skb->peeked;
  10. if (flags & MSG_PEEK) {
  11. if (_off >= skb->len && (skb->len || _off ||
  12. skb->peeked)) {
  13. _off -= skb->len;
  14. continue;
  15. }
  16. skb->peeked = ;
  17. atomic_inc(&skb->users);
  18. } else
  19. __skb_unlink(skb, queue);
  20.  
  21. spin_unlock_irqrestore(&queue->lock, cpu_flags);
  22. *off = _off;
  23. return skb;
  24. }

在获取到skb之后,下面该复制数据了,具体由skb_copy_datagram_iovec函数完成,完成后更新了下msg->msgname和msg_namelen字段。看下skb_copy_datagram_iovec复制函数,该函数也比较清晰,但是涉及到skb的组织方式,又稍显复杂,本节不打算很详细的讲述skb的组织,后面单独开一节来介绍,该函数主要分为三部分:

1、复制头部

2、复制分片

3、复制子skb

该部分内容在详细介绍skb的时候再做介绍,这样复制到iov中后,接收过程就完成了,用户空间就可以正常读取数据了……

以马内利

参考资料

linux3.10.1内核源码

内核通信之Netlink源码分析-用户内核通信原理3的更多相关文章

  1. 内核通信之Netlink源码分析-用户内核通信原理2

    2017-07-05 上文以一个简单的案例描述了通过Netlink进行用户.内核通信的流程,本节针对流程中的各个要点进行深入分析 sock的创建 sock管理结构 sendmsg源码分析  sock的 ...

  2. 内核通信之Netlink源码分析-用户内核通信原理

    2017-07-05 本节从一个小案例入手,结合源码分析下通过netlink进行内核和用户通信的流程. 内核端 按照传统CS模式,其实内核端可以作为是服务器端,用以接收用户的请求并作出处理,但是从ne ...

  3. 内核通信之Netlink源码分析-基础架构

    2017-07-04 netlink是一种基于网络的通信机制,一般用于内核内部或者内核与用户层之间的通信.其有一个明显的特点就是异步性,通信的双方不要求同时在线,也就不用阻塞等待.NetLink按照数 ...

  4. v79.01 鸿蒙内核源码分析(用户态锁篇) | 如何使用快锁Futex(上) | 百篇博客分析OpenHarmony源码

    百篇博客分析|本篇为:(用户态锁篇) | 如何使用快锁Futex(上) 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) ...

  5. jQuery 2.0.3 源码分析Sizzle引擎解析原理

    jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...

  6. wifidog源码分析 - 用户连接过程

    引言 之前的文章已经描述wifidog大概的一个工作流程,这里我们具体说说wifidog是怎么把一个新用户重定向到认证服务器中的,它又是怎么对一个已认证的用户实行放行操作的.我们已经知道wifidog ...

  7. Linux内核2.6.14源码分析-双向循环链表代码分析(巨详细)

    Linux内核源码分析-链表代码分析 分析人:余旭 分析时间:2005年11月17日星期四 11:40:10 AM 雨 温度:10-11度 编号:1-4 类别:准备工作 Email:yuxu97101 ...

  8. SOFA 源码分析 — 自定义线程池原理

    前言 在 SOFA-RPC 的官方介绍里,介绍了自定义线程池,可以为指定服务设置一个独立的业务线程池,和 SOFARPC 自身的业务线程池是隔离的.多个服务可以共用一个独立的线程池. API使用方式如 ...

  9. 【MyBatis源码分析】插件实现原理

    MyBatis插件原理----从<plugins>解析开始 本文分析一下MyBatis的插件实现原理,在此之前,如果对MyBatis插件不是很熟悉的朋友,可参看此文MyBatis7:MyB ...

随机推荐

  1. 动态调用WCF不添加服务(svcutil.exe)

    记录下 首先用svcutil.exe把指定wcf接口的信息下载下来. 生成代理类 比如说接口地址为 http://localhost:6666/Service1.svc 以管理员身份打开cmd 执形 ...

  2. SVN入门 TortoiseSVN 检出

    1. SVN检出(SVN Checkout) 检出项目文件. 新建或者进入目录下(比如qianduan1),右键 --> Svn 检出-->其中版本库URL我可以在SVN服务器获取到,将复 ...

  3. Extjs学习笔记--(四,基本函数介绍)

    Ext是Extjs的命名空间,为Extjs框架提供唯一的全局变量 这样做可以避免冲突,便于代码维护 1,apply和applyif方法 apply=function(object, config, d ...

  4. Android Studio 引入 so 文件

    1.在build.gradle中添加配置 task nativeLibsToJar(type: Zip, description: "create a jar archive of the  ...

  5. hasattr() 、getattr() 、setattr()

    hasattr(object, name) :用于判断一个对象中是否有指定的属性或方法,如果存在返回 True,否则返回 False getattr(object, name, [default]) ...

  6. securecrt 的安装

    http://bbs.feng.com/read-htm-tid-6939481.html ssh  -t  ip地址@用户名  -p 22

  7. Android 使用CheckBox实现多选效果

    CheckBox:复选框1.有两种状态: 选中状态(true),未选中状态(false)2.属性: android:id="@+id/checkbox" android:layou ...

  8. PyQt4文件对话框QFileDialog

    文件对话框允许用户选择文件或文件夹,被选择的文件可进行读或写操作. #!/usr/bin/python # -*- coding: utf-8 -*- import sys from PyQt4 im ...

  9. eclipse导入maven项目时报Could not calculate build plan: Plugin org.apache.maven.plugins:maven-resources

    在用Eclipse IDE for Java EE Developers进行maven项目的开发时,报错Could not calculate build plan: Plugin org.apach ...

  10. Jquery checkbox选中问题

    checkbox中有.checked的写法,判断当前是否是选中状态,不过这种是针对[object HTMLInputElement]这种类型的,而对于[object Object]这种类型是不能使用的 ...