Моя статья расскажет Вам как принять 10 миллионов пакетов в секунду без использования таких библиотек как Netmap, PF_RING, DPDK и прочие. Делать мы это будем силами обычного Линукс ядра версии 3.16 и некоторого количества кода на С и С++.
Сначала я хотел бы поделиться парой слов о том, как работает pcap — общеизвестный способ захвата пакетов. Он используется в таких популярных утилитах как iftop, tcpdump, arpwatch. Кроме этого, он отличается очень высокой нагрузкой на процессор.
Итак, Вы открыли им интерфейс и ждете пакетов от него используя обычный подход — bind/recv. Ядро в свою очередь получает данные из сетевой карты и сохраняет в пространстве ядра, после этого оно обнаруживает, что пользователь хочет получить его в юзер спейсе и передает через аргумент команды recv, адрес буфера куда эти данные положить. Ядро покорно копирует данные (уже второй раз!). Выходит довольно сложно, но это не все проблемы pcap.
Кроме этого, вспомним, что recv — это системный вызов и вызываем мы его на каждый пакет приходящий на интерфейс, системные вызовы обычно очень быстры, но скорости современных 10GE интерфейсов (до 14.6 миллионов вызовов секунду) приводят к тому, что даже легкий вызов становится очень затратным для системы исключительно по причине частоты вызовов.
Также стоит отметить, что у нас на сервере обычно более 2х логических ядер. И данные могут прилететь на любое их них! А приложение, которое принимает данные силами pcap использует одно ядро. Вот тут у нас включаются блокировки на стороне ядра и кардинально замедляют процесс захвата — теперь мы занимаемся не только копированием памяти/обработкой пакетов, а ждем освобождения блокировок, занятых другими ядрами. Поверьте, на блокировки может зачастую уйти до 90% процессорных ресурсов всего сервера.
Хороший списочек проблем? Итак, мы их все геройски попробуем решить!
Итак, для определенности зафиксируем, что мы работаем на зеркальных портах (что означает, что откуда-то извне сети нам приходит копия всего трафика определенного сервера). На них в свою очередь идет трафик — SYN флуд пакетами минимального размера на скорости 14.6 mpps/7.6GE.
Сетевые ixgbe, драйверы с SourceForge 4.1.1, Debian 8 Jessie. Конфигурация модуля: modprobe ixgbe RSS=8,8 (это важно!). Процессор у меня i7 3820, с 8ю логическими ядрами. Поэтому везде, где я использую 8 (в том числе в коде) Вы должны использовать то число ядер, которое есть у Вас.
ifconfig eth6 promisc
1 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%] 2 [ 0.0%] 3 [ 0.0%] 4 [ 0.0%] 5 [ 0.0%] 6 [ 0.0%] 7 [ 0.0%] 8 [ 0.0%]
TX eth6: 0 pkts/s RX eth6: 3882721 pkts/s TX eth6: 0 pkts/s RX eth6: 3745027 pkts/s
bash /root/pps.sh eth6 TX eth6: 0 pkts/s RX eth6: 12528942 pkts/s TX eth6: 0 pkts/s RX eth6: 12491898 pkts/s TX eth6: 0 pkts/s RX eth6: 12554312 pkts/s
1 [||||| 7.4%] 2 [||||||| 9.7%] 3 [|||||| 8.9%] 4 [|| 2.8%] 5 [||| 4.1%] 6 [||| 3.9%] 7 [||| 4.1%] 8 [||||| 7.8%]
We process: 222048 pps We process: 186315 pps
1 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||| 86.1%] 2 [|||||||||||||||||||||||||||||||||||||||||||||||||||||| 84.1%] 3 [|||||||||||||||||||||||||||||||||||||||||||||||||||| 79.8%] 4 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 88.3%] 5 [||||||||||||||||||||||||||||||||||||||||||||||||||||||| 83.7%] 6 [||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 86.7%] 7 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 89.8%] 8 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||90.9%]
Samples: 303K of event 'cpu-clock', Event count (approx.): 53015222600 59.57% [kernel] [k] _raw_spin_lock 9.13% [kernel] [k] packet_rcv 7.23% [ixgbe] [k] ixgbe_clean_rx_irq 3.35% [kernel] [k] pvclock_clocksource_read 2.76% [kernel] [k] __netif_receive_skb_core 2.00% [kernel] [k] dev_gro_receive 1.98% [kernel] [k] consume_skb 1.94% [kernel] [k] build_skb 1.42% [kernel] [k] kmem_cache_alloc 1.39% [kernel] [k] kmem_cache_free 0.93% [kernel] [k] inet_gro_receive 0.89% [kernel] [k] __netdev_alloc_frag 0.79% [kernel] [k] tcp_gro_receive
We process: 2250709 pps We process: 2234301 pps We process: 2266138 pps
1 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||92.6%] 2 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.1%] 3 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.2%] 4 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.3%] 5 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.1%] 6 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.7%] 7 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.7%] 8 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.2%]
Samples: 1M of event 'cpu-clock', Event count (approx.): 110166379815 17.22% [ixgbe] [k] ixgbe_clean_rx_irq 7.07% [kernel] [k] pvclock_clocksource_read 6.04% [kernel] [k] __netif_receive_skb_core 4.88% [kernel] [k] build_skb 4.76% [kernel] [k] dev_gro_receive 4.28% [kernel] [k] kmem_cache_free 3.95% [kernel] [k] kmem_cache_alloc 3.04% [kernel] [k] packet_rcv 2.47% [kernel] [k] __netdev_alloc_frag 2.39% [kernel] [k] inet_gro_receive 2.29% [kernel] [k] copy_user_generic_string 2.11% [kernel] [k] tcp_gro_receive 2.03% [kernel] [k] _raw_spin_unlock_irqrestore
We process: 3582498 pps We process: 3757254 pps We process: 3669876 pps We process: 3757254 pps We process: 3815506 pps We process: 3873758 pps
Samples: 778K of event 'cpu-clock', Event count (approx.): 87039903833 74.26% [kernel] [k] _raw_spin_lock 4.55% [ixgbe] [k] ixgbe_clean_rx_irq 3.18% [kernel] [k] tpacket_rcv 2.50% [kernel] [k] pvclock_clocksource_read 1.78% [kernel] [k] __netif_receive_skb_core 1.55% [kernel] [k] sock_def_readable 1.20% [kernel] [k] build_skb 1.19% [kernel] [k] dev_gro_receive 0.95% [kernel] [k] kmem_cache_free 0.93% [kernel] [k] kmem_cache_alloc 0.60% [kernel] [k] inet_gro_receive 0.57% [kernel] [k] kfree_skb 0.52% [kernel] [k] tcp_gro_receive 0.52% [kernel] [k] __netdev_alloc_frag
We process: 9611580 pps We process: 8912556 pps We process: 8941682 pps We process: 8854304 pps We process: 8912556 pps We process: 8941682 pps We process: 8883430 pps We process: 8825178 pps
Samples: 224K of event 'cpu-clock', Event count (approx.): 42501395417 21.79% [ixgbe] [k] ixgbe_clean_rx_irq 9.96% [kernel] [k] tpacket_rcv 6.58% [kernel] [k] pvclock_clocksource_read 5.88% [kernel] [k] __netif_receive_skb_core 4.99% [kernel] [k] memcpy 4.91% [kernel] [k] dev_gro_receive 4.55% [kernel] [k] build_skb 3.10% [kernel] [k] kmem_cache_alloc 3.09% [kernel] [k] kmem_cache_free 2.63% [kernel] [k] prb_fill_curr_block.isra.57
1 [||||||||||||||||||||||||||||||||||||| 55.1%] 2 [||||||||||||||||||||||||||||||||||| 52.5%] 3 [|||||||||||||||||||||||||||||||||||||||||| 62.5%] 4 [|||||||||||||||||||||||||||||||||||||||||| 62.5%] 5 [||||||||||||||||||||||||||||||||||||||| 57.7%] 6 [|||||||||||||||||||||||||||||||| 47.7%] 7 [||||||||||||||||||||||||||||||||||||||| 55.9%] 8 [||||||||||||||||||||||||||||||||||||||||| 61.4%]
К сожалению, не доступен сервер mySQL