我有一个情况,我需要检查一个先锋队的另一边是否已经打开它,但是我不能使用一个开放的,否则程序将开始做的东西。
为什么我必须这样做:我有一个启动服务器程序的程序(一个显示器)(都由我创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_CREAT
和O_EXCL
标志,以便创建信号量是原子的。 例如,如果监视器和服务器程序不存在,监视器和服务器程序将在启动时尝试创建信号量。如果存在,则调用将失败,即监视器或服务器,但不是这两个程序获得了创建信号量的权利并对其进行了初始化。 然后监视器等待信号量,同时服务器初始化fifo …一旦FIFO被初始化,服务器释放信号量,然后监视器能够继续。
这是我设想的过程…我相信它应该有效地解决你的竞争条件:
在监视器中:
fifo1
进行写入(非阻塞) fifo2
进行读取(块直到服务器打开fifo2
写入) 在服务器上:
fifo1
(块直到显示器打开它写入) fifo2
(非阻塞) 所以基本上你的显示器不能继续,直到有一个“已知状态”,一切正常初始化…服务器通过指定的信号灯显示状态。
如果按照正确的顺序执行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权限,这又是我不能做的事情。 希望这将有助于未来的其他人。