【问题标题】:Understanding POSIX select() with read and write fd_set通过读写 fd_set 理解 POSIX select()
【发布时间】:2014-05-21 13:32:35
【问题描述】:

对于一个学校项目,我们将使用文件描述符和 select() 在一台机器上实现并发。在程序中,我们有 RequestChannel 对象,其中包含两个文件描述符,一个用于读取,一个用于写入,用于与在执行开始时分叉的单独进程进行通信。我可以用写文件描述符来做一些事情,但读 fds 似乎永远不会准备好。我能否获得一些帮助来理解 select() 如何与读写描述符一起工作?我看到的一切都是在处理套接字,这让我很困惑,我只想了解通用文件描述符和 select()。

这是我的选择循环:

fd_set readset, writeset;
FD_ZERO(&readset); 
FD_SET(JohnDoe.read_fd(), &readset);
FD_SET(JoeSmith.read_fd(), &readset);
FD_SET(JaneSmith.read_fd(), &readset);
FD_ZERO(&writeset);
FD_SET(JohnDoe.write_fd(), &writeset);
FD_SET(JoeSmith.write_fd(), &writeset);
FD_SET(JaneSmith.write_fd(), &writeset);

int maxfd = 0;
maxfd = max(maxfd, JohnDoe.read_fd());
maxfd = max(maxfd, JohnDoe.write_fd());
maxfd = max(maxfd, JoeSmith.read_fd());
maxfd = max(maxfd, JoeSmith.write_fd());
maxfd = max(maxfd, JaneSmith.read_fd());
maxfd = max(maxfd, JaneSmith.write_fd());

int numready;
int count = 0;
while (count < 10) {
    numready = select(maxfd + 1, &readset, &writeset, NULL, NULL);
    if (numready == -1) {
        cout << "Fatal error, aborting\n";
        break; 
    }
    else { 
        if(FD_ISSET(JohnDoe.write_fd(), &writeset)) { //write_fd() returns write file descriptor
            JohnDoe.cwrite("data John Doe"); //one RequestChannel object
        }
        if(FD_ISSET(JoeSmith.write_fd(), &writeset)) {
            JoeSmith.cwrite("data Joe Smith");
        }
        if(FD_ISSET(JaneSmith.write_fd(), &writeset)) {
            JaneSmith.cwrite("data JaneSmith");
        }



        if(FD_ISSET(JohnDoe.read_fd(), &readset)) { 
            string s = JohnDoe.cread();
            cout << "John Doe cread: " << s << "\n";
        }
        if(FD_ISSET(JoeSmith.read_fd(), &readset)) { 
            string s = JoeSmith.cread();
            cout << "Joe Smith cread: " << s << "\n";
        }
        if(FD_ISSET(JaneSmith.read_fd(), &readset)) { 
            string s = JaneSmith.cread();
            cout << "Jane Smith cread: " << s << "\n";
        }
    }
}

【问题讨论】:

  • 我没有看到为select() 调用设置描述符集的代码。应该有一些 FD_ZERO()FD_SET() 调用来初始化描述符集。
  • 我之前只是显示了选择循环,但我添加了它

标签: c++ c select system-calls


【解决方案1】:

您需要在每次调用select() 之前重新初始化描述符集,因为select() 会修改描述符集。您可以通过在循环中使用FD_ZERO()/FD_SET() 或通过初始化您复制传递给select() 的“原型”集来执行此操作:

fd_set readset, writeset;

int maxfd = 0;
maxfd = max(maxfd, JohnDoe.read_fd());
maxfd = max(maxfd, JohnDoe.write_fd());
maxfd = max(maxfd, JoeSmith.read_fd());
maxfd = max(maxfd, JoeSmith.write_fd());
maxfd = max(maxfd, JaneSmith.read_fd());
maxfd = max(maxfd, JaneSmith.write_fd());

int numready;
int count = 0;
while (count < 10) {
    FD_ZERO(&readset); 
    FD_SET(JohnDoe.read_fd(), &readset);
    FD_SET(JoeSmith.read_fd(), &readset);
    FD_SET(JaneSmith.read_fd(), &readset);
    FD_ZERO(&writeset);
    FD_SET(JohnDoe.write_fd(), &writeset);
    FD_SET(JoeSmith.write_fd(), &writeset);
    FD_SET(JaneSmith.write_fd(), &writeset);

    numready = select(maxfd + 1, &readset, &writeset, NULL, NULL);

    // etc...

}

【讨论】:

  • 好吧,这行得通,但为什么每次选择调用都需要重新初始化 fd_sets?
  • 我已经更新了答案,基本上是因为select()修改了它们。
【解决方案2】:

是的,cjbrooks12 是正确的。在 select() 系统调用之间需要reset the fd_set

它们充当输入/输出参数;它们由系统调用读取和修改。当 select() 返回时,值已全部修改to reflect the set of file descriptors ready。因此,每次调用 select() 之前,您都有 to (re)initialize the fd_set 值。

并且还让 while 循环无限(或增加计数器值),以便它会等到 read fds 准备好。

【讨论】:

    猜你喜欢
    • 2020-03-04
    • 1970-01-01
    • 2014-08-05
    • 2011-02-02
    • 1970-01-01
    • 2015-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多