同步阻塞型IO之等待队列
https://blog.csdn.net/liaojunwu/article/details/112060943
进程调度 – Linux内核API prepare_to_wait_exclusive
]]>1 |
|
.
socket(AF_INET, SOCK_DGRAM, 0); -> inet_create
udp_rcv
__udp4_lib_rcv
udp_unicast_rcv_skb
udp_queue_rcv_skb
__udp_enqueue_schedule_skb
__skb_queue_tail
===================udp_queue_rcv_skb=========================
上面介绍了报文接收在软中断的处理流程,下面介绍用户态进程读取报文是如何实现的。用户态的recvmsg()最终会调用raw_recvmsg(),后者再调用skb_recv_datagram。如果套接字接收队列sk->sk_receive_queue中有报文就取skb并返回。否则调用wait_for_packet()等待,直到内核软中断收到报文并唤醒用户态进程。
sys_recvmsg()->sock_recvmsg()->…->sock_common_recvmsg()->raw_recvmsg()
sock_recvmsg
sock_recvmsg_nosec
sock->ops->recvmsg
inet_recvmsg//注册的ipv4 AF_INET
sk->sk_prot->recvmsg
udp_recvmsg//注册的数据包 SOCK_DGRAM
__skb_recv_udp
__skb_try_recv_from_queue//尝试从接收队列接收
__skb_wait_for_more_packets//如果不是非阻塞则 加入等待队列 休眠进程
1 |
|
在群里回答大家的问题的时候经常会出现运行L2FWD失败的情况
其实主要怀疑点:
1。是否把网卡绑定成功了?
2。运行L2FWD是否带了-p参数。。
3。网卡知否支持DPDK
绑卡绑定情况可以使用
dpdk_nic_bind.py –status
来查询
运行参数可以参考sample_app_ug-xxx. pdf
3。支持DPDK网卡
使用
lspic -nn
查看网卡devid
一般
01:00.0 Ethernet controller [0200]: Intel Corporation 82574L Gigabit Network Connection [8086:10d3]
02:00.0 Ethernet controller [0200]: Intel Corporation 82583V Gigabit Network Connection [8086:150c]
然后用这个在dpdk中
去查找一下rte_pci_dev_ids.h 中看有没有其定义(靠名字找);
然后找对应的宏定义的值
最简单粗暴,直接grep 0x150C 全局搜索;
注意:十六进制数是大写
如果觉得好,请下面顶
《OpenStack Nova 高性能虚拟机之 NUMA 架构亲和》
《OpenStack Nova 高性能虚拟机之 CPU 绑定》
《OpenStack Nova 高性能虚拟机之大页内存》
《多进程、多线程与多处理器计算平台的性能问题》
《计算机组成原理 — 存储系统》
《计算机组成原理 — 输入输出系统》
《计算机组成原理 — 总线系统》
《Linux 的零拷贝技术》
《数据包从物理网卡流经 Open vSwitch 进入 OpenStack 云主机的流程》
《Linux 内核网络协议栈》
《用户态网络协议栈还是内核协议栈?》
《网卡适配器收发数据帧流程》
在 x86 体系结构中,接受数据包的传统方式是 CPU 中断方式,即网卡驱动接收到数据包后通过中断通知 CPU 处理,然后由 CPU 拷贝数据并交给内核协议栈。在数据量大时,这种方式会产生大量 CPU 中断,导致 CPU 无法运行其他程序。
内核协议栈的数据转发流程:
内核协议栈的数据转发性能瓶颈有:
故此,对应的优化方案为:
DPDK(Data Plane Development Kit,数据平面开发套件)是一个开源的、快速处理数据平面数据包转发的开发平台及接口。运行于 Intel x86 与 ARM 平台上,最新版本也开始支持 PowerPC。
英特尔在 2010 年启动了对 DPDK 技术的开源化进程,于当年 9 月通过 BSD 开源许可协议正式发布源代码软件包,并于 2014 年 4 月在 www.dpdk.org 上正式成立了独立的开源社区平台,为开发者提供支持。开源社区的参与者们大幅推进了 DPDK 的技术创新和快速演进,而今它已发展成为 SDN 和 NFV 的一项关键技术。
DPDK 提供了一个用户态的高效数据包处理库函数,它通过环境抽象层、内核旁路协议栈、轮询模式的报文无中断收发、优化内存/缓冲区/队列管理、基于网卡多队列和流识别的负载均衡等多项技术,实现了在 x86 处理器架构下的高性能报文转发能力,用户可以在 Linux 用户态开发各类高速转发应用,也适合与各类商业化的数据平面加速解决方案进行集成。简而言之,DPDK 重载了网卡驱动,将数据包的控制平面和数据平面分离,驱动在收到数据包后不再硬中断通知 CPU,而是让数据包通过内核旁路的协议栈绕过了 Linux 内核协议栈,并通过零拷贝技术存入内存,这时应用层的程序就可以通过 DPDK 提供的接口读取数据包。
DPDK 数据包处理流程:
这种处理方式节省了 CPU 中断时间、内存拷贝时间,并向应用层提供了简单易行且高效的数据包处理接口函数,使得网络应用的开发更加方便。但同时,由于需要重载网卡驱动,因此该开发包目前只能用在部分采用 Intel 网络处理芯片的网卡设备中。DPDK 支持的网卡列表:https://core.dpdk.org/supported/,主流使用 Intel 82599(光口)和 Intel x540(电口)。DPDK 可以将数据包处理性能最多提高十倍。在单个英特尔至强处理器上获得超过 80 Mbps 的吞吐量,在双处理器配置中则可将该其提高一倍。
NFV 通过在通用 x86 架构硬件上运行的虚拟化网络功能(VNF),为电信运营商和互联网服务商提供了一种灵活的业务部署手段和高效的组网方案,可以支持固/移网络和 IDC 中 NAT、DPI、EPC、FW 等各类业务功能的灵活部署与弹性扩展。
不同于典型数据中心业务和企业网业务,电信广域网业务要求网元(如 BNG、DPI 等)具有高吞吐、低时延、海量流表支持、用户级 QoS 控制的特点。大量实践表明,通用 x86 服务器作为 NFV 基础设施用于高转发业务时,面临着严重的数据包转发性能瓶颈,需要有针对性地从硬件架构、系统 I/O、操作系统、虚拟化层、组网与流量调度、VNF 功能等层面进行性能优化,才能达到各类 NFV 网络业务的高性能转发要求。
根据 ETSI 的 NFV 参考架构,现实中的 NFV 应用系统一般由 NFV 基础设施(VIM)和 VNF 两类系统服务商提供。因此,相应的 NFV 端到端性能测试,也应划分为底层的 VIM 性能与上层的 VNF 性能两类,以明确各自的性能瓶颈,并避免性能调优工作相互干扰。
在 VIM 层面,由于采用了软件转发和软件交换技术,单个物理服务器内部的数据转发能力是 NFV 的主要性能瓶颈。在各类高速转发的 NFV 应用中,数据报文从网卡中接收,再传送到虚拟化的用户态应用程序处理。整个过程要经历 CPU 中断处理、虚拟化 I/O 与地址映射转换、虚拟交换层、内核协议栈、内核上下文切换、内存拷贝等多个费时的 CPU 操作和 I/O 处理环节。面对这样的性能损耗问题,业内通常采用消除海量中断、内核旁路协议栈、减少内存拷贝、CPU 多核任务分担、Intel VT 等技术来综合提升服务器数据平面的报文处理性能。但由于技术栈复杂,普通用户较难掌握,业界迫切需要一种综合的性能优化方案,同时提供良好的用户开发和商业集成环境。
在 VIM 层的各类性能优化技术方案中,DPDK 加速技术方案成为其中的典型代表。DPDK 以用户数据 I/O 通道优化为基础,结合了 Intel VT 技术、操作系统、虚拟化层与 vSwitch 等多种优化方案,已经形成了完善的性能加速整体架构,并提供了用户态 API 供高速转发类应用访问。
DPDK 技术分为基本技术和优化技术两类。其中,前者指标准的 DPDK 数据平面开发包和 I/O 转发实现技术。
NOTE:说 DPDK 依赖网卡设备不如说 DPDK 依赖的是网卡设备对应的驱动程序。支持 DPDK 的 NIC Kernel Driver 可以转换为 UIO Driver 模式。由此,如有需要,DPDK 实际上是可以在虚拟机上使用的,前提是网卡设备通过 Passthrough 的方式给到虚拟机。所以,该场景中 SR-IOV 网卡会是一个不错的选择。
在最底部的内核态(Linux Kernel),DPDK 拥有两个模块:KNI 与 IGB_UIO。而 DPDK 的上层用户态由很多库组成,主要包括核心部件库(Core Libraries)、平台相关模块(Platform)、网卡轮询模式驱动模块(PMD-Natives&Virtual)、QoS 库、报文转发分类算法(Classify)等几大类,用户应用程序可以使用这些库进行二次开发。下面我们逐一介绍这些组件的功能和作用。
传统的收发数据包方式,首先网卡通过中断方式通知内核协议栈对数据包进行处理,内核协议栈先会对数据包进行合法性进行必要的校验,然后判断数据包目标是否为本机的 Socket,满足条件则会将数据包拷贝一份向上递交到用户态 Socket 来处理。不仅处理路径冗长,还需要从内核到应用层的一次拷贝过程。
为了使得网卡驱动(e.g. PMD Driver)运行在用户态,实现内核旁路。Linux 提供了 UIO(User Space I/O)机制。使用 UIO 可以通过 read() 感知中断,通过 mmap() 实现和网卡设备的通讯。
简单来说,UIO 是用户态的一种 I/O 技术,DPDK 能够绕过内核协议栈,提供了用户态 PMD Driver 的支持,根本上是得益于 UIO 技术。DPDK 架构在 Linux 内核中安装了 IGB_UIO(igb_uio.ko 和 kni.ko.IGB_UIO)模块,以此借助 UIO 技术来截获中断,并重设中断回调行为,从而绕过内核协议栈后续的处理流程。并且 IGB_UIO 会在内核初始化的过程中将网卡硬件寄存器映射到用户态。
UIO 的实现机制是:对用户态暴露文件接口。当注册一个 UIO 设备 uioX 时,就会出现系统文件 /dev/uioX,对该文件的读写就是对网卡设备内存的读写。除此之外,对网卡设备的控制还可以通过 /sys/class/uio 下的各个文件的读写来完成。如下图:
此外,DPDK 还在用户态实现了一套精巧的内存池技术,内核态和用户态之间的的内存交互不进行拷贝,只做控制权转移。这样,当收发数据包时,就减少了内存拷贝的开销。
我们知道,Linux 内核在收包时有两种方式可供选择,一种是中断方式,另外一种是轮询方式。
从哲学的角度来说,中断是外界强加给你的信号,你必须被动应对,而轮询则是你主动地处理事情。前者最大的影响就是打断你当前工作的连续性,而后者则不会,事务的安排自在掌握。
中断对性能的影响有多大?在 x86 体系结构中,一次中断处理需要将 CPU 的状态寄存器保存到堆栈,并运行中断服务程序,最后再将保存的状态寄存器信息从堆栈中恢复。整个过程需要至少 300 个处理器时钟周期。
轮询对性能的提升有多大?网卡收到报文后,可以借助 DDIO(Direct Data I/O)技术直接将报文保存到 CPU 的 Cache 中,或者保存到内存中(没有 DDIO 技术的情况下),并设置报文到达的标志位。应用程序则可以周期性地轮询报文到达的标志位,检测是否有新报文需要处理。整个过程中完全没有中断处理过程,因此应用程序的网络报文处理能力得以极大提升。
故此,想要 CPU 执行始终高效,就必然需要一个内核线程去主动 Poll(轮询)网卡,而这种行为与当前的内核协议栈是不相容的,即便当前内核协议栈可以使用 NAPI 中断+轮询的方式,但依旧没有根本上解决问题。除非再重新实现一套全新的内核协议栈,显然这并不现实,但幸运的是,我们可以在用户态实现这一点。
针对 Intel 网卡,DPDK 实现了基于轮询方式的 PMD(Poll Mode Drivers)网卡驱动。该驱动由用户态的 API 以及 PMD Driver 构成,内核态的 UIO Driver 屏蔽了网卡发出的中断信号,然后由用户态的 PMD Driver 采用主动轮询的方式。除了链路状态通知仍必须采用中断方式以外,均使用无中断方式直接操作网卡设备的接收和发送队列。
PMD Driver 从网卡上接收到数据包后,会直接通过 DMA 方式传输到预分配的内存中,同时更新无锁环形队列中的数据包指针,不断轮询的应用程序很快就能感知收到数据包,并在预分配的内存地址上直接处理数据包,这个过程非常简洁。
PMD 极大提升了网卡 I/O 性能。此外,PMD 还同时支持物理和虚拟两种网络接口,支持 Intel、Cisco、Broadcom、Mellanox、Chelsio 等整个行业生态系统的网卡设备,以及支持基于 KVM、VMware、 Xen 等虚拟化网络接口。PMD 实现了 Intel 1GbE、10GbE 和 40GbE 网卡下基于轮询收发包。
UIO+PMD,前者旁路了内核,后者主动轮询避免了硬中断,DPDK 从而可以在用户态进行收发包的处理。带来了零拷贝(Zero Copy)、无系统调用(System call)的优化。同时,还避免了软中断的异步处理,也减少了上下文切换带来的 Cache Miss。
值得注意的是,运行在 PMD 的 Core 会处于用户态 CPU 100% 的状态,如下图:
由于,网络空闲时 CPU 会长期处于空转状态,带来了电力能耗的问题。所以,DPDK 引入了 Interrupt DPDK(中断 DPDK)模式。Interrupt DPDK 的原理和 NAPI 很像,就是 PMD 在没数据包需要处理时自动进入睡眠,改为中断通知,接收到收包中断信号后,激活主动轮询。这就是所谓的链路状态中断通知。并且 Interrupt DPDK 还可以和其他进程共享一个 CPU Core,但 DPDK 进程仍具有更高的调度优先级。
虽然 PMD 是在用户态实现的驱动程序,但实际上还是会依赖于内核提供的策略。其中 UIO 内核模块,是内核提供的用户态驱动框架,而 igb_uio 是 DPDK 用于与 UIO 交互的内核模块,通过 igb_uio 来 bind 指定的 PCI 网卡设备到 DPDK 使用。
igb_uio 内核模块主要功能之一就是用于注册一个 PCI 设备。实际上这是由 DPDK 提供个一个 Python 脚本 dpdk-devbind 来完成的,当执行 dpdk-devbind 来 bind 网卡时,会通过 sysfs 与内核交互,让内核使用指定的驱动程序来匹配网卡。具体的行为是向文件 /sys/bus/pci/devices/(pci id)/driver_override 写入指定驱动的名称,或者向 /sys/bus/pci/drivers/igb_uio(驱动名称)/new_id 写入要 bind 的网卡设备的 PCI ID。前者是配置设备,让其选择驱动;后者是是配置驱动,让其支持新的 PCI 设备。按照内核的文档 https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci 中提到,这两个动作都会促使驱动程序 bind 新的网卡设备。
dpdk-devbind 具体的步骤如下:
1 |
|
igb_uio 内核模块的另一个主要功能就是让用于态的 PMD 驱动程序得以与 UIO 进行交互:
KNI(Kernel NIC Interface,内核网卡接口),是 DPDK 允许用户态和内核态交换报文的解决方案,模拟了一个虚拟的网口,提供 DPDK 应用程序和 Linux 内核之间通讯没接。即 KNI 接口允许报文从用户态接收后转发到 Linux 内核协议栈中去。
虽然 DPDK 的高速转发性能很出色,但是也有自己的一些缺点,比如没有标准协议栈就是其中之一,当然也可能当时设计时就将没有将协议栈考虑进去,毕竟协议栈需要将报文转发处理,可能会使处理报文的能力大大降低。
上图是 KNI 的 mbuf 的使用流程,也可以看出报文的流向,因为报文在代码中其实就是一个个内存指针。其中 rx_q 右边是用户态,左边是内核态。最后通过调用 netif_rx 将报文送入 Linux 内核协议栈,这其中需要将 DPDK 的 mbuf 转换成标准的 skb_buf 结构体。当 Linux 内核向 KNI 端口发送报文时,调用回调函数 kni_net_tx,然后报文经过转换之后发送到端口上。
核心部件库(Core Libraries)是 DPDK 面向用户态协议栈应用程序员开发的模块。
NOTE:堆,是由开发人员主动分配和释放的存储空间, 若开发人员不释放,则程序结束时由 OS 回收,分配方式类似于链表;与堆不同,栈,是由操作系统自动分配和释放的存储空间 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。
NOTE:DPDK 基于 Linux 内核的无锁环形缓冲 kfifo 实现了一套自己的无锁机制。支持单生产者入列/单消费者出列和多生产者入列/多消费者出列操作,在数据传输的时候,降低性能的同时还能保证数据的同步。
注:
Memory Manager(librte_malloc,堆内存管理器):提供一组 API,用于从 HugePages 内存创建的 memzones 中分配内存。
Ring Manager(librte_ring,环形队列管理器):在一个大小有限的页表中,Ring 数据结构提供了一个无锁的多生产者-多消费者 FIFO API。相较于无锁队列,它有一些的优势,如:更容易实现,适应于大容量操作,而且速度更快。 Ring 在 Memory Pool Manager 中被使用,而且 Ring 还用于不同 CPU Core 之间或是 Processor 上处理单元之间的通信。
Memory Pool Manager(librte_mempool,内存池管理器):内存池管理器负责分配的内存中的 Pool 对象。Pool 由名称唯一标识,并使用一个 Ring 来存储空闲对象。它提供了其他一些可选的服务,例如:每个 CPU Core 的对象缓存和对齐方式帮助,以确保将填充的对象在所有内存通道上得到均匀分布。
Network Packet Buffer Management(librte_mbuf,网络报文缓冲管理):提供了创建和销毁数据报文缓冲区的能力。DPDK 应用程序中可以使用这些缓存区来存储消息以及报文数据。
Timer Manager(librte_timer,定时器管理):为 DPDK 应用程序的执行单元提供了定时服务,为函数异步执行提供支持。定时器可以设置周期调用或只调用一次。DPDK 应用程序可以使用 EAL 提供的接口获取高精度时钟,并且能在每个核上根据需要进行初始化。
平台相关模块(Platform)包括 KNI、POWER(能耗管理)以及 IVSHMEM 接口。
DPDK 优化技术指在 DPDK 应用过程中,为进一步提高各类用户应用程序的转发性能,所采取的性能调优方法和关键配置。
本节主要介绍基于 DPDK 进行应用开发和环境配置时,应用程序性能的影响因素以及相应的优化调整方法。这些因素并非必然劣化性能,可能因硬件能力、OS 版本、各类软硬环境参数配置等的差异产生较大波动,或者存在较大的不稳定性,相关的调优方法需要用户结合自身的 VNF 应用部署在实践中不断完善。
DPDK 具有广泛的平台适应性,可以运行在整个 x86 平台,从主流服务器平台(从高性能或者高能效产品系列),到桌面或者嵌入式平台,也可以运行于基于 Power 或者其他架构的运算平台。下图展示了一个通用双路服务器的内部架构,它包含了 2 个中央处理器,2 个分离的内存控制单元来连接系统内存,芯片组会扩展出大量高速的 PCIe 2.0/3.0 接口,用于连接外设,如 10Gbps 或者 25Gbps 网卡外设。
硬件规格对 DPDK 性能的影响体现在几个方面:
不同的 Linux OS 发行版使用的 Linux 内核版本不一样,配置的 Linux OS 服务也不一样。这些差异都会导致应用程序在网络报文处理能力上有所差别。由于 Linux 内核还在不断演进,Linux 的发行版也数量众多,同样的硬件配置环境下,不同的 Linux 发行版在小包的处理能力上存在差异。本文无法提供最佳 Linux 内核版本和配置,而只能给出部分参考建议,如:关闭部分 OS 服务。
OVS 作为 NFV 的一个重要组成模块,会运行在绝大多数的服务器节点上,提供虚拟机和虚拟机之间,以及虚拟网络和物理网络间的互连接口,其性能至关重要。OVS 2.4 开始正式支持 DPDK 加速,相比传统基于 Linux 内核的 OVS 版本,转发性能提高了数倍,为 VNF 在通用 x86 服务器上部署提供了有力支持。
OVS 缺省会为每一个 NUMA 节点创建一个 pmd 线程,该 pmd 线程以轮询方式处理属于其 NUMA 节点上的所有 DPDK 接口。为了高性能,需要利用前面提到的 CPU 绑核技术,把 pmd 线程绑定在一个固定的 CPU core 上处理。此外,为了增加扩展性,OVS 2.4 也支持网卡多队列以及多 pmd 线程数,这些参数均可动态配置,但具体配置应根据具体需求来决定。
如前所述,DPDK 考虑了 NUMA 以及多内存通道的访问效率,会在系统运行前要求配置 Linux 的 HugePage,初始化时申请其内存池,用于 DPDK 运行的主要内存资源。Linux 大页机制利用了处理器核上的的 TLB 的 HugePage 表项,这可以减少内存地址转换的开销。
如果想建立一个基于消息传递的核间通信机制,可以使用 DPDK Ring API,它是一个无锁的 ring 实现。该 Ring 机制支持批量和突发访问,即使同时读取几个对象,也只需要一个昂贵的原子操作,批量访问可以极大地改善性能。
DPDK 支持 CPU 微架构级别的优化,可以通过修改 DPDK 配置文件中的 CONFIG_RTE_MACHINE 参数来定义。优化的程度根据随编译器的能力而不同,通常建议采用最新的编译器进行编译。如果编译器的版本不支持该款 CPU 的特性,比如 Intel AVX 指令,那么它在编译时只会选用自己支持的指令集,这可能导致编译后生成的 DPDK 应用的性能下降。
1 |
|
每个人都有很大的潜力,但很多都没有发挥出来的平台。举个例子 其实像少爷这样的肯定也是有不少技术的,但打工这种模式并不适合把技术都拿出来,所以少爷转变为更合适打工的方式。就是能不多事就不多事,只要低调稳定才是打工的最明智选择。所以普通的打工模式不适合创业的。另外经过我自己尝试,证明单枪匹马确实不容易成事。
要不把当前公司的前景和估值量化下,我们最好是合作方式,我看我能投入什么和怎么合作。做为前期能合作在一起,那肯定是要都要有力的吧。而且一旦我们真的是合作了,我也希望我们是绝对的信任和开放的。就是真正的利益共同体,就不再为利益问题束缚。
]]>valgrind
Linux下几款C++程序中的内存泄露检查工具
]]>route add 119.29.29.29 MASK 255.255.255.255 192.168.100.1
1,首先在“运行”窗口输入cmd(按WIN+R打开运行窗口),然后回车进入命令行。
2,在命令行下输入route命令,会有对应的提示信息。
ROUTE [-f] [-p] [-4|-6] command [destination] [MASK netmask] [gateway] [METRIC metric] [IF interface]
-f 清除所有网关项的路由表。如果与某个
命令结合使用,在运行该命令前,
应清除路由表。
-p 与 ADD 命令结合使用时,将路由设置为
在系统引导期间保持不变。默认情况下,重新启动系统时,
不保存路由。忽略所有其他命令,
这始终会影响相应的永久路由。Windows 95
不支持此选项。
-4 强制使用 IPv4。
-6 强制使用 IPv6。
command 其中之一:
PRINT 打印路由
ADD 添加路由
DELETE 删除路由
CHANGE 修改现有路由
destination 指定目的主机或者目的网段的网络地址。
MASK 指定下一个参数为“网络掩码”值。
netmask 指定此路由项的子网掩码值(目的主机或者目的网段的子网掩码)。
如果未指定,其默认设置为 255.255.255.255。
gateway 指定网关(下一跳)。
interface 指定路由的接口号码。
METRIC 指定跃点数,例如目标的成本。
实例:
1,查看所有的路由表信息
route print
2,添加一条路由条目
route add 157.0.0.0 MASK 255.0.0.0 157.55.80.1
route add 157.0.0.0 MASK 255.0.0.0 157.55.80.1 METRIC 3
route add 157.0.0.0 MASK 255.0.0.0 157.55.80.1 METRIC 3 IF 2
3,添加一条永久路由条目(-p 表示永久路由,重启后不丢失)
route -p add 157.0.0.0 MASK 255.0.0.0 157.55.80.1
4,删除路由条目
route delete 157.0.0.0
5,修改路由条目(CHANGE 只用于修改网关和/或跃点数)
route CHANGE 157.0.0.0 MASK 255.0.0.0 157.55.80.5 METRIC 2 IF 2
————————————————
版权声明:本文为CSDN博主「老吉来了」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/javazhouchon/article/details/119423181
Linux中增加软路由的两种方法
第一种:
route add -net 172.16.6.0 netmask 255.255.255.0 gw 172.16.2.254 dev eth0
/* 增加一条网络172.16.6.0/24 经过172.16.2.254 eth0 */
/* -net增加网络 -host增加主机 netmask 子网掩码 gw 网关 dev 装置,设备,这里是你的网卡名*/
route del gw 172.16.2.254 /* 删除默认网关172.16.2.254 */
route del -net 172.16.86.0/24 /* 删除默认网络172.16.86.0 */
route /* 显示当前路由表 */
常用的是这种方式,但有时你在删除或一条软路由时会不起作用,会有什么提示:
SIOCADDRT: 無法接觸網路
所以这时用如下的这种方法就可以了
第二种:
实现的功能和上面的一样
ip route add 172.16.6.0/24 via 172.16.2.254 dev eth0
ip route del gw 172.16.2.254
ip route del 172.16.6.0/24 dev eth0
ip route
删除的方法:
添加路由:
route add -net 10.0.0.0 netmask 255.0.0.0 dev eth0
删除路由:
route del -net 10.0.0.0 netmask 255.0.0.0 dev eth0
添加默认路由:
route add default gw 10.0.0.1
删除默认路由:
route del default gw 10.0.0.1
或者
route del default
-————————————————————————————–
linux下添加路由的方法:
一:使用 route 命令添加
使用route 命令添加的路由,机器重启或者网卡重启后路由就失效了,方法:
//添加到主机的路由
# route add –host 192.168.168.110 dev eth0
# route add –host 192.168.168.119 gw 192.168.168.1
//添加到网络的路由
# route add –net IP netmask MASK eth0
# route add –net IP netmask MASK gw IP
# route add –net IP/24 eth1
//添加默认网关
# route add default gw IP
//删除路由
# route del –host 192.168.168.110 dev eth0
二:在linux下设置永久路由的方法:
1.在/etc/rc.local里添加
方法:
route add -net 192.168.3.0/24 dev eth0
route add -net 192.168.2.0/24 gw 192.168.3.254
2.在/etc/sysconfig/network里添加到末尾
方法:GATEWAY=gw-ip 或者 GATEWAY=gw-dev
3./etc/sysconfig/static-router :
any net x.x.x.x/24 gw y.y.y.y
]]>原来内核协议栈的方式数据是从 网卡–>驱动–>协议栈–>socket接口–>业务
而DPDK的方式是基于UIO(Userspace I/O)旁路数据。数据从 网卡–>DPDK轮询模式–>DPDK基础库–>业务
为了让驱动运行在用户态,linux提供UIO机制,使用UIO可以通过read感知中断,通过mmap实现核网卡的通讯
要开发用户态驱动有几个步骤:
DPDK的UIO驱动屏蔽了硬件发出的中断,然后在用户态采用主动轮训的方式,这种模式被称为PMD(Poll Mode Driver)
UIO 旁路了内核,主动轮训去掉硬中断,DPDK从而可以在用户态做收发包处理。带来zero copy,无系统调用的好处,同步处理减少上下文切换带来的Cache MIss。
运行在PMD的CORE会处于CPU100%的状态。
网络空闲时CPU 长期空转,会带来能耗问题。所以,DPDK推出interrupt DPDK模式。
interrupt DPDK:
1. 采用HugePage减少TLB Miss
默认下Linux采用4KB为一页,页越小内存越大,页表的开销越大,页表的内存占用也越大。CPU有TLB(Translation Lookaside Buffer)成本高所以一般就只能存放几百到上千个页表项。如果进程要使用64G内存,则64G/4KB=16000000(一千六百万)页,每页在页表项中占用16000000 * 4B=62MB。如果用HugePage采用2MB作为一页,只需64G/2MB=2000,数量不在同个级别。
而DPDK采用HugePage,在x86-64下支持2MB、1GB的页大小,几何级的降低了页表项的大小,从而减少TLB-Miss。并提供了内存池(Mempool)、MBuf、无锁环(Ring)、Bitmap等基础库。根据我们的实践,在数据平面(Data Plane)频繁的内存分配释放,必须使用内存池,不能直接使用rte_malloc,DPDK的内存分配实现非常简陋,不如ptmalloc。
2. SNA(Shared-nothing Architecture)
软件架构去中心化,尽量避免全局共享,带来全局竞争,失去横向扩展的能力。NUMA体系下不跨Node远程使用内存。
3. SIMD(Single Instruction Multiple Data)
从最早的mmx/sse到最新的avx2,SIMD的能力一直在增强。DPDK采用批量同时处理多个包,再用向量编程,一个周期内对所有包进行处理。比如,memcpy就使用SIMD来提高速度。
SIMD在游戏后台比较常见,但是其他业务如果有类似批量处理的场景,要提高性能,也可看看能否满足。
4. 不使用慢速API
这里需要重新定义一下慢速API,比如说gettimeofday,虽然在64位下通过vDSO已经不需要陷入内核态,只是一个纯内存访问,每秒也能达到几千万的级别。但是,不要忘记了我们在10GE下,每秒的处理能力就要达到几千万。所以即使是gettimeofday也属于慢速API。DPDK提供Cycles接口,例如rte_get_tsc_cycles接口,基于HPET或TSC实现。
dpdk和linux内核中断区别,DPDK解析—–DPDK,PF_RING对比
https://blog.csdn.net/weixin_28865923/article/details/116609012
2.2 DPDK UIO机制
采用Linux提供UIO机制,可以旁路Kernel,将所有报文处理的工作在用户空间完成。
图片引自参考文档[1]
如上图,左边是传统的数据包获取方式,路径为:
网卡 -> Kernel驱动 -> Kernel TCP/IP协议栈 -> Socket接口 -> 业务
右边是DPDK的方式,基于UIO。数据路径为:
网卡 -> DPDK轮询模式-> DPDK基础库 -> 业务
2.3 DPDK 零拷贝(Zero Copy)
通过UIO+mmap 实现数据零拷贝(zero copy)是DPDK的特点之一。那么常规的Linux Kernel(libpcap)以及pf-ring处理数据包的流程要进行几次数据拷贝呢?
libpcap
libpcap采用的是传统的数据包获取方式,如上图左边路径。
网卡接收到数据包后,第一步需要把数据从网卡拷贝到主存(RX buffer)中。但是在这个拷贝的过程中使用了DMA(Direct Memory Access)技术。DMA 传输将数据从一个地址空间复制到另外一个地址空间。由CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成,CPU是不需要参与,也不会产生CPU资源消耗。因此这个第一步的拷贝可以忽略。
第二步,Kernel会将网卡通过DMA传输进入主存(RX buffer)的内容拷贝一份进行处理,这里进行了一次数据拷贝。
第三步,由于内核空间和用户空间的内存是不共享的,Kernel会将数据包内容再进行一次拷贝用于用户空间使用。
综上,传统的数据包捕获方式(libpcap)会进行两次数据包拷贝。
pf-rfing
pf-ring采用mmap()将传统的两次拷贝减少至一次拷贝。
第一步,同样使用DMA技术,把数据从网卡拷贝到主存(RX buffer)中。
第二步,pf-ring会将网卡通过DMA传输进入主存(RX buffer)的内容拷贝一份放入环形缓冲区中(ring)中,这里进行了一次数据拷贝。
第三步,使用mmap()将环形缓冲区映射至用户空间,用户空间可以直接访问这个环形缓冲区中的数据。
pf-ring zc
pf-ring zc(zero copy)更是将pf-ring的一次拷贝也省去,达到了零拷贝的目的,具体的:
第一步,使用DMA技术,把数据从网卡拷贝到主存(RX buffer)中。
第二步,使用mmap()直接将RX buffer的数据映射到用户用户空间,使用户空间可以直接访问RX buffer的数据。
下图非常清晰地展示了pf-ring zc实现零拷贝的过程:
图片引自参考文档[3]
DPDK
DPDK实现零拷贝的方式与pf-ring zc类似,首先通过DMA将网卡数据拷贝至主存(RX buffer);随后使用mmap()直接将RX buffer的数据映射到用户用户空间,使用户空间可以直接访问RX buffer的数据,以此实现了零拷贝。
由上述可以看出,pf-ring zc和dpdk均可以实现数据包的零拷贝,两者均旁路了内核,但是实现原理略有不同。pf-ring zc通过zc驱动(也在应用层)接管数据包,dpdk基于UIO实现。
值得注意的是,无论是pf-ring zc还是dpdk,旁路内核就意味着应用程序完全接管网卡,这样一来,当通过UIO驱动或ZC驱动打开设备时,设备将无法用于标准网络。
3.PMD
DPDK的UIO驱动屏蔽了硬件发出中断,然后在用户态采用主动轮询的方式,这种模式被称为PMD(Poll Mode Driver)。
正常情况下,网卡收到数据包后,会发出中断信号,CPU接收到中断信号后,会停止正在运行的程序,保护断点地址和处理机当前状态,转入相应的中断服务程序并执行中断服务程序,完成后,继续执行中断前的程序。
DPDK采用轮询的方式,直接访问RX和TX描述符而没有任何中断,以便在用户的应用程序中快速接收,处理和传送数据包。这样可以减少CPU频繁中断,切换上下文带来的消耗。
在网卡大流量时,DPDK这种PMD的方式会带来较大的性能提升;但是在流量小的时候,轮询会导致CPU空转(不断轮询),从而导致CPU性能浪费和功耗问题。
为此,DPDK还推出了Interrupt DPDK模式,它的原理和Linux的NAPI很像,就是没包可处理时进入睡眠,改为中断通知。并且可以和其他进程共享同个CPU Core,但是DPDK进程会有更高调度优先级。
与DPDK相比,pf-ring(no zc)使用的是NAPI polling和应用层polling,而pf-ring zc与DPDK类似,仅使用应用层polling。
]]>创建sock和关联协议,之后recv 和send都用的这个sock。
1 |
|
说明:socket的创建初始化了以下模块
例:收包inet_recvmsg 发包inet_sendmsg
1 |
|
传输层到网络层发送图
邻居层到物理层
UDP之数据报发送过程 https://blog.csdn.net/wangquan1992/article/details/109164638
1 |
|
1 |
|
「干货」25 张图一万字,拆解 Linux 网络包发送过程(下)
https://baijiahao.baidu.com/s?id=1707608648230579710&wfr=spider&for=pc
1 |
|
while 循环不断地从队列中取出 skb 并进行发送。注意,这个时候其实都占用的是用户进程的系统态时间(sy)。只有当 quota 用尽或者其它进程需要 CPU 的时候才触发软中断进行发送。
该函数会进入到 __netif_reschedule,由它来实际发出 NET_TX_SOFTIRQ 类型软中断。是通过软中断
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
软中断的实现难机制很多,比如bottom half(下半部)、task queue,不过这两者已经被废弃,下面三种实现机制是现在kernel还支持的:
软中断(softirq):内核2.3引入,是最基本、最优先的软中断处理形式,为了避免名字冲突,本文中将这种子类型的软中断叫softirq。
tasklet:其底层使用softirq机制实现,提供了一种用户方便使用的软中方式,为软中断提供了很好的扩展性。
work queue:前两种软中断执行时是禁止抢占的(softirq的ksoftirq除外),对于用户进程不友好。如果在softirq执行时间过长,会继续推后到work queue中执行,work queue执行处于进程上下文,其可被抢占,也可以被调度,如果软中断需要执行睡眠、阻塞,直接选择work queue。
Linux的中断处理机制 : https://zhuanlan.zhihu.com/p/80371745
Linux内核浅析-软中断 :https://zhuanlan.zhihu.com/p/81742153
linux 中断子系统-softirq 的实现原理: https://zhuanlan.zhihu.com/p/363225717
s3c2440 linux中断过程:https://blog.csdn.net/oqqYuJi12345678/article/details/99726927
软中断的触发时机
1、irq_exit:在硬中断退出时,会检查local_softirq_pending和preemt_count,如果都符合条件,则执行软中断。
1 |
|
2、local_bh_enable:使用此函数开启软中断时,会检查local_softirq_pending,如果都符合条件,则执行软中断。调用链为local_bh_enable()->__local_bh_enable()->do_softirq()。
3、raise_softirq:主动唤起一个软中断,会首先设置__softirq_pending对应的软中断位为挂起,然后检查in_interrupt,如果不在中断中,则唤起ksoftirq线程执行软中断(ksoftirq是softirq的一种执行机制,在软中的运行流程中会提到)。
软中断线程是在系统初始化时创建的,cpu几个核心就创建几个线程。
1 |
|
socks5协议是一款广泛使用的代理协议,它在使用TCP/IP协议通讯的前端机器和服务器机器之间扮演一个中介角色,使得内部网中的前端机器变得能够访问Internet网中的服务器,或者使通讯更加安全。SOCKS5 服务器通过将前端发来的请求转发给真正的目标服务器, 模拟了一个前端的行为。在这里,前端和SOCKS5之间也是通过TCP/IP协议进行通讯,前端将原本要发送给真正服务器的请求发送给SOCKS5服务器,然后SOCKS5服务器将请求转发给真正的服务器。
代理的工作流程图为:
第一步,客户端向代理服务器发送代理请求,其中包含了代理的版本和认证方式:
1 | +----+----------+----------+ |
如果是socks5代理,第一个字段VER
的值是0x05
,表明是socks代理的第5个版本。
第二个字段NMETHODS
表示支持的认证方式,第三个字段是一个数组,包含了支持的认证方式列表:
0x00
: 不需要认证0x01
: GSSAPI认证0x02
: 用户名和密码方式认证0x03
: IANA认证0x80-0xfe
: 保留的认证方式0xff
: 不支持任何认证方式服务端收到客户端的代理请求后,选择双方都支持的加密方式回复给客户端:
1 | +----+--------+ |
此时客户端收到服务端的响应请求后,双方握手完成,开始进行协议交互。
数据包分析
客户端开启socks代理,使用浏览器访问网页。抓包软件抓到客户端的认证请求:
服务端的认证响应请求:
握手完成后,客户端要把需要执行的操作指令发给客户端,表明自己要执行代理的请求。请求帧格式:
1 | +----+-----+-------+------+----------+----------+ |
各字段含义:
VER
: 代理版本信息
CMD1
2
3
4
5
6
7
8
9
10
11
: 代理指令
- `0x01`: connect指令,tcp代理时使用。
- `0x02`: bind,很少使用,类似FTP协议中主动连接场景,服务端后服务端会主动连接到客户端。
- `0x03`: udp代理时使用。
- `RSV`: 保留字段
- ```
ATYP
: 地址类型- `0x01`: IPv4地址类型- `0x03`: unix域socket类型代理- `0x04`: IPv6地址类型
DST.ADDR
: 需要连接的目的地址
DST.PORT
: 需要连接的目的端口
数据包分析
以下是一个去往console.cloud.tencent.com:443
的socks5代理请求:
客户端发完上面的请求连接后,服务端会发起连接到DST.ADDR:DST.PORT
,然后返回响应到客户端,响应格式:
1 | +----+-----+-------+------+----------+----------+ |
其中VER/RSV/ATYP
的含义和上面相同,其他字段的意思:
REP
: 请求响应
0x00
: 成功0x01-0x08
: 失败0x09-0xff
: 未使用BND.ADDR
: 连接到的远程地址
BND.PORT
: 连接到的远程端口
数据包分析
以下是上面代理请求的响应数据包:
当连接建立后,客户端就可以和正常一样访问服务端通信了,此时通信的数据除了目的地址是发往代理程序以外,所有内容都是和普通连接一模一样。对代理程序而言,后面所有收到的来自客户端的数据都会原样转发到服务读端。
例如代理的HTTPS请求连接,实际上发送的数据和普通HTTPS交互过程一模一样:
中间的
Socks Protocol
栏是wireshark根据连接上下文自动解析出来的,实际上数据包中并没有这一栏。
socks5通信的交互流程:
前向纠错(FEC)算法被广泛使用在实时音视频领域提升音视频的弱网抗性,只要收到FEC分组内的冗余包和一定数量的数据包,就能根据FEC算法恢复出对应的冗余包。常见的FEC实现包括M+1系列的异或算法、M+N系列的RS矩阵算法,这2种实现算法各有优缺点。
异或算法实现相对简单,将M个数据包逐字节进行异或计算,计算得到的结果即为冗余包。这种算法只需要进行异常运算,复杂度低。但是抗丢包能力弱,例如 4+1算法,5个包里面最多只能丢1个包,否则就无法恢复。
在实时音视频领域主要采用M+N系列的CRS矩阵算法。
CRS矩阵算法,CRS 即 Cauchy Reed-Solomon coding
,基于柯西矩阵的RS编码。RS编码只是一种应对擦除的编码,也就是应对数据块丢失的场景,并且我们是知道哪一块丢失的,然后我们可以通过冗余信息把丢失的数据块找回来。CRS指的是Cauthy RS,其中用到了柯西矩阵。计算过程相对复杂,一般需要在GF(2^8)伽罗瓦域上进行运算。具体计算过程,如下所示。
假设我们有个M个数据包,想要得到N个冗余包,一次异或计算只能得到一个冗余包,无法实现。如果直接用编码矩阵(M+N)*矩阵M,就可以得到一个M+N行的编码输出矩阵,其中L为原始数据包的长度。
编码输出矩阵的M行为原始数据,因此编码矩阵的前M行必须是一个M*M的单位阵,后N行要满足在编码矩阵M+N行中随便选择M行组成的子矩阵都是可逆的,柯西矩阵以算法复杂度较低被选中。矩阵的运算都是逐字节进行的,而单个字节能够表示的范围为0~256,如果直接进行矩阵运算得到的结果肯定会超过单个字节的上限,因此要把矩阵运算放到GF(2^8)伽罗瓦域上进行计算。
数据在网络中传输发生丢包,只要丢失的包个数小于N,就可以通过CRS恢复原始数据。例如,采用FEC 4+2,丢失2个数据包的恢复过程。
接收端收到数据包后,检测到编码输出数据包中前M个包中有数据包丢失,只要丢失的包不超过冗余包个数N,就可以从收到的数据包和冗余包里选出M个数据包,当然这M个数据包对应编码矩阵中的M行,只要从编码矩阵中选出来的M行组成的可逆,就可以计算出丢失的数据。
数据恢复的关键步骤在于矩阵求逆,通常采用LU分解算法,时间消耗达到N^3 。
FEC算法在伽罗华域的计算方式,可以进一步优化。在运算上主要有以下方面。
之前实现过一个版本的FEC算法,为了减少运算量,采用以时间换空间的算法将生成矩阵与逆矩阵提前计算好。在进行数据恢复时,根据M和N的组合方式找到对应子矩阵,再根据子矩阵找到对应的逆矩阵。缺点也很明显,只支持特定几种类型的FEC算法,抗连续丢包能力弱。
]]>//底精度
time_t tv_sec; // seconds
typedef struct timespec timespec_t;
struct timespec {
time_t tv_sec; // seconds time_t就是long int 类型
long tv_nsec; // and nanoseconds
};
//高精度
struct timeval us; //gettimeofday(&us,NULL);
timespec time; //
struct tm now_time; //
//时间获取
time_t time(NULL); // 等同于now = time(NULL) //获取相对于1970到现在的秒数
stat(filename->d_name, &file_stat); //获取相对于1970到现在的秒数
clock_gettime(CLOCK_REALTIME, &time); //获取相对于1970到现在的秒数
//以下为time_t转换为本地时间
time_t -> tm
localtime_r(&file_stat.st_atime, &now_time);
timespec -> tm
localtime_r(&time.tv_sec, &nowtime);
time_t -> tm
struct tm *tblock = localtime(&timer);
tm -> string
方法1
struct tm *nowtime = localtime(&timer);
sprintf(date_time, “%d-%d-%d %d:%d:%d\n”, now_time->tm_year + 1900, now_time->tm_mon + 1,
now_time->tm_mday, now_time->tm_hour, now_time->tm_min, now_time->tm_sec);
方法2
struct tm now_time; //
localtime_r(&time.tv_sec, &nowtime);
strftime(date_time, sizeof(date_time), “%Y-%m-%d %H:%M:%S”, localtime_r(&file_stat.st_mtime, &t));
方法3struct tm now_time; //localtime_r(&file_stat.st_atime, &now_time);sprintf(date_time, "%d-%d-%d %d:%d:%d\n", now_time.tm_year + 1900, now_time.tm_mon + 1, now_time.tm_mday, now_time.tm_hour, now_time.tm_min, now_time.tm_sec);
timeval -> string
printf(“gettimeofday: tv_sec=%ld, tv_usec=%ld\n”, us.tv_sec, us.tv_usec);
time_t -> string
高精度
gettimeofday
struct timeval tv;
struct timezone tz;
gettimeofday (&tv , &tz);
printf(“tv_sec; %d\n”, tv,.tv_sec);
printf(“tv_usec; %d\n”,tv.tv_usec);
printf(“tz_minuteswest; %d\n”, tz.tz_minuteswest);
printf(“tz_dsttime, %d\n”,tz.tz_dsttime);
sshfs -C root@192.168.180.40:/home/www /Users/dnsnat/Documents/kyland
sudo diskutil unmount force /Users/dnsnat/Documents/kyland
brew install node
npm get registry #查看原本镜像
npm config set registry http://registry.npm.taobao.org/ #修改成淘宝镜像
npm config set registry https://registry.npmjs.org/ #镜像还原
pip3 install –user –upgrade pynvim
]]>这里重点要分清硬中断和软中断区别。上半部分下半部分就是说的:
旧版本可以参考道系统方式,新版本netif_rx已集成NAPI技术了。每个数据包到来,都会产生硬件中断,中断处理程序netif_rx->enqueue_to_backlog将收到的包放入当前cpu的收包队列softnet_data->input_pkt_queue中,并且通过enqueue_to_backlog->____napi_schedule将非napi设备对应的虚拟设备napi结构softnet->backlog结构挂在当前cpu的待收包设备链表softnet_data->poll_list中,并触发软中断,软中断处理过程中,所以会调用默认初始化backlog的回调处理函数process_backlog,如果NAPI则是自己注册的。将收包队列input_pkt_queue合并到softdata->process_queue后面,并依次处理该队列中的数据包
数据包到来,第一个数据包产生硬件中断,中断处理程序将设备的napi_struct结构挂在当前cpu的待收包设备链表softnet_data->poll_list中,并触发软中断,软中断执行过程中,遍历softnet_data->poll_list中的所有设备,依次调用其收包函数napi_sturct->poll,处理收包过程;
中断上半部
e100_intr(中断处理程序)–>__napi_schedule–>____napi_schedule(将设备对应的napi结构加入到当前cpu的待收包处理队列softnet_data->poll_list中,并触发软中断)
数据包到来,第一包产生中断,中断处理程序得到执行,其中关键步骤为调用__napi_schedule(&nic->napi)将设备对应的napi加入到当前cpu的softnet_data->poll_list中;
中断下半部
net_rx_action(软中断收包处理程序)–>napi_poll(执行设备包处理回调napi_struct->poll)
但是 NAPI 存在一些比较严重的缺陷:
对于上层的应用程序而言,系统不能在每个数据包接收到的时候都可以及时地去处理它,而且随
着传输速度增加,累计的数据包将会耗费大量的内存,经过实验表明在 Linux 平台上这个问题会比
在 FreeBSD 上要严重一些;
另外一个问题是对于大的数据包处理比较困难,原因是大的数据包传送到网络层上的时候耗费的
时间比短数据包长很多(即使是采用 DMA 方式),所以正如前面所说的那样,NAPI 技术适用于对
高速率的短长度数据包的处理。
使用 NAPI 先决条件:
驱动可以继续使用老的 2.4 内核的网络驱动程序接口,NAPI 的加入并不会导致向前兼容性的丧失,但是 NAPI 的使用至少要得到下面的保证:
E1000网卡驱动程序对NAPI的支持:上面已经介绍过了,使用NAPI需要在编译内核的时候选择打开相应网卡设备的NAPI支持选项,对于E1000网卡来说就是CONFIG_E1000_NAPI宏。E1000网卡的初始化函数,也就是通常所说的probe方法,定义为e1000_probe():
1 | static int __devinit e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
在分析网卡接收数据包的过程中,设备的open方法是值得注意的,因为在这里对网卡设备的各种数据
结构进行了初始化,特别是环形缓冲区队列。E1000网卡驱动程序的open方法注册为e1000_open():
1 | static int e1000_open(struct net_device *netdev) |
事实上e1000_open() 函数调用了e1000_setup_rx_resources()函数为其环形缓冲区分配资源。
e1000设备的接收方式是一种缓冲方式,能显著的降低 CPU接收数据造成的花费,接收数据之前,软件需要预
先分配一个 DMA 缓冲区,一般对于传输而言,缓冲区最大为 8Kbyte 并且把物理地址链接在描述符的 DMA
地址描述单元,另外还有两个双字的单元表示对应的 DMA 缓冲区的接收状态。
在 /driver/net/e1000/e1000/e1000.h 中对于环形缓冲队列描述符的数据单元如下表示:
1 | struct e1000_desc_ring { |
1 | static int e1000_setup_rx_resources(struct e1000_adapter *adapter) |
在e1000_up()函数中,调用request_irq()向系统申请irq中断号,然后将e1000_intr()中断处理
函数注册到系统当中,系统有一个中断向量表irq_desc[]。然后使能网卡的中断。
接 下来就是网卡处于响应中断的模式,这里重要的函数是 e1000_intr()中断处理函数,关于这个
函数的说明在内核网络设备操作笔记当中,这里就不重复了,但是重点强调的是中断处理函数中对
NAPI部分 的处理方法,因此还是将该函数的源码列出,不过省略了与NAPI无关的处理过程:
1 | static irqreturn_t e1000_intr(int irq, void *data, struct pt_regs *regs) |
下面介绍一下__netif_rx_schedule(netdev)函数的作用:
1 | static inline void __netif_rx_schedule(struct net_device *dev) |
在内核网络设备操作阅读笔记当中已经介绍过net_rx_action()这个重要的网络接收软中断处理函数了,
不过这里为了清楚的分析轮询机制,需要再次分析这段代码:
1 | static void net_rx_action(struct softirq_action *h) |
下面介绍一下e1000网卡的轮询poll处理函数e1000_clean(),这个函数只有定义了NAPI宏的情况下才有效:
1 |
|
设备轮询接收机制中最重要的函数就是下面这个函数,当然它同时也可以为中断接收机制所用,只不过处理过
程有一定的差别。
1 | static boolean_t |
下面分析的这个函数有助于我们了解环形接收缓冲区的结构和工作原理:
1 | static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter) |
设备收发包之NAPI/非NAPI方式收包
]]>华为经典的两款4G LTE网卡E3372、E8372,俗称卡托,小巧,性能在同类产品中比较不错。有的时候我们需要把4G信号转成有线或者wifi使用,那就可以把E3372(E8372)插在openwrt路由器上实现。E3372根据固件不同有两种模式实现,一种是版本号以21开头固件的NCM模式(拨号),一种是版本号以22开头固件的Hilink模式(Nat),e8372只有Hilink模式。下面分别介绍这两种模式的设置方法。
opkg install kmod-usb-dwc2
opkg install kmod-usb-storage
1 | cat /sys/kernel/debug/usb/devices |
在openwrt系统中安装kmod-usb-net-rndis kmod-usb-net kmod-usb2 usb-modeswitch kmod-usb-net-cdc-ether。
安装完成后,把E3372(E8372)插入路由器USB口,待4G指示灯亮起,打开openwrt路由器wan口(没有就添加它)设置界面,你会发现物理设置接口多了一项以太网适配器: “eth2” ,eth后面的数字是在已有接口基础上增加的。那么它就是4G设备接口,就选中它并保存应用。wan口协议设置成DHCP客户端,这个时候你会发现wan已经能够获取到IP地址,只要防火墙设置正确就能上网了。
如果需要IPv6,再以相同的方法设置wan6,协议设置成DHCPv6客户端,这个时候就能获取到IPv6地址了,具体如何配置openwrt的IPv6,见《openwrt设置nat转发实现ipv6访问互联网》。
在openwrt系统中安装comgt comgt-ncm kmod-usb-net-cdc-ncm kmod-usb-net-huawei-cdc-ncm luci-proto-ncm kmod-usb2 usb-modeswitch kmod-usb-serial-wwan kmod-usb-serial kmod-usb-serial-option。注意luci-proto-ncm是NCM模式的设置界面,不安装它就无法web界面配置NCM拨号。
安装完这些软件,把支持NCM模式的E3372插入路由器USB口,添加接口LTE(名称随便),协议选择NCM,然后再如下图对该接口进行设置。这里是联通的拨号设置,移动和电信略有不同。
保存并应用设置后,过一会将看到网卡指示灯常亮,表明已经拨号成功,但是这个时候路由器本身已经可以上网,但是连接路由器的设备还无法联网。还要对wan口进行设置,没有的要添加这个接口。设置wan口协议为DHCP客户端,物理设置接口选择以太网适配器: “wwan0”,保存并应用,这个时候你会发现wan口已经获取到IP地址了,设置完成后如下图这样。这个时候只要防火墙设置正确,所有设备就可以上网了。
怎么样把Hilink模式的E3372改成NCM模式,请看《华为E3372无线网卡Hilink模式改Modem模式(刷机)》。
]]>https://multipath-tcp.org/pmwiki.php/Users/ConfigureRouting
mptcp 测试网站
1 | #公网测试 |
git clone https://github.com/tracebox/tracebox
./configure CPPFLAGS=-std=c++11
tracebox -p ‘IP/tcp{dst=443}/MPCAPABLE’ multipath-tcp.org
]]>sudo tcpdump -e -n -i eth0 ether proto arp
十六进制查看
sudo tcpdump -e -n -x
route add -net 119.29.29.29 netmask 255.255.255.255 dev vboxnet1
route add default gw 192.168.1.1 dev eth1
]]>1 |
sudo ./src/mlvpn -c client/mlvpn0.conf -u dnsnat -v –debug
sudo ifconfig mlvpn0 192.168.190.1
sudo ip route add 192.168.190.0/24 dev mlvpn0
sudo ifconfig mlvpn0 mtu 1400
server
sudo ./src/mlvpn -c server/mlvpn0.conf -u root -v –debug
sudo ifconfig mlvpn0 192.168.190.2
sudo ip route add 192.168.190.0/24 dev mlvpn0
sudo ifconfig mlvpn0 mtu 1400
sudo taskset -pc 1 4076
sudo taskset -pc 0 3882
iperf3 -c 192.168.181.23 -p 5201 -b 100M -t 600 -i 1 -u
第一路打流120Mbps
Accepted connection from 192.168.181.80, port 48954
[ 5] local 192.168.181.23 port 5201 connected to 192.168.181.80 port 40768
[ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams
[ 5] 0.00-1.00 sec 6.24 MBytes 52.3 Mbits/sec 0.160 ms 3049/7566 (40%)
[ 5] 1.00-2.00 sec 7.25 MBytes 60.8 Mbits/sec 0.207 ms 4408/9655 (46%)
[ 5] 2.00-3.00 sec 6.84 MBytes 57.4 Mbits/sec 0.679 ms 6006/10958 (55%)
[ 5] 3.00-4.00 sec 5.41 MBytes 45.4 Mbits/sec 0.239 ms 4303/8220 (52%)
[ 5] 4.00-5.00 sec 4.07 MBytes 34.1 Mbits/sec 0.464 ms 8706/11652 (75%)
[ 5] 5.00-6.00 sec 5.50 MBytes 46.2 Mbits/sec 1.344 ms 6540/10525 (62%)
[ 5] 6.00-7.00 sec 5.44 MBytes 45.6 Mbits/sec 0.317 ms 6064/10004 (61%)
^C[ 5] 7.00-7.28 sec 1.62 MBytes 48.9 Mbits/sec 0.423 ms 2321/3493 (66%)
[ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams
[ 5] 0.00-7.28 sec 42.4 MBytes 48.8 Mbits/sec 0.423 ms 41397/72073 (57%) receiver
第二路打流120Mbps
Accepted connection from 192.168.180.116, port 34248
[ 5] local 192.168.180.167 port 5201 connected to 192.168.180.116 port 36491
[ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams
[ 5] 0.00-1.00 sec 7.35 MBytes 61.7 Mbits/sec 0.220 ms 3934/9260 (42%)
[ 5] 1.00-2.00 sec 7.36 MBytes 61.8 Mbits/sec 0.365 ms 4556/9888 (46%)
[ 5] 2.00-3.00 sec 7.36 MBytes 61.7 Mbits/sec 0.440 ms 5002/10333 (48%)
[ 5] 3.00-4.00 sec 7.36 MBytes 61.8 Mbits/sec 0.201 ms 5377/10708 (50%)
^C[ 5] 4.00-4.71 sec 5.21 MBytes 61.8 Mbits/sec 0.228 ms 3932/7704 (51%)
[ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams
[ 5] 0.00-4.71 sec 34.7 MBytes 61.7 Mbits/sec 0.228 ms 22801/47893 (48%) receiver
聚合打流120Mbps
Accepted connection from 192.168.190.1, port 37068
[ 5] local 192.168.190.2 port 5201 connected to 192.168.190.1 port 53060
[ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams
[ 5] 0.00-1.00 sec 11.0 MBytes 92.0 Mbits/sec 268.919 ms 3836/12367 (31%)
[ 5] 1.00-2.00 sec 10.5 MBytes 88.1 Mbits/sec 215.123 ms 5593/13761 (41%)
[ 5] 2.00-3.00 sec 12.9 MBytes 108 Mbits/sec 15.588 ms 3871/13870 (28%)
[ 5] 3.00-4.00 sec 12.6 MBytes 106 Mbits/sec 77.637 ms 4179/13966 (30%)
[ 5] 4.00-5.00 sec 12.7 MBytes 106 Mbits/sec 0.285 ms 4307/14160 (30%)
[ 5] 5.00-6.00 sec 13.9 MBytes 116 Mbits/sec 99.522 ms 3325/14100 (24%)
[ 5] 6.00-7.00 sec 13.3 MBytes 112 Mbits/sec 62.812 ms 3154/13532 (23%)
[ 5] 7.00-8.00 sec 12.9 MBytes 108 Mbits/sec 23.962 ms 3762/13788 (27%)
[ 5] 8.00-9.00 sec 14.5 MBytes 121 Mbits/sec 83.716 ms 2891/14134 (20%)
^C[ 5] 9.00-9.05 sec 781 KBytes 129 Mbits/sec 61.042 ms -81/512 (-16%)
[ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams
[SUM] 0.0- 9.0 sec 41147 datagrams received out-of-order
[ 5] 0.00-9.05 sec 115 MBytes 106 Mbits/sec 61.042 ms 34837/124190 (28%) receiver
无线
48.9 Mbits/sec 0.423 ms
有线
61.7 Mbits/sec 0.228 ms
聚合
106 Mbits/sec 61.042 ms
]]>ovs 用于数据中心交换网络 在表项多时 性能不如vpp 不适合4层应用
f-stack 侵入式开发 协议栈不能和应用分离 用于服务器应用
vpp 支持应用分离 多线程 性能强 未来趋势 招聘多
]]>