8

我有一个以 819.2 Hz (~1.2ms) 的速率向我的 QNX Neutrino 机器发送 UDP 数据包的源。我希望以尽可能少的延迟和抖动接收和处理这些消息。

我的第一个代码基本上是:

SetupUDPSocket(); 
while (true) {
    recv(socket, buffer, BufferSize, MSG_WAITALL); // blocks until whole packet is received
    processPacket(buffer);
}

问题是 recv() 仅在系统的每个计时器滴答时检查是否有新的数据包可用。计时器滴答声通常为 1ms。所以,如果我使用它,我会得到很大的抖动,因为我每 1 毫秒或每 2 毫秒处理一个数据包。我可以重置计时器刻度的大小,但这会影响整个系统(以及其他进程的其他计时器等)。而且我仍然会有抖动,因为我肯定不会完全匹配 819.2 Hz。

于是,我尝试使用网卡的中断线(5)。但似乎还有其他因素导致中断上升。我曾经遵循以下代码:

ThreadCtl(_NTO_TCTL_IO, 0);
SIGEV_INTR_INIT(&event);
iID = InterruptAttachEvent(IRQ5, &event, _NTO_INTR_FLAGS_TRK_MSK);

while(true) {
    if (InterruptWait(0, NULL) == -1) {
        std::cerr << "errno: " << errno << std::endl;
    }

    length = recv(socket, buffer, bufferSize, 0); // non-blocking this time

    LogTimeAndLength(); 

    InterruptUnmask(IRQ5, iID;
} 

这会导致在开始时进行一次成功读取,然后在经过 0 次后读取 0 字节长度。看来,在执行 InterruptUnmask() 之后,InterruptWait() 根本不等待,所以必须已经有一个新的中断(或相同的?!)。

是否可以用网卡的中断线做类似的事情?是否还有其他可能以 819.2 Hz 的速率接收数据包?

关于网卡的一些信息:'pci -vvv' 输出:

Class          = Network (Ethernet)
Vendor ID      = 8086h, Intel Corporation 
Device ID      = 107ch,  82541PI Gigabit Ethernet Controller
PCI index      = 0h
Class Codes    = 020000h
Revision ID    = 5h
Bus number     = 4
Device number  = 15
Function num   = 0
Status Reg     = 230h
Command Reg    = 17h
I/O space access enabled
Memory space access enabled
Bus Master enabled
Special Cycle operations ignored
Memory Write and Invalidate enabled
Palette Snooping disabled
Parity Error Response disabled
Data/Address stepping disabled
SERR# driver disabled
Fast back-to-back transactions to different agents disabled
Header type    = 0h Single-function
BIST           = 0h Build-in-self-test not supported
Latency Timer  = 40h
Cache Line Size= 8h un-cacheable
PCI Mem Address = febc0000h 32bit length 131072 enabled
PCI Mem Address = feba0000h 32bit length 131072 enabled
PCI IO Address  = ec00h length 64 enabled
Subsystem Vendor ID = 8086h
Subsystem ID        = 1376h
PCI Expansion ROM = feb80000h length 131072 disabled
Max Lat        = 0ns
Min Gnt        = 255ns
PCI Int Pin    = INT A
Interrupt line = 5
CPU Interrupt  = 5h
Capabilities Pointer = dch
Capability ID        = 1h - Power Management
Capabilities         = c822h - 28002000h
Capability ID        = 7h - PCI-X
Capabilities         = 2h - 400000h
Device Dependent Registers:
0x040:  0000 0000 0000 0000   0000 0000 0000 0000 
...
0x0d0:  0000 0000 0000 0000   0000 0000 01e4 22c8 
0x0e0:  0020 0028 0700 0200   0000 4000 0000 0000 
0x0f0:  0500 8000 0000 0000   0000 0000 0000 0000 

和“nicinfo”输出:

wm1: 
    INTEL 82544 Gigabit (Copper) Ethernet Controller

    Physical Node ID ........................... 000E0C C5F6DD
    Current Physical Node ID ................... 000E0C C5F6DD
    Current Operation Rate ..................... 100.00 Mb/s full-duplex
    Active Interface Type ...................... MII
    Active PHY address ....................... 0
    Maximum Transmittable data Unit ............ 1500
    Maximum Receivable data Unit ............... 0
    Hardware Interrupt ......................... 0x5
    Memory Aperture ............................ 0xfebc0000 - 0xfebdffff
    Promiscuous Mode ........................... Off
    Multicast Support .......................... Enabled

谢谢阅读!

4

4 回答 4

1

我不太确定为什么“问题是如果有新的数据包可用,recv() 只会在系统的每个计时器滴答声中检查。计时器滴答声通常是 1 毫秒。” 对于抢占式操作系统来说是正确的。系统配置中一定有问题,或者网络协议栈实现有问题。

几年前,当我为 Yahoo BB Japan 做一些 IPTV STB 项目时,我遇到了 RTP 接收问题。问题不在于延迟或抖动,而是在我们添加一些 NDS 算法后,STB 中的整体系统性能。我们使用的是vxWorks,vxWorks支持ethernet hook接口,驱动每次接收到一个ethernet packet都会调用这个接口。

我将一个 API 连接到其中,然后直接从以太网数据包中解析具有指定端口的 UDP。当然,我们有一些假设没有碎片,这是由网络设置保证的性能问题。也许您还可以检查一下是否可以在 QNX 以太网驱动程序中获得相同的挂钩。至少您发现抖动是否来自驱动程序。

于 2013-02-27T05:30:23.867 回答
0

How big are your UDP packets ? If the packet size is small you will gain greater efficiency by packing more data into single packet and decreasing transmission rate.

于 2012-09-07T09:48:09.810 回答
0

抱歉,我参加聚会有点晚了,但是我遇到了您的问题,发现这与我遇到的情况相似。您可以尝试使用信号的软件中断,而不是硬件中断。QNX 在这里有一些文档:http ://www.qnx.com/developers/docs/qnx_4.25_docs/qnx4/sysarch/microkernel.html#IPCSIGNALS 。我当时使用的是 CentOS,但理论是一样的。根据http://www.qnx.com/developers/docs/6.3.0SP3/neutrino/lib_ref/s/socket.html您可以使用 ioctl() 为给定文件描述符的 SIGIO 信号设置接收组...在您的情况下为 UDP 套接字。当套接字有准备好读取的数据时,会向 ioctl() 指示的进程发送一个 SIGIO 信号。使用 sigaction() 告诉操作系统使用什么信号处理函数。在您的情况下,信号处理程序可以从套接字读取数据并将其存储在缓冲区中进行处理。使用 pause() 暂停进程,直到它处理 SIGIO 信号。当信号处理程序返回时,进程将被唤醒,您可以处理缓冲区中的数据。这应该允许您处理传入的数据,而无需处理计时器或硬件中断。需要注意的一件事是,您的系统可以像 UDP 流量进入一样快地处理这些信号。

于 2013-03-20T04:06:07.710 回答
0

我怀疑中断服务路由(ISR)没有屏蔽中断。也许它是为边缘敏感而设计的,而中断是电平敏感的。

于 2012-10-09T22:22:24.940 回答