【问题标题】:Unable to synchronize blocking read and write on FIFO无法同步 FIFO 上的阻塞读写
【发布时间】:2011-05-21 13:41:33
【问题描述】:

我正在制作一个非常简单的程序,它由客户端使用的一个小 API 和一个服务器进程组成。客户端进程和服务器进程通过一对 FIFO 进行通信(一个与对服务器的请求,一个与来自服务器的响应)。

  • 服务器通过阻塞读取(O_RDWR 标志)继续读取请求 FIFO
  • 客户端将请求写入请求 FIFO(O_WRONLY 标志)
  • 服务器读取请求,对其进行处理并将响应写入响应 FIFO(O_WRONLY 标志)
  • 客户端读取响应(O_RDONLY 标志):如果服务器没有传送任何数据(我的意思是未定义的长缓冲区),那么写入 FIFO 的响应就足够了,工作就完成了
  • 否则...
  • 客户端读取到服务器将要发送数据的响应,因此它再次打开响应 FIFO(O_RDONLY 标志)
  • 服务器使用(O_WRONLY 标志)写入数据

在客户端读取另一端之前,最后一次写入似乎不会阻塞服务器进程:为什么?我错过了什么? 为了实现我的目标,我必须在 write 调用之前放置一个 sleep(1) ,但这仅适用于对服务器的某种请求:我该如何帮助您帮助我?

服务器代码

                /* until here everything is ok: client read the response and waits for the buffer */
                sleep(1);
                /* open the FIFO and send the buffer */
                if((fifofrom = open(FIFOFROMMMBOXD, O_WRONLY)) == -1)   logMmboxd("error in opening FIFOFROM again for the buffer\n", 1);
                else                                                    logMmboxd("opened FIFOFROM again for the buffer\n", 0);

                if((write(fifofrom, mails, sizeof(mmbox_mail_complete)*m)) != sizeof(mmbox_mail_complete)*m)   logMmboxd("error in writing FIFOFROM again for the buffer\n", 1);
                else                                                                                           logMmboxd("written on FIFOFROM again for the buffer\n", 0);      
                close(fifofrom); 

                logMmboxd("messages list definitely sent\n", 0);

客户代码

void lockUp(Request *request, Response *response, void **buffer)
{
int fifofrom, fifoto, lock;     

/* lockto access the FIFOs */
if((lock = open(LOCK, O_RDONLY)) == -1)   logMmboxman("error in opening LOCK\n", 1);
else                                      logMmboxman("opened LOCK\n", 0);

if(flock(lock, LOCK_EX) == -1)            logMmboxman("error in acquiring LOCK\n", 1);              
else                                      logMmboxman("acquired LOCK\n", 0);  

/* open the FIFO and write the request */
if((fifoto = open(FIFOTOMMBOXD, O_WRONLY)) == -1)   logMmboxman("error in opening FIFOTO\n", 1); 
else                                                logMmboxman("opened FIFOTO\n", 0);  

if((write(fifoto, request, sizeof(Request))) != sizeof(Request))   logMmboxman("error in writing FIFOTO\n", 1);
else                                                               logMmboxman("written on FIFOTO\n", 0);
close(fifoto);

/* waiting for response on FIFOFROM */
if((fifofrom = open(FIFOFROMMMBOXD, O_RDONLY)) == -1)   logMmboxman("error in opening FIFOFROM\n", 1);
else                                                    logMmboxman("opened FIFOFROM\n", 0);

if((read(fifofrom, response, sizeof(Response))) != sizeof(Response))   logMmboxman("error in reading FIFOFROM\n", 1);
else                                                                   logMmboxman("read from FIFOFROM\n", 0);
close(fifofrom);

/* if size>0 then the server has to send a buffer of data to me! */
if(response->size)
{
    if((fifofrom = open(FIFOFROMMMBOXD, O_RDONLY)) == -1)   logMmboxman("error in opening FIFOFROM again for the buffer\n", 1);
    else                                                    logMmboxman("opened FIFOFROM again for the buffer\n", 0);

    *buffer = (void*)malloc(response->size);

    if(read(fifofrom, *buffer, response->size) != response->size)   logMmboxman("error in reading FIFOFROM again for the buffer\n", 1);
    else                                                            logMmboxman("read from FIFOFROM again for the buffer\n", 0);
    close(fifofrom);    
}

/* read the response: I release the lock */
if(flock(lock, LOCK_UN) == -1)            logMmboxman("error in releasing LOCK\n", 1);              
else                                      logMmboxman("released LOCK\n", 0);  

return;
}

【问题讨论】:

  • 请张贴代码。根据您的描述无法修复。
  • 好的,我正在发布一些代码 :)
  • 我怀疑对于这个应用程序,使用 UNIX 域套接字而不是 FIFO 会更好。首先,它们是双向的。

标签: c linux


【解决方案1】:

所以 (1) 你关于 FIFO 同步的推理是正确的; (2) linux 搞砸的可能性几乎为零;所以(3)你有一个错误。

很难说,但由于您最初以 O_RDWR 的形式打开服务器 fifo,也许您并没有按照您的想法关闭?也许您正在运行第二个客户端,该客户端已经将该 fifo 作为读取器打开,并且当您以 O_WRONLY 重新打开时它不会阻塞?

无论如何,您真正的问题是您设计的协议。你不需要我可以看到需要锁或保持打开和关闭fifos。你确实需要一个合理的通信协议。

通常是这样的:

(1) 服务器有一个众所周知的 fifo,客户端可以打开和写入。

(2) 客户端传递一个事务代码或其他表明这是初始请求的东西。包括服务器与客户端建立连接的一些方法。这可能是一个完整的路径名,或者只是一个 pid,客户端和服务器都使用预定路径来创建第二个 fifo(例如,都打开“/tmp/myfifo_pid”或类似的)。

(3) 在客户端和服务器打开第二个先进先出后,所有进一步的客户端请求到服务器包括 pid(或其他),以便服务器知道在哪里输出请求。

(4) 完成后,客户端发送一个事务指示,以便服务器可以关闭第二个fifo。客户端也是如此。

(5) 对许多客户重复此操作。

请注意,您需要为消息指定格式。久负盛名的技术是使用长度代码(似乎是您正在尝试的)或某种结尾的 msg 分隔符(例如换行符)。

编辑

可能的消息格式:

长度|转码||数据|

其中长度告诉您有多少后续字节构成整个消息、转码指定和操作(例如,打开我的输出 fifo、关闭它、发送数据等),而数据是适合事务的任何内容。

【讨论】:

  • 鸭 我确实关闭了 FIFO。我使用 O_RDWR 标志,这样我就可以继续循环阻塞读取:如果我只使用 O_RDWR 如果没有进程在另一侧写入,则读取不会阻塞并且处于活动等待状态。不可能:1)我只是在客户端上测试它 2)要访问 FIFO 链,你必须获得适当的锁。此外,FIFO 是命名的,当然众所周知。格式化消息是什么意思?
  • @hysoka44 - 你过于复杂了。最好先让最简单的基本案例(服务器/1 客户端)工作,然后从那里迭代改进。暂时忘记阻塞,永远忘记锁定。专注于设计,检查返回/错误代码,以及当您无法一口气读/写一条消息时会发生什么(例如,查看读/写的返回代码)。基本情况正常工作后,其余部分将自行处理。
  • 我在最简单的情况下进行测试,只有 1 个客户端。不过我会检查错误代码以了解发生了什么,谢谢大家
猜你喜欢
  • 1970-01-01
  • 2013-03-31
  • 2017-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-06
  • 2013-01-16
  • 1970-01-01
相关资源
最近更新 更多