fifo: разблокировать ожидающие открытые вызовы

Я использую FIFO (именованный канал) для IPC. Теперь обработайте вызовы A

  • mkfifo(path)
  • open(path)

Естественно, open() будет блокироваться до тех пор, пока файл не будет записан процессом B. Теперь мне нужен способ аннулировать FIFO. Поэтому я звоню

  • unlink(path)

Теперь я ожидал, что любой блокирующий вызов open вернется, но этого не происходит, и мой процесс зависает на неопределенный срок.

Как я могу разблокировать вызов open, когда FIFO не связан? Должен ли я прибегать к O_NONBLOCK?

PS: я попробовал предложенный подход записи/отключения/закрытия безрезультатно. Вызов open немедленно блокируется.

void invalidate() {
    int fd = open(path, O_WRONLY)
    unlink(path)
    close(fd)
}

я думаю проблема в том

FIFO должен быть открыт на обоих концах (чтение и запись) перед передачей данных. Обычно открытие блоков FIFO происходит до тех пор, пока другой конец также не будет открыт.

Однако invalidate должен работать, не зная, открыт ли в данный момент FIFO для чтения или нет.


person Erik Aigner    schedule 10.06.2020    source источник
comment
Удаление файла не влияет на процессы, которые уже ссылаются на файл.   -  person Barmar    schedule 10.06.2020
comment
Я не думаю, что есть способ сделать то, что вы хотите.   -  person Barmar    schedule 10.06.2020
comment
Можно использовать неблокировку, и программе придется периодически проверять, существует ли еще ссылка.   -  person Barmar    schedule 10.06.2020
comment
Я пробовал неблокировать, но это приносит с собой другие проблемы, а именно, я не могу различить нулевые байты write, обозначающие конец потока.   -  person Erik Aigner    schedule 11.06.2020
comment
Если читать нечего, read() возвращает -1 и устанавливает ERRNO = EWOULDBLOCK.   -  person Barmar    schedule 11.06.2020
comment
Я попробовал это, но получил очень противоречивые результаты для EWOULDBLOCK. Иногда он устанавливал EWOULDBLOCK и возвращал 0, в большинстве случаев он даже не устанавливал EWOULDBLOCK... не знаю, что здесь происходит.   -  person Erik Aigner    schedule 11.06.2020
comment
Ну, так оно и должно работать. Что-то явно не так с тем, как вы кодируете вещи, потому что ответ ниже также должен работать и не работает для вас.   -  person Barmar    schedule 11.06.2020
comment
Ну, я не вижу, что может пойти не так, просто вызывая mkfifo/open/read по порядку   -  person Erik Aigner    schedule 11.06.2020
comment
Вызов open(path, O_RDONLY) должен вернуться, как только другой процесс выполнит open(path, O_WRONLY), или наоборот.   -  person Barmar    schedule 11.06.2020
comment
Как только я делаю open при аннулировании, он тоже зависает.   -  person Erik Aigner    schedule 11.06.2020
comment
Вы открываете в петле? Предыдущий должен вернуться, потом зависнет следующий.   -  person Barmar    schedule 11.06.2020
comment
Я не понимаю вашего последнего комментария. Первый открытый должен висеть при запуске. Затем другой процесс открывает FIFO в другом направлении, и первый должен разблокироваться.   -  person Barmar    schedule 11.06.2020
comment
Это работает, если вы НЕ пытаетесь аннулировать его?   -  person Barmar    schedule 11.06.2020
comment
Кажется, я знаю, в чем проблема. Чтобы этот подход работал, мне нужно знать, открыт ли FIFO в настоящее время для чтения кем-то другим. FIFO должен быть открыт на обоих концах (чтение и запись) перед передачей данных. Обычно открытие блоков FIFO происходит до тех пор, пока другой конец также не будет открыт.   -  person Erik Aigner    schedule 12.06.2020
comment
Не факт, что FIFO должен быть открыт во время аннулирования, он должен работать в обоих случаях. Обновил мой вопрос.   -  person Erik Aigner    schedule 12.06.2020


Ответы (3)


Инвалидатор должен открывать FIFO в режиме неблокирующей записи. Таким образом, его вызов open() не будет зависать, если его не ждут читатели. Затем он должен немедленно закрыть FIFO.

Любые процессы, ожидавшие открытия только для чтения, вернутся при открытии FIFO. Тогда они сразу же прочитают EOF.

void invalidate() {
    int fd = open(path, O_WRONLY | O_NONBLOCK);
    unlink(path);
    close(fd);
}
person Barmar    schedule 11.06.2020
comment
Я думаю, что понял это сейчас. Мне нужна была комбинация open с O_NONBLOCK и обработка EINTR с другой стороны. - person Erik Aigner; 12.06.2020
comment
Если другая сторона открыта для записи, закрытие стороны чтения приведет к получению сигнала SIGPIPE при попытке записи. - person Barmar; 12.06.2020
comment
Я сделал небольшую модификацию, используя O_RDWR, чтобы также разблокировать любые ожидающие вызовы open(fd, O_WRONLY). - person Erik Aigner; 12.06.2020

Откройте FIFO для записи, unlink его, затем закройте. Это сделает процесс A open успешным, и результирующий FD сразу же окажется в EOF. (Кстати, unlink не нужен, если вы просто хотите вернуть open, но все же это хорошая идея для очистки.)

person Joseph Sible-Reinstate Monica    schedule 11.06.2020
comment
Пробовал писать и закрывать, но open() все равно висит. - person Erik Aigner; 11.06.2020
comment
@ErikAigner Это работает для меня. Пожалуйста, отредактируйте код, который вы пробовали, в свой вопрос (как программы, которая блокирует, так и той, которая должна ее разблокировать). - person Joseph Sible-Reinstate Monica; 11.06.2020
comment
Обновил ответ. - person Erik Aigner; 11.06.2020
comment
@ErikAigner Вам вообще не нужно write этого делать. Вам просто нужно open с помощью O_WRONLY. - person Joseph Sible-Reinstate Monica; 11.06.2020
comment
Ах, да, моя вина... Я действительно сделал открытие здесь. Написал бит по моей (неисправной) памяти. Фиксированный. - person Erik Aigner; 11.06.2020
comment
@ErikAigner Для меня, как только этот open выполняется, первый процесс разблокируется. Кроме того, я не использую O_NONBLOCK. И что вам возвращает open во втором процессе? - person Joseph Sible-Reinstate Monica; 11.06.2020

В Linux вы можете (непереносимо) открыть FIFO в режиме O_RDWR. Когда вы делаете и удерживаете FIFO, другие открыватели не будут заблокированы в своих вызовах open (независимо от того, являются ли они открытыми вызовами только для чтения или только для записи). Затем вы можете отменить связь с FIFO, сохраняя при этом дескриптор файла O_RDWR.

Пример кода:

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
int main(int C, char **V)
{
    int fd;
    if(0>(fd=open(V[1], O_RDWR))) return perror("open"),1;
    if(0>unlink(V[1])) return perror("unlink"),1;
}
person PSkocik    schedule 11.06.2020
comment
О, я прочитал это немного поздно, извините, тем временем придумал O_RDWR трюк ^^ - person Erik Aigner; 12.06.2020
comment
@ErikAigner Это частично работает и на MacOS. По крайней мере часть O_RDWR. Но там, похоже, разблокируется только один открыватель, ожидающий этого fifo. В Linux он разблокирует их все (например, скажем, у вас есть несколько cat fifo или cat </dev/null > fifo). На Cygwin я получаю EPERM для O_RDWR. Я думаю, можно обойти с двумя потоками. - person PSkocik; 12.06.2020