13

C擬似コードの場合:

while (1) {
    fifo = open("fifo", O_RDONLY | O_NONBLOCK);
    fd_set read;
    FD_SET(fifo, &read);
    select(nfds, &read, NULL, NULL, NULL);
}

select()プロセスは、別のプロセスがに書き込むまで、によってトリガーされてスリープしfifoます。その後、常にfifo読み取り可能なファイル記述子として検出されます。

この動作を回避する方法(つまり、fifo一度読み取った後、別の書き込みを取得するまで読み取り不能として検出する方法は?)

4

2 に答える 2

40

そのFIFOを読み取り専用(O_RDONLY)として開いた場合、FIFOへのライターがない場合は常に、読み取り側が。を受け取りますEOF

Selectシステムコールがオンに戻り、処理EOFするたびEOFに新しいがありますEOF。これが、観察された動作の理由です。

これを回避するには、読み取りと書き込みの両方でそのFIFOを開きます(O_RDWR)。これにより、FIFOに少なくとも1つのライターが存在することが保証されるため、EOFFIFOに書き込みが行われない限り、selectは返されません。

于 2013-08-11T20:47:09.820 に答える
3

read()簡単な答えは、戻るEWOULDBLOCK(または)まで読むかEAGAIN、エラーでクラップスすることです。

使用しているオペレーティングシステム(またはランタイム)にバグがない限り、あなたが言っていることは単純に起こり得ません。そうでなければ、あなたは何か間違ったことをしているに違いありません。たとえば、select()レベルトリガーI/Oを使用しています。ほとんどの場合、ソケットを完全に使い果たしていないのでselect()、そこに何かが残っていることを常に示していると思います(これはエッジトリガーイベント通知では発生しません)。

read()以下は、記述子が読み取り可能な状態のままになるのを避けるために、リターンまで読み取る方法を示す簡単な例ですEWOULDBLOCK(OS Xでこれをコンパイルしてテストしましたが、エラーチェックもほとんどありませんが、アイデアを得る必要があります)::

/*
 * FIFO example using select.
 *
 * $ mkfifo /tmp/fifo
 * $ clang -Wall -o test ./test.c
 * $ ./test &
 * $ echo 'hello' > /tmp/fifo
 * $ echo 'hello world' > /tmp/fifo
 * $ killall test
 */

#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    int fd;
    int n;
    fd_set set;
    ssize_t bytes;
    size_t total_bytes;
    char buf[1024];

    fd = open("/tmp/fifo", O_RDWR | O_NONBLOCK);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    FD_ZERO(&set);
    FD_SET(fd, &set);

    for (;;) {
        n = select(fd+1, &set, NULL, NULL, NULL);
        if (!n)
            continue;
        if (n == -1) {
            perror("select");
            return EXIT_FAILURE;
        }
        if (FD_ISSET(fd, &set)) {
            printf("Descriptor %d is ready.\n", fd);
            total_bytes = 0;
            for (;;) {
                bytes = read(fd, buf, sizeof(buf));
                if (bytes > 0) {
                    total_bytes += (size_t)bytes;
                } else {
                    if (errno == EWOULDBLOCK) {
                        /* Done reading */
                        printf("done reading (%lu bytes)\n", total_bytes);
                        break;
                    } else {
                        perror("read");
                        return EXIT_FAILURE;
                    }
                }
            }
        }
    }

    return EXIT_SUCCESS;
}

基本的に、レベルトリガーI / Oは、以前に通知されていたとしても、何か読むものがある場合は常に通知を受け取ることを意味します。逆に、エッジトリガーI / Oは、新しいデータが到着するたびに1回だけ通知を受け取ることを意味し、データを読み取るかどうかは関係ありません。select()レベルトリガーI/Oインターフェイスです。

それが役に立てば幸い。幸運を!

于 2013-01-30T00:33:06.293 に答える