前言

本篇缘起是我要实现类似ss -i 的功能,通过netlink获取系统中的所有socket信息
代码如下

//发送tcpdiag的数据
sendto(netlinkdf, msg,...)
//sleep足够时间,使得内核处理
//因为每次都读取少部分数据,以为内核没来得及处理
sleep(10)char buffer[10000]
len = recvfrom(buffer, sizeof(buffer), 0);

上面流程中,recvfrom 返回的字节大小,小于buffer大小,理应我们认为是收全了的,但是实际情况就是,buffer中的数据,实际上只有非常小的一部分socket信息

recvfrom/recvmsg

首先还是要看下recvmsg 做了哪些事情,注意recvfrom 最后还是调用了recvmsg

SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,unsigned int, flags, struct sockaddr __user *, addr,int __user *, addr_len)
{...msg.msg_control = NULL;msg.msg_controllen = 0;msg.msg_iovlen = 1;msg.msg_iov = &iov;iov.iov_len = size;iov.iov_base = ubuf;msg.msg_name = (struct sockaddr *)&address;msg.msg_namelen = sizeof(address);err = sock_recvmsg(sock, &msg, size, flags);if (err >= 0 && addr != NULL) {err2 = move_addr_to_user(&address,msg.msg_namelen, addr, addr_len);if (err2 < 0)err = err2;}fput_light(sock->file, fput_needed);
out:return err;
}

上篇 讲过,netlink注册的一些函数调用栈是这样的
sock_recvmsg->netlink_recvmsg
核心就是netlink_recvmsg ,核心流程如下

static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
{struct sock *sk = sock->sk;struct netlink_sock *nlk = nlk_sk(sk);//从netlink的socket中,获取接收队列中的数据,这个队列其实就是socket信息//socket信息,首先是当你调用sendto(msg)发送tcp_diag请求的时候//就已经同步的放到了netlink的socket中了skb = skb_recv_datagram(sk, flags, noblock, &err);//copy到用户态err = skb_copy_datagram_iovec(data_skb, 0, msg->msg_iov, copied);//MSG_TRUNC告诉内核recvfrom的返回值大小,是实际skb的大小,而不是因为入参buffer太小从而返回buffer大小//if (flags & MSG_TRUNC)copied = data_skb->len;//释放skbskb_free_datagram(sk, skb);//核心在这里,接着获取剩余的socket信息if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) {ret = netlink_dump(sk);if (ret) {sk->sk_err = ret;sk->sk_error_report(sk);}}
}

上面的流程有几个核心点,首先,对于tcp_diag流程而言,上篇 讲过当你sendto(msg)发送到内核时,实际调用了netlink_dump->tcp_diag_dump函数,来循环获取本机所有socket信息,这个过程是同步的。但是tcp_diag_dump之所以高性能很关键的一点就是因为它本身不会循环获取完所有的socket,这会导致加锁时间太长。所以,下一次获取剩余数据的时机 其实就是当用户调用recvfrom/recvmsg的时候。那么,怎么避免不重复获取socket,靠的是nlk->cb中保存了之前的信息。

所以,当我们执行一次len = recvfrom(buffer, sizeof(buffer), 0); ,如果tcp_diag模块还没循环完成,即nlk->cb还有值,那么虽然len返回的大小小于buffer的大小,但是实际上,本次recvfrom 操作之时,内核因为又跑了一遍netlink_dump->tcp_diag_dump,导致netlink的socket的接受队列里面,实际还是有数据的,而这些数据,并没有通过当前这次recvfrom反馈出来,这导致用户态的代码就很奇葩。

