sendmsg+原始以太网+若干帧

sendmsg + raw ethernet + several frames

本文关键字:原始 以太网 sendmsg+      更新时间:2023-10-16

我使用linux 3.x和现代glibc(2.19)

我想在不切换内核/用户空间的情况下来回发送几个以太网帧。

我有MTU = 1500,我想发送800 KB。我初始化接收器地址如下:

struct sockaddr_ll socket_address;
socket_address.sll_ifindex = if_idx.ifr_ifindex;
socket_address.sll_halen = ETH_ALEN;
socket_address.sll_addr[0] = MY_DEST_MAC0;
//...

之后我可以调用sendto/sendmsg 800KB / 1500 ~= 500次,一切都很好,但这需要用户空间<->内核协商~每秒500 * 25次。我想避开它。

我尝试用适当的信息CCD_ 8,但收到错误"消息太长",看起来msghdr::msg_iov无法用size > MTU描述某些内容。

所以问题是,在Linux上同时从用户空间发送许多原始以太网帧可能吗?

PS

我从文件中获取数据(800KB),并将其读取到内存中。所以struct iovec对我来说很好,我可以创建合适数量的以太网头,并且必须iovec per 1500 packet,一点到数据,一点对以太网头。

哇。

我的上一家公司生产实时隐藏视频编码硬件。在实验室里,我们必须在绑定链路上以每秒200MB的速度进行爆破,所以我对此有一些经验。以下内容基于此。

在你能够调谐之前,你必须测量。您不想进行多个系统调用,但您能通过计时测量来证明开销是显著的吗?

我在clock_gettime周围使用了一个包装程序,它以纳秒的精度返回一天中的时间(例如(tv_sec * 100000000) + tv_nsec)。称之为"纳米时间"。

因此,对于任何给定的系统调用,都需要一个度量:

tstart = nanotime();
syscall();
tdif = nanotime() - tstart;

对于send/sendto/sendmsg/write,这样做会使数据变小,这样您就可以确保没有阻塞[或使用O_NONBLOCK,如果适用]。这为您提供系统调用开销

为什么要直接使用以太网帧?TCP(或UDP)通常足够快,现代NIC卡可以在硬件中完成信封包装/剥离。我想知道是否存在特定的情况,需要以太网帧,或者是因为您没有获得所需的性能,并将其作为解决方案。请记住,您所做的是800KB/s(~1MB/s),而我的项目在TCP上所做的比这多100x-200倍。

对套接字使用两个普通的write调用怎么样?一个用于标头,一个用于数据[all800KB]。write可以在套接字上使用,并且没有EMSGSIZE错误或限制。

此外,为什么您需要将头放在单独的缓冲区中?当你分配缓冲区时,只需执行:

datamax = 800 * 1024;  // or whatever
buflen = sizeof(struct header) + datamax;
buf = malloc(buflen);
while (1) {
datalen = read(fdfile,&buf[sizeof(struct header)],datamax);
// fill in header ...
write(fdsock,buf,sizeof(struct header) + datalen);
}

这甚至适用于以太网帧的情况。

还可以做的一件事是使用setsockopt来增加套接字的内核缓冲区的大小。否则,你可以发送数据,但在接收器耗尽数据之前,它会被丢弃在内核中。

要测量电线的性能,请在标题中添加一些字段:

u64 send_departure_time;  // set by sender from nanotime
u64 recv_arrival_time;  // set by receiver when the packet arrives

所以,发送方设置出发时间并写[只需为这个测试做标题]。将此数据包称为Xs。当它到达时,接收者会在上面盖章。接收方立即向发送方发送回一条消息[称之为Xr],该消息带有出发印章和Xs的内容。当发送者收到这个信息时,它会在上面盖上到达时间。

有了以上内容,我们现在有了:

T1 -- time packet Xs departed sender
T2 -- time packet Xs arrived at receiver
T3 -- time packet Xr departed receiver
T4 -- time packet Xr arrived at sender

假设您在一个相对安静的连接上这样做,几乎没有其他流量,并且您知道链路速度(例如1 Gb/s),那么使用T1/T2/T3/T4,您可以计算开销。

您可以重复TCP/UDP与ETH的测量。你可能会发现,它给你买的并没有你想象的那么多。再一次,你能用精确的测量来证明吗?

我在上述公司工作时"发明"了这种算法,结果发现它已经是通过100Gb以太网NIC卡发送原始视频的视频标准的一部分,并且NIC在硬件中进行时间戳。

你可能要做的另一件事是增加一些油门控制。这与bittorrent或PCIe总线的功能类似。

当PCIe总线节点首次启动时,它们会通信可用于"盲写"的可用缓冲区空间。也就是说,发送者可以在没有任何ACK消息的情况下自由地发送这么多消息。当接收器耗尽其输入缓冲区时,它会向发送器发送周期性的ACK消息,其中包含能够耗尽的字节数。发送方可以将此值添加回盲写限制并继续。

出于您的目的,盲写限制是接收器的内核套接字缓冲区的大小。

更新

根据您评论中的一些附加信息[实际的系统配置应该以更完整的形式显示,作为对底部问题的编辑]。

确实需要一个原始套接字并发送以太网帧。可以通过ifconfig设置更大的MTU或使用SIOCSIFMTU设置ioctl调用来减少开销。我推荐ioctl。您可能不需要将MTU设置为800KB。您的CPU的NIC卡有一个实际限制。你可以很容易地将MTU从1500增加到15000。这将使系统调用的开销减少10倍,这可能"足够好"。

您可能必须使用sendto/sendmsg。两个write调用是基于到TCP/UDP的转换。但是,我怀疑sendmsgmsg_iov将比sendto有更多的开销。如果搜索,您会发现您想要的大多数示例代码都使用sendtosendmsg对您来说似乎是较少的开销,但它可能会导致内核的更多开销。下面是一个使用sendto的示例:http://hacked10bits.blogspot.com/2011/12/sending-raw-ethernet-frames-in-6-easy.html

