Red de conocimiento informático - Material del sitio web - ¿Qué tan difícil es escribir un programa en Linux que reciba 1 millón de paquetes UDP por segundo?

¿Qué tan difícil es escribir un programa en Linux que reciba 1 millón de paquetes UDP por segundo?

Primero, supongamos:

Medir paquetes por segundo (pps) es más interesante que medir bytes por segundo (Bps). Puede obtener Bps más altos canalizando mejor y enviando paquetes más largos, mientras que aumentar los pps es mucho más difícil en comparación.

Dado que estamos interesados ​​en pps, nuestros experimentos utilizarán paquetes UDP más cortos. Para ser precisos, una carga útil UDP de 32 bytes equivale a 74 bytes en la capa Ethernet.

En el laboratorio utilizaremos dos servidores físicos: un "receptor" y un "remitente".

Ambos cuentan con dos procesadores Xeon de seis núcleos a 2 GHz. Cada servidor tiene 24 procesadores con Hyper-Threading (HT) habilitado, equipados con la NIC multicola 10G de Solarflare y tiene una configuración de 11 colas de recepción. Más sobre esto más adelante.

El código fuente del programa de prueba es: udpsender, udpreceiver.

Conocimientos preparatorios

Utilizamos 4321 como puerto para paquetes UDP. Antes de comenzar, debemos asegurarnos de que la transmisión no se vea interferida por iptables:

Shell<. /p> p>

receptor$ iptables -I ENTRADA 1 -p udp --dport 4321 -j ACEPTAR

receptor$ iptables -t raw -I PREROUTING 1 -p udp --dport 4321 - j NOTRACK

Para comodidad de futuras pruebas, definimos claramente la dirección IP:

Shell

receiver$ for i in `seq 1 20`;

dirección IP agregar 192.168.254.$i/24 dev eth2;

hecho

remitente$ dirección IP agregar 192.168.254.30/24 dev eth3

1. Método simple

Primero, realizamos algunos de los experimentos más simples. Simplemente enviando y recibiendo, ¿cuántos paquetes se transmitirán?

Pseudocódigo para simular el remitente:

Python

fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

fd .bind(("0.0.0.0", 65400))# Seleccione el puerto de origen para reducir el no determinismo

fd.connect((("192.168.254.1", 4321))

mientras es cierto:

fd.sendmmsg(["x00" * 32] * 1024)

Dado que utilizamos llamadas al sistema ordinarias para enviar, el cambio de contexto del kernel no es muy eficiente, por lo que es mejor. Para evitar esto, Linux agregó recientemente una conveniente llamada al sistema llamada sendmmsg que nos permite enviar una gran cantidad de paquetes en una sola llamada, lo que nos permite enviar 1024 paquetes a la vez.

Pseudocódigo para simular el receptor:

Python

fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

fd .bind(("0.0.0.0", 4321))

mientras que Verdadero:

paquetes = [Ninguno] * 1024

fd.recvmmsg(paquetes, MSG_WAITFORONE)

Del mismo modo, recvmmsg es una versión más eficiente de la llamada al sistema que recv normal.

Probemos esto:

Shell

remitente$ ./udpsender 192.168.254.1:4321

receptor$ ./udpreceiver1 0.0 . 0.0:4321

0,352 Mpp 10,730 MiB / 90,010 Mb

0,284 M pp 8,655 MiB / 72,603 ​​Mb

0,262 M pp 7,991 MiB / 67,033 Mb

p>

0.199M pps 6.0.081MiB / 51.013Mb

0.195M pps 5.956MiB / 49.966Mb

0.199M pps 6.060MiB / 50.836Mb

0.200Mpps 6.097MiB / 51.147Mb