Выбор по именованному каналу (FIFO) вызывает бесконечный цикл - PullRequest
0 голосов
/ 29 декабря 2018

У меня есть петля.Внутри этого цикла я пытаюсь определить, инициировано ли чтение или запись в файл с именованным каналом (FIFO) с помощью select().

Если инициировано чтение, я вызываю read() в дескрипторе файла FIFO..
Если запись активирована, я вызываю write() в дескрипторе файла FIFO

Проблема в том, что если произойдет запись, и я запишу в FIFO, она вызовет чтение.И тогда, когда я читаю из FIFO, он запускает запись.Вызывает бесконечный цикл.

Этот цикл происходит немедленно, если я использую тот же файловый дескриптор в режиме O_RDWR.Этот цикл происходит после первой записи, если я создаю отдельный дескриптор файла для чтения и записи.

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

int main() {
    // Open export fifo
    int fd = open("./foo-fifo", O_RDWR | O_CREAT);
    if (fd < 0) { // Failed to open
        perror("error opening fifo");
    }

    // Read or write fifo until "quit" is in buffer 
    while (true) {
        fd_set read_fds;
        fd_set write_fds;

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

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

        int num_fds = select(fd+1, &read_fds, &write_fds, NULL, NULL);
        if (num_fds < 0) { // Failed to select
            perror("failed to select fifo fd");
        } else if (num_fds == 0) { // Timeout
            continue;
        }

        // If read
        if (FD_ISSET(fd, &read_fds)) {
            char buf[1000] = "";

            if (read(fd, buf, sizeof(buf)) < 0) {
                perror("error reading fifo");
            }

            printf("read: \"%s\"\n", buf);

            if (strcmp(buf, "quit\n") == 0) {
                break;
            }
        }

        // If write
        if (FD_ISSET(fd, &write_fds)) {
            char *buf = "foo";

            if (write(fd, buf, sizeof(buf)) < 0) {
                perror("error writing fifo");
            }

            printf("write: \"%s\"\n", buf);
        }
    }

    // Close fifo
    if (close(fd) < 0) { // Failed to close
        perror("failed to close export fifo");
    }

    return 0;
}

Запустите пример, загрузив код отсюда (GitHub Gist) .Затем запустите:

gcc -o fifo fifo.c
./fifo

На выходе отобразится цикл между чтением и записью:

write: "foo"
read: ""
write: "foo"
read: ""
write: "foo"
...

1 Ответ

0 голосов
/ 02 января 2019

Примечание: Это предварено моими главными комментариями.

Нам нужны два процесса (например, сервер и клиент).

Тифы в одном направлении (aписатель и читатель), не как сокет.

Итак, чтобы сделать это с fifos, вам понадобится два из них.(например) Для процессов A и B нам нужны два канала / fifos: pipeAB и pipeBA.

Процесс A записывает в pipeAB, а B читает из pipeAB.

Процесс B записывает в pipeBA, а A читает из pipeBA

Если вы хотите использовать сокет, вы можете сделать сокет PF_UNIX (он же AF_UNIX).См. man 7 unix и man 2 socketpair.

. Или вы можете создать полноценный сокет AF_INET с хостом, установленным на localhost, с некоторым фиксированным номером порта.

В качестве упражнения [для вас]рассмотрите возможность сделать это несколькими способами.Таким образом, опция argv, такая как -Tp для двойных труб, -Tu для AF_UNIX и -Ts для AF_INET и т. Д. Только инициализация будет другой.В противном случае протокол был бы почти идентичен.

Для сокетов AF_UNIX, если клиент и сервер - разные программы, может быть проще создать файл типа сокета в файловой системе.Это можно сделать, заполнив struct sockaddr_un «именем файла», а затем используя bind после вызова socket.См. https://www.ibm.com/support/knowledgecenter/en/SSB23S_1.1.0.13/gtpc1/unixsock.html для примера

...