我有一种情况需要检查一个fifo的另一面是否已打开它,但是我不能使用open,否则程序将开始做的事情。
为什么我必须这样做:我有一个启动服务器程序(由我创建)的程序(监视器)。监视器使用此fifo进行通信,因为在服务器已启动时可以关闭/重新打开监视器。
问题是当监视器启动服务器时:在这种情况下,我必须以某种方式等待创建fifos,然后打开它们。 实际上我在显示器上使用了一段时间来检查何时创建了fifos,但是以这种方式打开了服务器可以执行的之前的(即使mkfifo之后的指令实际上是开放!!!)。
你可能会说我在显示器上以错误的顺序打开了fifos(我在读取fifo之前打开了写入FIFO(WRONLY)),问题是我无法恢复此命令,因为它是必需的服务器将在打开(RDONLY)fifo上等待客户端。
有关如何避免这种竞争条件的任何建议? 在检查是否创建了fifos之后,我实际上正在监视器中使用睡眠,这显然解决了问题,但我认为绝对不正确。
感谢所有人
修改1:
这就是目前的工作方式
服务器
mkfifo(fifo1)
mkfifo(fifo2)
open(fifo1 O_RDONLY)
open(fifo2 O_WRONLY)
监视器
while (fifo1 doesn't exists && fifo2 doesn't exists);
open(fifo1 O_WRONLY)
open(fifo2 O_RDONLY)
我认为竞争条件现在非常明确,重要的是要注意到fifos正在阻塞(只有RDONLY阻塞,即使另一方没有任何人也不会阻止=> 这是unix行为,不是我设计的)。
编辑2:
竞争条件发生在第一个十五开放水平。 我必须在显示器执行之前在服务器上打开第一个fifo。
答案 0 :(得分:1)
您可能希望使用sem_open()
的命名信号量,该信号量对文件系统级别的每个程序都是可见的,以便同步这两个程序。基本上你的监视程序将等待锁定的信号量,直到服务器增加它。此时,所有的fifo都将被初始化,你可以使用你的监视器移动foward,其中fifo处于已知状态。
确保在O_CREAT
的初始调用中使用O_EXCL
和sem_open()
标志,以便创建信号量是原子的。例如,如果监视器和服务器程序尚不存在,它将尝试在启动时创建信号量...如果它存在,则调用将失败,这意味着监视器或服务器,但不是两个程序,获得了创建信号量并初始化它的权利。当服务器初始化fifo时,监视器等待信号量...一旦fifo初始化,服务器释放信号量,然后监视器就能继续。
这是我想象的过程......我相信它应该有效地解决你的竞争条件:
在监视器中:
fifo1
进行书写(非阻止)fifo2
进行阅读(阻止服务器打开fifo2
进行写入)在服务器中:
fifo1
(阻止显示器打开它进行书写)fifo2
(非阻止)所以基本上你的监视器无法继续,直到有一个“已知状态”,其中所有内容都已正确初始化...服务器通过命名的信号量向监视器指示状态。
答案 1 :(得分:1)
如果按正确的顺序执行open(),则没有竞争条件。 (唯一可能的比赛将是第三个过程干扰相同的fifos)从精细的手册:
“但是,它必须在两端同时打开才能继续对其进行任何输入或输出操作。打开FIFO以便正常读取阻塞,直到某个其他进程打开相同的FIFO 写作,反之亦然。“
这意味着订购
{进程1:开放(fifo1,RO); process2:open(fifo1,WO); }
...
{过程1:开放(fifo2,WO); process2:open(fifo2,RO); }
将永远成功(假设没有进程饥饿)每个fifo的操作顺序并不重要;对于fifo1,process1或process2可以先行(并且将被阻止,直到另一方成功)。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define FIFO1 "fifo1"
#define FIFO2 "fifo2"
void do_master(void);
void do_slave(void);
void randsleep(unsigned max);
/************************************/
void do_master(void)
{
int in,out, rc;
char buff[20];
randsleep(5);
mkfifo(FIFO1, 0644);
randsleep(7);
mkfifo(FIFO2, 0644);
out = open(FIFO2, O_WRONLY);
if (out == -1) {
fprintf(stderr, "[Master]: failed opening output\n" );
return;
}
fprintf(stderr, "[Master]: opened output\n" );
in = open(FIFO1, O_RDONLY);
if (in == -1) {
fprintf(stderr, "[Master]: failed opening input\n" );
close(out);
return;
}
fprintf(stderr, "[Master]: opened input\n" );
rc = write( out, "M2S\n\0" , 5);
fprintf(stderr, "[Master]: wrote %d\n", rc );
rc = read( in, buff , sizeof buff);
fprintf(stderr, "[Master]: read %d: %s\n", rc, buff );
unlink(FIFO1);
unlink(FIFO2);
}
/***********************************/
void do_slave(void)
{
int in,out, rc;
unsigned iter=0;
char buff[20];
loop1:
in = open(FIFO2, O_RDONLY);
if (in == -1) {
fprintf(stderr, "[Slave%u]: failed opening input\n", ++iter );
randsleep(2);
goto loop1;
}
fprintf(stderr, "[Slave]: opened input\n" );
loop2:
out = open(FIFO1, O_WRONLY);
if (out == -1) {
fprintf(stderr, "[Slave%u]: failed opening output\n", ++iter );
randsleep(3);
goto loop2;
}
fprintf(stderr, "[Slave]: opened output\n" );
rc = write( out, "S2M\n\0" , 5);
fprintf(stderr, "[Slave]: wrote %d\n", rc );
rc = read( in, buff , sizeof buff);
fprintf(stderr, "[Slave]: read %d:%s\n", rc, buff );
}
/*************************************/
void randsleep(unsigned max)
{
unsigned val;
val = rand();
val %= max;
sleep(val);
return;
}
/*************************************/
int main(void)
{
int rc;
switch (rc=fork()) {
case -1: exit(1); break;
case 0: do_slave(); break;
default: do_master(); break;
}
exit (0);
}
答案 2 :(得分:0)
考虑使用类型为SOCK_STREAM
的Unix域套接字。服务器将bind
其套接字转换为文件系统中的名称。每个客户端都有自己的与服务器的双向连接。
答案 3 :(得分:0)
至少我发现的'直到现在让我明白没有办法检测是否打开了一个fifo ,除非你打开它。
修改1:
正如杰森所说,有两种方式(但我的作业都不允许):
1)*您可以通过/ proc / PID / fd进行搜索(用数字进程ID替换PID)以查看哪些客户端进程已经打开了您的FIFO * 2)另一种选择是在FIFO上调用热熔器
然而,第一个需要老师不想要的东西:在proc / PID / fd里面观察。我听到的第二个需要root权限,这也是我不能做的事情。希望这将有助于将来的其他人。