从程序中打开FIFO时检测

我有一个情况,我需要检查一个先锋队的另一边是否已经打开它,但是我不能使用一个开放的,否则程序将开始做的东西。

为什么我必须这样做:我有一个启动服务器程序的程序(一个显示器)(都由我创build)。 显示器使用此fifo进行通信,因为在服务器已经启动的情况下可以closures/重新打开显示器。

问题是显示器启动服务器的时候:在这种情况下,我必须等待fifos被创build,然后打开它们。 实际上,我在显示器上使用了一段时间来检查fifo的创build时间,但是这样在服务器执行之前打开fifo(即使mkfifo之后的指令实际上是打开的!)。

你可能会说我在显示器上以错误的顺序打开了fifo(我在打开fifo之前打开了write fifo(WRONLY)),问题是我无法恢复这个顺序,因为它需要服务器将等待客户开放(RDONLY)前端。

任何有关如何避免这种竞争条件的build议? 我实际上在检查fifos是否被创build后在显示器上使用了一个睡眠,这明显地解决了这个问题,但是我认为这是不正确的。

谢谢大家

编辑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) 

我认为现在的比赛条件是非常明确的,重要的是要注意,fifo阻塞(只有RDONLY阻塞,即使没有任何人在另一边WRONLY也不会阻塞=> 这是一个unix行为,不是由我 )。

编辑2:

比赛状况发生在第一个五十开放水平。 监视器之前,我必须打开服务器上的第一个fifo。

您可能希望使用sem_open()命名的信号量,该信号对于文件系统级别的每个程序都是可见的,以便同步这两个程序。 基本上你的监视器程序将等待锁定的信号,直到服务器增加它。 在这一点上,所有的fifo都将被初始化,并且你可以使用已知状态的fifo移动你的显示器。

确保在对sem_open()的初始调用中使用O_CREATO_EXCL标志,以便创建信号量是原子的。 例如,如果监视器和服务器程序不存在,监视器和服务器程序将在启动时尝试创建信号量。如果存在,则调用将失败,即监视器或服务器,但不是这两个程序获得了创建信号量的权利并对其进行了初始化。 然后监视器等待信号量,同时服务器初始化fifo …一旦FIFO被初始化,服务器释放信号量,然后监视器能够继续。

这是我设想的过程…我相信它应该有效地解决你的竞争条件:

在监视器中:

  1. 创建指定的信号量并将其设置为锁定状态
  2. 启动服务器
  3. 等待FIFO在文件系统级别可见
  4. 打开fifo1进行写入(非阻塞)
  5. 打开fifo2进行读取(块直到服务器打开fifo2写入)
  6. 等待信号量(可能有超时),直到服务器解锁,表示现在已经成功打开了两个FIFO。

在服务器上:

  1. 创建FIFO
  2. 打开fifo1 (块直到显示器打开它写入)
  3. 打开fifo2 (非阻塞)
  4. 现在解锁信号量,服务器已经打开了两个FIFO

所以基本上你的显示器不能继续,直到有一个“已知状态”,一切正常初始化…服务器通过指定的信号灯显示状态。

如果按照正确的顺序执行open(),则没有竞争条件。 (唯一可能的比赛将是第三个进程干扰相同的fifos)从精细的手册:

“但是,它必须在两端同时打开,然后才能对其进行任何输入或输出操作,打开一个FIFO用于正常读取块,直到其他进程打开相同的FIFO进行写入,反之亦然。

这意味着订购

{过程1:打开(fifo1,RO); process2:open(fifo1,WO); }

{process 1:open(fifo2,WO); process2:open(fifo2,RO); }

将永远成功(没有过程饥饿)每个先锋队的行动顺序并不重要; 对于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); } 

考虑使用SOCK_STREAM类型的Unix域套接字。 服务器将其套接字bind到文件系统中的名称。 每个客户端都有自己的双向连接到服务器。

至少我发现的“直到现在让我明白, 没有办法检测一个fifo是否被打开 ,除非你打开它。

编辑1:

正如杰森所说,有两种方法(但是我的作业都不允许):

1)*您可以通过/ proc / PID / fd进行搜索(用数字进程ID替换PID),以查看哪些客户端进程已经打开了您的FIFO * 2) 另一种方法是调用FIFO中的fuser

但是,第一个需要教师不需要的东西:在proc / PID / fd内部看。 我听到的第二个需要root权限,这又是我不能做的事情。 希望这将有助于未来的其他人。