如何告诉另一个线程一个线程在recv()调用中* now *

时间:2019-07-30 11:25:46

标签: linux multithreading sockets system-calls recv

有一个嵌入式Linux系统,带有一个外部连接的(以太网)设备,一旦被命令启动,该设备就会发送大量UDP数据。 在启动时,我有一个线程可以在程序运行时的剩余时间内连续接收UDP数据。 正如我所看到的,从原则上讲,出于可靠性的考虑,不仅是偶然的,还必须确保UDP接收循环必须在启动外部数据源之前对recv()进行首次调用,否则第一个数据包可能会丢失,取决于调度程序的异想天开。 (这全部是在非常本地的,有目的的简单网络设置中进行的-数据包丢失通常不是问题且无法解决-速度为王) 当前,在调用该UDP接收代码之前,我启动了一个延迟一段时间的临时线程,然后使数据源发送UDP数据。 当前,这是在接收线程“布防”时(即在recv()调用中,OS等待数据)“确保”第一个UDP数据包到达的方式。

即使我在第一次调用recv()告诉程序的其余部分之前设置一个条件变量,“好的,您现在就可以启用数据源,我已经准备好了”-它可以,从理论上讲,发生在信号标记和对recv的实际调用(或/和recv的内部实际准备就绪)之间存在某种调度引起的延迟。

有没有比使用一些“经验延迟时间”更优雅/适当的方法来解决这个问题?

用于说明的伪代码:

// ******** main thread ********
thread delayed( [&]{ sleepMs(500); enableUdpDataSource(); } );
thread udpRecv( [&]{ udpRecvUntilTimeout() } );
delayed.join();
udpRecv.join();
return 0;

// ******** UDP thread ********
void udpRecvUntilTimeout()
{
  udpInit(); // set up socket, buffer sizes etc

  while (shouldRun)
  {
    // recv() needs to be "armed" *before* the data source is enabled.
    // If I set a condition variable for another thread right here,
    // there may be a scheduling intervention between it and the actual
    // engaging of recv() - when the other thread happily enables the datasource.
    int received = recv( sockFd, buf, maxlen, 0 );
    timeoutWatchdogReset();
    processReceivedData();
  }
}

2 个答案:

答案 0 :(得分:1)

在较早的版本中,我建议对bind的调用是可选的,但是当然不是。您必须调用它才能告诉内核打开哪个UDP端口。 在bind之后,内核将缓冲传入的UDP数据包,如果您对客户端网络详细信息不感兴趣,则可以调用recv(否则,调用recvfrom)。

遵循以下原则:

char buf[1500];
struct sockaddr_in addr;

int sd = socket(AF_INET, SOCK_DGRAM, 0);

memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons((unsigned short) 1234); // UDP port

bind(sd, (struct sockaddr *)&addr, sizeof(addr));

// start data sending thread

sleep(1); // for testing

recv(sd, buf, 100, 0);

但是UDP无法保证;您可能仍然会丢失数据包(例如,如果发送方过载了接收方)

答案 1 :(得分:0)

在使用Linux时,可能可以使用FTRACE来确定接收线程的下落。通过此函数跟踪,可以(通常在事后调试/分析中)查看进程进行的函数调用。我很确定这是通过/ sys或/ proc文件系统公开的,因此应该可以实时监视它。

因此,如果让您的临时线程查看接收线程的系统调用,它将能够发现它进入对recv()的调用。

如果FTRACE尚未内置在内核中,则需要重新编译内核以包含它。可能很方便-FTRACE + kernelshark还是一种调试应用程序的好方法。