除了提高系统调用开销外,更大的MTU可能会提高"连线"的效率,尽管这在您的用例中似乎不是问题。我有CPU+FPGA系统以及它们之间通信的经验,但我仍然对你关于"不使用电线"的评论感到困惑。FPGA连接到CPU的以太网引脚,我得到了——有点像。更准确地说,你的意思是连接到CPU"NIC卡/芯片以太网引脚的FPGA引脚吗?

CPU/NIC是否在同一块PC板上,FPGA引脚是否通过PC板迹线连接?否则,我不理解"不使用电线"。

然而,我必须再次指出,在您盲目地尝试改进性能之前,您必须能够衡量您的性能。

您是否运行了我建议的用于确定系统调用开销的测试用例?如果它足够小,尝试为其进行优化可能不值得,这样做可能会严重损害在其他领域的性能,而您在开始时没有意识到这一点。

举个例子,我曾经处理过一个系统,它有严重的性能问题,以至于系统无法工作。我怀疑串行端口驱动程序很慢,所以我从高级语言(如C)重新编码到汇编程序中。

我将驱动程序的性能提高了2倍,但它对系统的性能改进不到5%。事实证明,真正的问题是其他代码正在访问不存在的内存,这只会导致总线超时,显著降低系统速度[它确实没有生成中断,这将使其像在现代系统中一样容易找到]。

从那时起,我了解了测量的重要性。我的优化是基于受过教育的猜测,而不是硬数据。之后:吸取教训!

如今,我从未尝试过大型优化,直到我可以首先进行测量。在某些情况下,我会添加一个我"确信"会让事情变得更好的优化(例如内联函数)。当我测量它时[并且因为可以测量它],我发现新代码实际上慢了,我必须恢复更改。但是,这就是重点:我可以用性能数据来证明/反驳这一点。

您使用的CPU是什么:x86、arm、mips等。时钟频率是多少?多少DRAM?有多少核心?

您使用的FPGA是什么(例如Xilinx、Altera)?具体类型/零件号是什么?最大时钟速率是多少?FPGA是完全用于逻辑,还是内部也有CPU,如microblaze、nios、arm?FPGA可以访问自己的DRAM吗?

如果你增加MTU,FPGA能从缓冲区/空间的角度或时钟速度的角度处理它吗???如果你增加MTU,你可能需要添加一个ack/sync协议,就像我在最初的文章中建议的那样。

目前,CPU正在对数据进行写入,希望FPGA能够处理它。这意味着CPU和FPGA之间存在开放竞争条件。

纯粹作为发送小数据包的副作用,可以减轻这种情况。如果MTU增加太多,可能会使FPGA不堪重负。换句话说,正是你试图优化掉的开销,才使得FPGA能够跟上数据速率。

这就是我所说的盲目优化的意外后果。它可能会产生意想不到的更严重的副作用。

发送到FPGA的数据的性质是什么?您正在发送800KB,但多久发送一次?

我假设这是而不是FPGA固件本身,原因有几个。你说固件已经快满了[正在接收以太网数据]。此外,固件通常通过I2C总线、ROM或FPGA编程器加载。那么,我说得对吗?

您正在将数据从一个文件发送到FPGA。这意味着它只在CPU应用程序启动时发送一次。这是正确的吗?如果是这样,就不需要优化,因为这是一个初始化/启动成本,对运行的系统几乎没有影响。

因此,我不得不假设该文件被加载了很多次,每次都可能是不同的文件。这是正确的吗?如果是这样,您可能需要考虑read系统调用的影响。不仅来自系统调用开销,还来自最佳读取长度。例如,IIRC,磁盘到磁盘或文件到文件复制/传输的最佳传输大小为64KB,具体取决于文件系统或底层磁盘的特性。

因此,如果你想减少开销,从文件中读取数据可能比让应用程序生成数据要多得多(如果可能的话)。

内核syscall接口的设计开销非常低。内核程序员(我恰好是其中之一)花了大量时间来确保低开销。

您说您的系统正在将大量CPU时间用于其他事务。你能测量其他东西吗?您的应用程序是如何构建的?有多少流程?有多少线程?他们是如何沟通的?什么是延迟/througput?您可能能够找到[很可能找到]更大的瓶颈并重新编码这些瓶颈,您将获得CPU使用量的总体减少,远远超过从MTU调整中获得的最大好处。

尝试优化系统调用开销可能与我的串行端口优化类似。付出了很多努力,但总体结果令人失望。

在考虑性能时,重要的是从系统的整体角度来考虑。在你的情况下,这意味着CPU、FPGA和其他任何东西

你说中央处理器在做很多事情。这些算法中的一些可以/应该进入FPGA吗?它们的原因不是因为FPGA几乎没有空间,否则你会吗?FPGA固件是否100%完成?或者,还有更多的RTL要写吗?如果你在FPGA中的空间利用率达到90%,并且你需要更多的RTL,你可能希望考虑使用具有更多逻辑空间的FPGA部分,可能具有更高的时钟速率。

在我的视频公司,我们使用FPGA。我们使用了FPGA供应商拥有的最大/最快的最先进部件。我们还使用了几乎100%的逻辑空间,并要求部件的最大时钟速率。供应商告诉我们,我们是其全球客户公司中FPGA资源的最大消费者。正因为如此,我们使供应商的开发工具变得紧张。地点和路线经常会失败,必须重新运行才能获得正确的位置和满足时间要求。

因此,当FPGA几乎充满逻辑时,位置和路径可能很难实现。这可能是考虑更大一部分的原因(如果可能的话)