UDP パケットの高速ストリームを読み取るために packet_mmap を使用しています。次のコード セグメントのいずれかを使用して着信フレームを待機すると、問題なく動作します。
// ring[i].iov_base points to the start address of the ith frame
struct tpacket_hdr *header = (struct tpacket_hdr *) ring[i].iov_base;
// Using poll on socket to wait for data
while(!(header -> tp_status & TP_STATUS_USER))
{
struct pollfd pfd;
pfd.fd = _socket;
pfd.events = POLLIN | POLLERR;
pfd.revents = 0;
poll(&pfd, 1, -1);
}
// Using nanosleep to wait for incoming data
while(!(header -> tp_status & TP_STATUS_USER))
{
struct timespec t, r;
t.tv_nsec = 1;
t.tv_sec = 0;
nanosleep(&t, &r)
}
しかし、ビジー待機しようとすると (while(!(header -> tp_status & TP_STATUS_USER)) ;
いくつかのパケットが読み取られた後、ステートメントは無期限に True のままになります。これはなぜでしょうか? カーネルは、システム コールが発行されたときにのみフレームをリング バッファーに転送しますか? 入力ソケットは次のように初期化されます。socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))
.
また、このコードを (poll または nanosleep を使用して) 使用すると、パケットがドロップされているように見えますが、UDP ソケットを使用した単純な受信コードではドロップされないため、packet_mmap の実装が遅くなります。ドロップされたパケットがソケットによって検出されることがありますが、次のPACKET_STATISTICS
オプションを使用する場合getsockopt
:
if (header -> tp_status & TP_STATUS_LOSING)
{
struct tpacket_stats stats;
socklen_t size_sock = sizeof(tpacket_stats);
if (getsockopt(_socket, SOL_PACKET, PACKET_STATISTICS, &stats, &size_sock) > -1)
printf("Dropped packets: [%d, %d]\n", stats.tp_drops, stats.tp_packets);
}
ドロップされたパケットがないことを示します (出力例: "ドロップされたパケット: [0, 5]")。ソケットではPACKET_STATISTICS
動作が異なりますか?PACKET_RX_RING
このコードの完全なコード リストは、こちらから入手できます。