Linux select()和多个套接字的FIFO排序?
Linux select()调用中继事件排序有什么方法吗 我所看到的描述: 在一台机器上,我编写了一个简单的程序,它发送三个多播数据包,分别发送给三个不同的多播组。这些数据包是背靠背发送的,中间没有延迟。即sendto(mcast_集团1);sendto(mcast_集团2);sendto(mcast_组3) 在另一台机器上,我有一个接收程序。程序对每个多播组使用一个套接字。每个套接字执行一个bind()和IP_向其侦听的地址添加_成员身份(即加入/订阅)。然后程序对三个套接字执行select() 当select返回时,所有三个套接字都可用于读取。但哪一个先来?套接字的ready for reading列表是一个集合,因此没有顺序。我想要的是,如果select()在每个接收到的数据包中只返回一次,则按顺序(增加的开销在这里是可以接受的)。或者,是否有其他类型的机制可用于确定数据包接收顺序 其他信息:Linux select()和多个套接字的FIFO排序?,linux,select,multicast,fifo,coalescing,Linux,Select,Multicast,Fifo,Coalescing,Linux select()调用中继事件排序有什么方法吗 我所看到的描述: 在一台机器上,我编写了一个简单的程序,它发送三个多播数据包,分别发送给三个不同的多播组。这些数据包是背靠背发送的,中间没有延迟。即sendto(mcast_集团1);sendto(mcast_集团2);sendto(mcast_组3) 在另一台机器上,我有一个接收程序。程序对每个多播组使用一个套接字。每个套接字执行一个bind()和IP_向其侦听的地址添加_成员身份(即加入/订阅)。然后程序对三个套接字执行select(
- 操作系统是x86_64上的CentOS 5(实际上是Redhat Enterprise Linux)
- NIC硬件是Intel 82571EB
- 我尝试了e1000e驱动程序版本1.3.10-k2和2.1.4-NAPI
- 我已经尝试将NIC的中断固定到一个未加载且隔离的CPU内核上
- 我已经通过设置驱动程序选项InterruptThrottrate=0禁用了硬件IRQ合并,并通过ethtool设置rx usecs=0
- 我也尝试过使用epoll,它也有同样的行为
最后一句话:如果我只使用一个套接字,那么数据包顺序将被保留。在本例中,我绑定到INADDR_ANY(0.0.0.0)并在同一套接字上多次执行IP_添加_成员资格。但这不适用于我们的应用程序,因为我们需要绑定到实际多播地址提供的过滤。最终,在同一台机器上会有多个多播接收程序,订阅集可能相互交叉。因此,另一种解决方案可能是找到另一种方法来实现bind()的过滤效果,但不使用bind()。如果select()返回>1,则事件之间的距离必须非常近,以至于排序问题变得毫无意义。如果select()则返回>1事件之间的距离必须非常近,以至于排序问题变得毫无意义。您可以使用fstat获取文件描述符准备就绪的时间戳
有关详细信息,请阅读您可以使用fstat获取文件描述符准备就绪的时间戳
要了解更多信息,请阅读您可以使用IP\u PKTINFO获取数据包发送到的多播组的地址,即使套接字订阅了一组多播组。有了这些,您将按顺序获得数据包,并能够按组地址进行过滤。请参见下面的示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#define PORT 1234
#define PPANIC(msg) perror(msg); exit(1);
#define STATS_PATCH 0
int main(int argc, char **argv)
{
fd_set master;
fd_set read_fds;
struct sockaddr_in serveraddr;
int sock;
int opt = 1;
size_t i;
int rc;
char *mcast_groups[] = {
"226.0.0.1",
"226.0.0.2",
NULL
};
#if STATS_PATCH
struct stat stat_buf;
#endif
struct ip_mreq imreq;
FD_ZERO(&master);
FD_ZERO(&read_fds);
rc = sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(rc == -1)
{
PPANIC("socket() failed");
}
rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if(rc == -1)
{
PPANIC("setsockopt(reuse) failed");
}
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(PORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
rc = bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if(rc == -1)
{
PPANIC("bind() failed");
}
rc = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
if(rc == -1)
{
PPANIC("setsockopt(IP_PKTINFO) failed");
}
for (i = 0; mcast_groups[i] != NULL; i++)
{
imreq.imr_multiaddr.s_addr = inet_addr(mcast_groups[i]);
imreq.imr_interface.s_addr = INADDR_ANY;
rc = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&imreq, sizeof(struct ip_mreq));
if (rc != 0)
{
PPANIC("joing mcast group failed");
}
}
FD_SET(sock, &master);
while(1)
{
read_fds = master;
rc = select(sock + 1, &read_fds, NULL, NULL, NULL);
if (rc == 0)
{
continue;
}
if(rc == -1)
{
PPANIC("select() failed");
}
if(FD_ISSET(sock, &read_fds))
{
char buf[1024];
int inb;
char ctrl_msg_buf[1024];
struct iovec iov[1];
iov[0].iov_base = buf;
iov[0].iov_len = 1024;
struct msghdr msg_hdr = {
.msg_iov = iov,
.msg_iovlen = 1,
.msg_name = NULL,
.msg_namelen = 0,
.msg_control = ctrl_msg_buf,
.msg_controllen = sizeof(ctrl_msg_buf),
};
struct cmsghdr *ctrl_msg_hdr;
inb = recvmsg(sock, &msg_hdr, 0);
if (inb < 0)
{
PPANIC("recvmsg() failed");
}
for (ctrl_msg_hdr = CMSG_FIRSTHDR(&msg_hdr); ctrl_msg_hdr != NULL; ctrl_msg_hdr = CMSG_NXTHDR(&msg_hdr, ctrl_msg_hdr))
{
if (ctrl_msg_hdr->cmsg_level == IPPROTO_IP && ctrl_msg_hdr->cmsg_type == IP_PKTINFO)
{
struct in_pktinfo *pckt_info = (struct in_pktinfo *)CMSG_DATA(ctrl_msg_hdr);
printf("got data for mcast group: %s\n", inet_ntoa(pckt_info->ipi_addr));
break;
}
}
printf("|");
for (i = 0; i < inb; i++)
printf("%c", isprint(buf[i])?buf[i]:'?');
printf("|\n");
#if STATS_PATCH
rc = fstat(sock, &stat_buf);
if (rc == -1)
{
perror("fstat() failed");
} else {
printf("st_atime: %d\n", stat_buf.st_atime);
printf("st_mtime: %d\n", stat_buf.st_mtime);
printf("st_ctime: %d\n", stat_buf.st_ctime);
}
#endif
}
}
return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#include,当前无法检索套接字的顺序或它们更改的时间戳,因为没有为套接字索引节点设置所需的回调。但是如果您能够修补内核,您可以通过在select系统调用中设置时间来解决这个问题
以下补丁可能会给您一个想法:
diff --git a/fs/select.c b/fs/select.c
index 467bb1c..3f2927e 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -435,6 +435,9 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
unsigned long in, out, ex, all_bits, bit = 1, mask, j;
unsigned long res_in = 0, res_out = 0, res_ex = 0;
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
in = *inp++; out = *outp++; ex = *exp++;
all_bits = in | out | ex;
@@ -452,6 +455,16 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
f = fdget(i);
if (f.file) {
const struct file_operations *f_op;
+ struct kstat stat;
+
+ int ret;
+ u8 is_sock = 0;
+
+ ret = vfs_getattr(&f.file->f_path, &stat);
+ if(ret == 0 && S_ISSOCK(stat.mode)) {
+ is_sock = 1;
+ }
+
f_op = f.file->f_op;
mask = DEFAULT_POLLMASK;
if (f_op->poll) {
@@ -464,16 +477,22 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
res_in |= bit;
retval++;
wait->_qproc = NULL;
+ if(is_sock && f.file->f_inode)
+ f.file->f_inode->i_ctime.tv_sec = tv.tv_sec;
}
if ((mask & POLLOUT_SET) && (out & bit)) {
res_out |= bit;
retval++;
wait->_qproc = NULL;
+ if(is_sock && f.file->f_inode)
+ f.file->f_inode->i_ctime.tv_sec = tv.tv_sec;
}
if ((mask & POLLEX_SET) && (ex & bit)) {
res_ex |= bit;
retval++;
wait->_qproc = NULL;
+ if(is_sock && f.file->f_inode)
+ f.file->f_inode->i_ctime.tv_sec = tv.tv_sec;
}
/* got something, stop busy polling */
if (retval) {
diff--git a/fs/select.c b/fs/select.c
索引467bb1c..3f2927e 100644
---a/fs/select.c
+++b/fs/select.c
@@-435,6+435,9@@int do_select(int n,fd_set_bits*fds,struct timespec*end_time)
对于(i=0;if\u路径,&stat);
+如果(ret==0&&S_ISSOCK(统计模式)){
+is_sock=1;
+ }
+
f_op=f.file->f_op;
掩码=默认的\u POLLMASK;
如果(f_op->poll){
@@-464,16+477,22@@int do_select(int n,fd_set_bits*fds,struct timespec*end_time)
res|u in |=位;
retval++;
等待->\u qproc=NULL;
+if(is_sock&&f.file->f_inode)
+f.file->f_inode->i_ctime.tv_sec=tv.tv_sec;
}
if((屏蔽和轮询集)和((输出和位)){
res_out |=位;
retval++;
等待->\u qproc=NULL;
+if(is_sock&&f.file->f_inode)
+f.file->f_inode->i_ctime.tv_sec=tv.tv_sec;
}
if((掩码和POLLEX_集)和(ex&bit)){
res_ex |=位;
retval++;
等待->\u qproc=NULL;
+if(is_sock&&f.file->f_inode)
+f.file->f_inode->i_ctime.tv_sec=tv.tv_sec;
}
/*有消息了,别忙着投票了*/
如果(返回){
注:
这是…只为你:)-不要期望它出现在主线上
在测试每个相关fd之前调用do_gettimeofday()。
为了获得更高的粒度,这应该在每次迭代中完成(并且只有在需要的时候)