如何告诉另一个线程一个线程在 recv() 调用中*现在*
How to tell another thread that one thread is within recv() call *now*
有一个带有外部连接(以太网)设备的嵌入式 Linux 系统,一旦通过命令启动它就会发送大量 UDP 数据。
启动时,我有一个线程在程序运行时的剩余时间持续接收 UDP 数据。
正如我所看到的,为了原则上的可靠性,不仅仅是偶然的,必须确保 UDP 接收循环必须在外部数据源启动之前首先调用 recv() ,否则第一个数据包可能会丢失,取决于调度程序的突发奇想。 (这一切都在一个非常本地的、有目的的简单网络设置中——数据包丢失通常不是问题,也没有得到处理——速度为王)
目前,就在调用该 UDP 接收代码之前,我启动了一个延迟一段时间的临时线程,然后使数据源能够发送 UDP 数据。
这是当前 "ensure" 当接收线程 "armed" 时第一个 UDP 数据包到达的方式,即在 recv() 调用中, OS 等待数据。
即使我,比如说,在第一次调用 recv() 之前设置一个条件变量来告诉程序的其余部分 "ok, you can enable the data source now, I'm ready" - 理论上,它可能会发生一些调度在信号标记和实际调用 recv 之间引起的延迟(or/and 因为 recv 的内部实际上已经准备好了)。
有没有比使用一些 "empirical delay time" 更优雅/正确的方法来解决这个问题?
用于说明的伪代码:
// ******** 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();
}
}
在较早的版本中,我建议对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 没有任何保证;您可能仍然会丢失数据包(例如,如果发送方使接收方超载)
当您使用 Linux 时,可能可以使用 FTRACE 来确定您的接收线程的位置。此处的函数跟踪允许一个人(通常在 post 即时调试/分析中)查看进程进行的函数调用。我很确定这是通过某些 /sys 或 /proc 文件系统公开的,因此应该可以实时监控它。
因此,如果您让临时线程查看接收线程的系统调用,它将能够发现它进入了对 recv() 的调用。
如果 FTRACE 尚未内置到您的内核中,您将需要重新编译内核以包含它。可能很方便——无论如何,FTRACE + kernelshark 是调试应用程序的好方法。
有一个带有外部连接(以太网)设备的嵌入式 Linux 系统,一旦通过命令启动它就会发送大量 UDP 数据。 启动时,我有一个线程在程序运行时的剩余时间持续接收 UDP 数据。 正如我所看到的,为了原则上的可靠性,不仅仅是偶然的,必须确保 UDP 接收循环必须在外部数据源启动之前首先调用 recv() ,否则第一个数据包可能会丢失,取决于调度程序的突发奇想。 (这一切都在一个非常本地的、有目的的简单网络设置中——数据包丢失通常不是问题,也没有得到处理——速度为王) 目前,就在调用该 UDP 接收代码之前,我启动了一个延迟一段时间的临时线程,然后使数据源能够发送 UDP 数据。 这是当前 "ensure" 当接收线程 "armed" 时第一个 UDP 数据包到达的方式,即在 recv() 调用中, OS 等待数据。
即使我,比如说,在第一次调用 recv() 之前设置一个条件变量来告诉程序的其余部分 "ok, you can enable the data source now, I'm ready" - 理论上,它可能会发生一些调度在信号标记和实际调用 recv 之间引起的延迟(or/and 因为 recv 的内部实际上已经准备好了)。
有没有比使用一些 "empirical delay time" 更优雅/正确的方法来解决这个问题?
用于说明的伪代码:
// ******** 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();
}
}
在较早的版本中,我建议对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 没有任何保证;您可能仍然会丢失数据包(例如,如果发送方使接收方超载)
当您使用 Linux 时,可能可以使用 FTRACE 来确定您的接收线程的位置。此处的函数跟踪允许一个人(通常在 post 即时调试/分析中)查看进程进行的函数调用。我很确定这是通过某些 /sys 或 /proc 文件系统公开的,因此应该可以实时监控它。
因此,如果您让临时线程查看接收线程的系统调用,它将能够发现它进入了对 recv() 的调用。
如果 FTRACE 尚未内置到您的内核中,您将需要重新编译内核以包含它。可能很方便——无论如何,FTRACE + kernelshark 是调试应用程序的好方法。