1
votes

I use libpcap to capture packets, and I need to put the packets into a FIFO queue as soon as a packet is available. But the FIFO queue is shared by 2 threads, one thread call pcap_next() and put packet into the FIFO queue. Another thread fetch packet from the fifo queue. so I have to relate it to a mutex. Like below:

u_char* pkt;
for(;;){
    pkt = pcap_next();
    lock(&mutex);
    some_process(pkt);
    insert(pkt, list);
    unlock(&mutext);
 }

The pcap_next() is related to a packet buffer, if there is no packet in the buffer, pcap_next() is blocked. If there is/are packet(s), each call of pcap_next() returns 1 packet.

it can only fetch oen packet for each lock-unlock operation pair, If packet arrival is not frequent, then it is fine. But if packet arrival is frequent, like in the buffer there are many pending packets, it is a bit resource-consuming to don a lock-unlock operation pair for one packet.

what I hope is: after processing and inserting a packet, I immediately can check whether there are packets available in the packet buffer. If there are, continue processing and insertion. Otherwise, unlock mutex and go back to loop.

Is there a workaround for this?

2

2 Answers

1
votes

Try something such as

/*
 * XXX - this can fail on some platforms and with some devices,
 * and there may be issues with select() on this.  See the
 * pcap_get_selectable_fd() man page for details.
 */
pcap_fd = pcap_get_selectable_fd(p);
pcap_setnonblock(p);  /* XXX - check for failure */

for (;;) {
    fd_set fdset;
    struct timeval timeout;

    /*
     * Wait for a batch of packets to be available.
     */
    FD_ZERO(&fdset);
    FD_SET(pcap_fd, &fdset);
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    if (select(1, &fdset, NULL, NULL, &timeout) == -1) {
        report an error;
    } else {
        lock(&mutex);
        pcap_dispatch(p, -1, callback, pointer-to-stuff);
        unlock(&mutex);
    }
}

That way, you lock the mutex, process an entire batch of packets, and then unlock the mutex. Many OS capture mechanisms deliver multiple packets in a batch, so there will be one lock/unlock pair per batch in this case.

callback would do the some_process(pkt); and insert(pkt, list); stuff.

It is possible that, once you're done with a batch, the next batch will be immediately available, so this doesn't achieve an absolute minimum of lock/unlock pairs; however, the absolute minimum might lock out the other thread for a significant period of time, so that it can never make any progress, so lock and unlock around each batch might be best.

0
votes

just use pcap_dispatch(), or select() with non-blocking style pcap_next()