还有一点很关键,如果我们的用户态代码的buffer太小,例如buffer大小是1k,但是skb是3k,由于调用完这次recvfrom,skb就被释放了(没加MSG_PEEK的话),本次buffer是不全的,下次recvfrom 的数据实际上是新一次的dump出来的。从而连netlink格式都是错误的。

 for() {//循环多次,len返回一直有数据,但是大小可能一直小于buffer大小len = recvfrom(buffer, sizeof(buffer), 0);}

这简直是尿频尿不尽。
所以netlink接受数据的流程,其实是很奇葩的。那应用层该怎么办呢?实际上就是循环读,然后判断netlink的done数据包,然后停止读。以 iproute2 为例,rtnl_recvmsg->__rtnl_recvmsg封装了recvmsg函数

static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
{struct iovec *iov = msg->msg_iov;char *buf;int len;iov->iov_base = NULL;iov->iov_len = 0;//获取实际大小len = __rtnl_recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC);if (len < 0)return len;if (len < 32768)len = 32768;//开辟实际大小buf = malloc(len);if (!buf) {fprintf(stderr, "malloc error: not enough buffer\n");return -ENOMEM;}iov->iov_base = buf;iov->iov_len = len;//消费一次skb,skb大小就是lenlen = __rtnl_recvmsg(fd, msg, 0);if (len < 0) {free(buf);return len;}if (answer)*answer = buf;elsefree(buf);return len;
}

而应用层是这么读的

static int rtnl_dump_filter_l(struct rtnl_handle *rth,const struct rtnl_dump_filter_arg *arg)
{struct sockaddr_nl nladdr;struct iovec iov;struct msghdr msg = {.msg_name = &nladdr,.msg_namelen = sizeof(nladdr),.msg_iov = &iov,.msg_iovlen = 1,};char *buf;int dump_intr = 0;while (1) {int status;const struct rtnl_dump_filter_arg *a;int found_done = 0;int msglen = 0;status = rtnl_recvmsg(rth->fd, &msg, &buf);while (NLMSG_OK(h, msglen)) {//不断判断netlink messagede type,为done就breakif (h->nlmsg_type == NLMSG_DONE) {err = rtnl_dump_done(h, a);found_done = 1;break; /* process next filter */}if (h->nlmsg_type == NLMSG_ERROR) {err = rtnl_dump_error(rth, h, a);if (err < 0) {free(buf);return -1;}goto skip_it;}if (!rth->dump_fp) {err = a->filter(h, a->arg1);if (err < 0) {free(buf);return err;}}skip_it:h = NLMSG_NEXT(h, msglen);}//读到了 NLMSG_DONE 就退出if (found_done) {if (dump_intr)fprintf(stderr,"Dump was interrupted and may be inconsistent.\n");return 0;}if (msg.msg_flags & MSG_TRUNC) {fprintf(stderr, "Message truncated\n");continue;}if (msglen) {fprintf(stderr, "!!!Remnant of size %d\n", msglen);exit(1);}}
}

netlink 读取数据流程相关推荐

  1. pytorch dataset读取数据流程_高效 PyTorch :如何消除训练瓶颈

    加入极市专业CV交流群,与 10000+来自港科大.北大.清华.中科院.CMU.腾讯.百度 等名校名企视觉开发者互动交流! 同时提供每月大咖直播分享.真实项目需求对接.干货资讯汇总,行业技术交流.关注 ...

  2. pytorch dataset读取数据流程_10条PyTorch避坑指南

    点击上方"深度学习工坊",选择加"星标" 重磅干货,第一时间送达 本文转载自:机器之心  |  作者:Eugene Khvedchenya 参与:小舟.蛋酱.魔 ...

  3. internetreadfile读取数据长度为0_Go发起HTTP2.0请求流程分析(后篇)——标头压缩

    阅读建议 这是HTTP2.0系列的最后一篇,笔者推荐阅读顺序如下: Go中的HTTP请求之--HTTP1.1请求流程分析 Go发起HTTP2.0请求流程分析(前篇) Go发起HTTP2.0请求流程分析 ...

  4. 一文带你了解HBase读取数据详细流程

    HBase数据读取流程 1.hbase数据读取流程简单描述 ​ 一般来说,在描述hbase读取流程的时候,简单的描述如下: ​ 1).客户端从zookeeper中获取meta表所在的regionser ...

  5. live555从RTSP服务器读取数据到使用接收到的数据流程分析

    本文在linux环境下编译live555工程,并用cgdb调试工具对live555工程中的testProgs目录下的openRTSP的执行过程进行了跟踪分析,直到将从socket端读取视频数据并保存为 ...

  6. cdatabase读取excel第一行数据_“蟒蛇”py对Excel的读取——数据操作用它,老板都得重新认识你...

    在python自动化中,经常会遇到对数据文件的操作,比如添加多名员工,但是直接将员工数据写在python文件中,不但工作量大,要是以后再次遇到类似批量数据操作还会写在python文件中吗? 应对这一问 ...

  7. Java版 QQ空间自动登录无需拷贝cookie一天抓取30WQQ说说数据流程分析【转】

    Java版 QQ空间自动登录无需拷贝cookie一天抓取30WQQ说说数据&流程分析 QQ空间说说抓取难度比较大,花了一个星期才研究清楚! 代码请移步到GitHub GitHub地址:http ...

  8. HBase建表高级属性,hbase应用案例看行键设计,HBase和mapreduce结合,从Hbase中读取数据、分析,写入hdfs,从hdfs中读取数据写入Hbase,协处理器和二级索引

    1. Hbase高级应用 1.1建表高级属性 下面几个shell 命令在hbase操作中可以起到很到的作用,且主要体现在建表的过程中,看下面几个create 属性 1. BLOOMFILTER 默认是 ...

  9. HDFS的工作机制,HDFS写数据流程,HDFS读数据流程(来自学习资料)

    4.hdfs的工作机制 (工作机制的学习主要是为加深对分布式系统的理解,以及增强遇到各种问题时的分析解决能力,形成一定的集群运维能力)   注:很多不是真正理解hadoop技术体系的人会常常觉得HDF ...

最新文章

  1. 2.6 处理数据不匹配问题-深度学习第三课《结构化机器学习项目》-Stanford吴恩达教授
  2. Docker Review - dockerfile 实战_给基础镜像增加功能
  3. 数据中心级交换机考核方法
  4. OpenSSL的SSL/BIO_get_fd
  5. python里的关键字有哪些_Python 中的关键字有哪些?
  6. 2018第一场多校 -补题
  7. 作为产品经理为什么选择开源GIS
  8. 计算机重做系统 d盘,重装系统后c盘变成d盘怎么办|pe装系统c盘变d盘解决方法
  9. html3d建模,数百个 HTML5 例子学习 HT 图形组件 – 3D 建模篇
  10. Blue Coat:打击移动领域的坏人
  11. 笔记本处理器排名_上半年最受欢迎处理器TOP10榜单:AMD终进榜,9代酷睿无缘前10...
  12. 【论文阅读笔记】GPT三部曲
  13. 谁是合约届「技术之王」?
  14. HEVC 高级运动向量预测技术(AMVP)
  15. java实现购物券消费方案
  16. uni-app项目使用多语言切换功能
  17. 简易实用的Web Notification桌面通知
  18. 未来两周目标计划---C++ and Disassembly(不积跬步无以至千里,不积小流无以成江海)...
  19. springboot查看请求ip、ip归属地、设备信息、本地MAC地址、操作系统信息
  20. 域服务器计算机信息丢失,网管的困惑:域控制器哪去了Windows系统 -电脑资料

热门文章

  1. kubeadm 高可用k8s(v1.23.5)
  2. bootstrap modal框的事件监控(modal框打开和关闭触发事件)
  3. php 图片添加超链接,百度帖吧图片加“超链接”引流量的实现方法
  4. Java高级程序员技术积累
  5. 详细解释sprintf 函数(转)
  6. 外汇保证金交易的优势
  7. Oracle 11g使用透明数据加密保护存储数据
  8. 使用腾达(Tenda)路由器部分网址突然打不开,怎么办?
  9. 【FreeMarker】【模板文件FTL】模板总体结构
  10. 170322 PyQt-后台界面设置、WidgetList的使用 汇编5.1