【问题标题】:C use select and read from pipeC使用选择并从管道中读取
【发布时间】:2011-10-16 21:56:31
【问题描述】:

我正在开发一个程序,我使用 select 从管道中进行选择以进行读取。

问题是我正在使用if(FD_ISSET(pfd[i][0], &read_set)),但验证失败,因为 FD_ISSET 返回 0,但如果我尝试使用 read(pfd[i][0],&c,sizeof(c)) 读取,它将正常工作,并且管道 pfd[0] 中的内容不同像我想要的那样来自 pfd[1]。

我想知道到底发生了什么,因为 FD_ISSET 返回 0 但我实际上可以从中读取内容

编辑

全局变量 CORES 将控制我创建多少进程 下一个代码将创建管道并使用 FD_SET 设置位

for(i=0; i<CORES; i++)
{
    if(pipe(pfd[i])==-1)
        perror("Ocorreu um erro a criar um pipe");

    FD_SET(pfd[i][0], &read_set);
}

read_set 在 main() 启动后立即定义。我在上述代码之后使用 for() 使用 fork() (x = CORES) 创建 x 个进程

那么这部分在父进程中运行:

    while (x<CORES){
    int selec = select(pfd[CORES-1][0]+1, &read_set, NULL, NULL, NULL);

    if(selec>0)
    {
        if(FD_ISSET(pfd[x][0], &read_set))
        {
            close(pfd[x][1]);

            if(read(pfd[x][0],&c,sizeof(c))==-1)
                perror("Ocorreu um erro a ler do pipe");

            printf("c= %f \n",c);
            c_final+=c;
            x++;
        }
        else
            printf("\nFile descriptor pfd[%d][0] is not set\n",x);

    }
    else if(selec == -1)
        perror("Ocorreu um erro no select");
}

问题在于FD_ISSET(pfd[x][0], &amp;read_set) 不会通过,因为它没有设置为 pfd[0] 但它可以设置为 pfd[1] 或反之亦然(因为我不知道它是否会失败0 或 1)

EDIT2:

select() 返回一个 int,表示 read_set 中的文件描述符数。我将 CORES 设置为 2,因此它创建了 2 个进程和 2 个管道,并在 read_set 中设置了 2 个位。我的 select() 正在返回 int 1。它应该返回 int 2 还是 0 计数(所以它会是 0 和 1 = 2)?

【问题讨论】:

  • 你在某处做错了什么,显示代码。
  • 在调用select之前,fd_set必须包含你想要等待的所有文件描述符,然后在select返回之后,只有准备好的那些仍然会被设置。我猜你以某种方式搞砸了..
  • 您是否使用非阻塞读取来执行此操作?如果没有,则 read 将等待输入,即使您没有在选择时输入,这也会使您看起来像是输入了。
  • while循环在select之外有点奇怪。通常,您会先进行选择,然后使用循环查看设置了哪些描述符。
  • 带有x 的while 循环是错误的。或者换一种说法:如果您只对一个fd 感兴趣,为什么还要选择?你可能会忙着等待

标签: c select pipe


【解决方案1】:

select 的第一个参数应该是1+max_fd。虽然pfd[CORES-1][0] 可能是最大的,但不能保证。您可以在填充 read_set 时计算最大的文件描述符,并将其用于您的 select 调用。

此外,您需要在每次调用 select 之前重新填充 read_set。所以需要在while循环之外的for循环进来。实际上,最好将select移到外面,并将以for循环开始设置read_set并在while循环之后结束的块包含在另一个循环中 - 所以在每次选择返回时,您无需再次进入选择即可处理所有信号描述符。


编辑:澄清我对循环的含义:每次选择返回时都应该检查所有描述符。但是您不能只调用一次 select,因为不能保证所有答案都会立即准备好。所以你做这样的事情:

for (i=0; i != CORES; ++i)
{
  // Call pipe() and populate pfd[i]
}

while (!finished) // Need a proper condition to terminate this loop
{
    // Setup read_set
    FD_ZERO(&read_set);
    for (i=0; i != CORES; ++i)
    {
        FD_SET(pfd[i][0], &read_set);
        // find max_fd here
    }
    // select here
    if (select(max_fd+1, blahblah) > 0)
    {
        for (i=0; i != CORES; ++i) // This used to be while(x < CORES). But a for loop would do
        {
            // Check the fd, and process
        }
    }
    else
    {
         // error in select
    }
}

【讨论】:

  • 重新填充 read_set 是什么意思?我对你对循环和while的解释有点困惑。你可以给我一个例子吗?谢谢你的回答
  • @andrepcg 我的意思是从 0 到 CORES 的 for 循环,为 read_set 变量调用 FD_SET()。当然,不用打电话给pipe。每次您要调用select 时,您都应该重复该循环(这是从记忆中得出的,因此您可能需要检查一下)。我将编辑我的答案以澄清其余部分。
  • 但是如果我在选择之前已经为每个文件描述符做了 FD_SET 为什么我还要这样做呢?谢谢!
  • @andrepcg select() 调用修改了read_set。如果您再次调用 select,您将只选择准备好读取的 fd(其余的将被清除)。所以你用你感兴趣的描述符重建read_set
  • @andrepcg:您在其中一个选择之前完成了它,但由于选择在循环内,因此每次迭代都需要完成。
【解决方案2】:

我建议在这种情况下,您尝试读取所有这些不同的文件描述符并检查非阻塞写入(即,您似乎正在尝试检查是否有任何结尾管道处于活动状态),您使用poll() 而不是select(),除非出于某种原因您必须使用select()。使用 poll,您可以简单地将所有文件描述符设置在 struct pollfd 的数组中,并为他们提供您要测试的事件。然后poll() 返回后的结果会让你知道有多少文件描述符有事件。我认为这种方法会让你更容易管理你在这里拥有的东西。例如,您可以执行以下操作:

struct pollfds descriptors[CORES * 2] = {0};

for(int i=0; i<CORES; i++)
{
    if(pipe(pfd[i])==-1)
        perror("Ocorreu um erro a criar um pipe");

    //wait for either a read on pipe[0] or a non-blocking write on pipe[1]
    descriptors[i*2].fd = pfd[i][0];
    descriptors[i*2].events = POLLIN;
    descriptors[(i*2)+1].fd = pdf[i][1];
    descriptors[(i*2)+2].events = POLLOUT;
}

while (x < CORES)
{
    int num_events = poll(descriptors, CORES * 2, -1); //infinite timeout period

    //...check your descriptors array for events
}

【讨论】:

  • 这是我正在处理的作业,我需要使用 select() 从特定管道中读取数据
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-23
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多