无法同步 FIFO 上的阻塞读取和写入
我正在制作一个非常简单的程序,它由客户端使用的小型 API 和服务器进程组成。客户端进程和服务器进程通过一对 FIFO 进行通信(一个包含对服务器的请求,另一个包含来自服务器的响应)。
- 服务器继续以阻塞读取方式读取请求 FIFO(O_RDWR 标志)
- 客户端在请求 FIFO 上写入请求(O_WRONLY 标志)
- 服务器读取请求,对其进行处理并将响应写入响应 FIFO(O_WRONLY 标志) )
- 客户端读取响应(O_RDONLY 标志):如果服务器没有传送任何数据(我的意思是未定义的长缓冲区),那么写入 FIFO 的响应就足够了,工作就完成
- ……
- 了 客户端读取服务器将要发送数据的响应,因此它再次打开响应 FIFO(O_RDONLY 标志)
- 服务器使用(O_WRONLY 标志)写入数据
最后一次写入似乎不会阻塞服务器进程,直到客户端正在读取另一边:为什么?我缺少什么? 为了实现我的目标,我必须在写入调用之前放置一个 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;
}
I'm making a very simple program which is composed of a small API used by clients and a server process. Client processes and server process communicate by a couple of FIFO (one with the requests to the server and one with the responses from the server).
- the server keeps on reading the requests FIFO with a blocking read (O_RDWR flag)
- the clients write a request on the requests FIFO (O_WRONLY flag)
- the server read the request, it works on it and write the response on the responses FIFO (O_WRONLY flag)
- the clients read the response (O_RDONLY flag): if the server hasn't to communicate any data (I mean an undefined long buffer), then the response written on the FIFO is enough and the job is done
- else...
- the client reads in the response that the server is going to send data, so it opens the responses FIFO again (O_RDONLY flag)
- server writes data with (O_WRONLY flag)
The last write doesn't seem to block the server process until a client is reading on the other side: why? what am I missing?
In order to achieve my goal I had to put a sleep(1) before the write call but this works only with some kind of requests to the server: how can I help you to help me?
SERVER CODE
/* 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);
CLIENT CODE
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
因此 (1) 您关于 FIFO 同步的推理是正确的; (2) Linux 搞砸的可能性几乎为零;所以(3)你有一个错误。
很难说,但由于您最初将服务器 fifo 作为 O_RDWR 打开,也许您没有关闭您的想法?也许您正在运行第二个客户端,该客户端已经作为读取器打开了该 fifo,并且当您以 O_WRONLY 重新打开时,它不会阻塞?
无论如何,您真正的问题是您设计的协议。我认为您不需要锁或保持打开和关闭 fifo。您确实需要一个合理的通信协议。
通常,这类似于:
(1) 服务器有一个客户端打开并写入的众所周知的 fifo。
(2) 客户端传递事务代码或指示这是初始请求的内容。其中包括服务器建立回客户端连接的一些方法。这可以是整个路径名,也可以只是客户端和服务器使用预先指定的路径来创建第二个 fifo 的 pid(例如,都打开“/tmp/myfifo_pid”或类似的名称)。
(3) 客户之后&服务器打开第二个 fifo,所有向服务器发出的进一步客户端请求都包含 pid(或其他内容),以便服务器知道在哪里输出请求。
(4) 一切完成后,客户端发送一个事务指示,以便服务器可以关闭第二个 fifo。客户端也做同样的事情。
(5) 对任意多个客户端重复此操作。
请注意,您需要指定消息的格式。历史悠久的技术是使用长度代码(似乎是您正在尝试的)或某种消息结束分隔符(例如换行符)。
编辑
可能的消息格式:
长度|转码||数据|
其中 length 告诉您有多少后续字节组成整个消息,转码指定和操作(例如打开我的输出 fifo、关闭它、发送数据等),而 data 是适合事务的任何内容。
So (1) your reasoning about the synchronization of the FIFOs is correct; (2) there is a near zero chance linux is screwing up; so (3) you have a bug.
It's hard to tell but since you are initially opening the server fifo as O_RDWR perhaps you aren't closing what you think? Perhaps you have a second client running that already has that fifo open as a reader and when you reopen as O_WRONLY it doesn't block?
In any case, your real problem is the protocol you have designed. You have no need that I can see to need for locks or to keep open and closing the fifos. You do need a sensible communication protocol.
Typically this is something like:
(1) the server has a well-know fifo that clients open and write to.
(2) clients pass a transaction code or something indicating this is the initial request. Included is some method of the server establishing a connection back to the client. This could be an entire path name or just a pid that both client and server use with a pre-ordained path to create the second fifo (e.g. both open "/tmp/myfifo_pid" or some such).
(3) After the client & server open the second fifo, all further client requests to the server include the pid (or whatever) so the server knows where to output the request.
(4) After all is done, the client sends a transaction indicating such so that the server can close the 2nd fifo. The client does the same.
(5) Repeat for however many clients.
Note that you need to specify a format for your messages. Time honored techniques are to use a length code (seems to be what you are trying) or some kind of end-of-msg delimiter (e.g. newline).
Edit
Possible Message format:
length|trancode||data|
where length tells you how many subsequent bytes comprise the whole message, transcode specifies and operation (e.g. open my output fifo, close it, send data, whatever), and data is whatever is appropriate for the transaction.