我使用POSIX命名的管道(fifos)从一个或多个线程发送记录给另一个线程读取(只有一个线程进行读取)。 但是,100条记录中的第83条记录被直接丢弃。 客户端核心调用write,返回值正确地报出了记录的长度(720字节),所以客户端(writer)核心确认了记录的发送,但是在gdb调试模式下切换到阅读器核心,并开启了调度器锁定,我循环读取前面几条记录,然后读取失败--管道中没有记录,尽管客户端(writer)核心确认了写入。
管道的容量是65,536字节(Linux默认情况下)。 我假设每读一条记录,管道的内容就会减少1条记录,所以在第83条记录被丢弃的时候,我在管道中大约有5条之前的记录,也就是3600字节--不足以填满管道。
我是在非阻塞模式下打开管道的,因为当我在阻塞模式下打开管道时,两端都冻结了。 根据man pages at http:/man7.orglinuxman-pagesman7fifo.7.html。,"FIFO必须在两端(读和写)打开,才能传递数据。 一般情况下,打开FIFO会阻塞,直到另一端也被打开。" 我的问题是两端都阻塞了,不会再往前走。 还说:"在Linux下,打开一个FIFO进行读写,在阻塞和非阻塞模式下都会成功。 POSIX则没有定义这个行为。"
两端的代码很简单。
int64_t fifo_write(int fd, const void *buf, size_t count) {
int status_write = write(fd, buf, count);
return status_write; }
int64_t fifo_read(int fd, void *buf, size_t count) {
int status_read = read(fd, buf, count);
return status_read; }
从我的NASM程序中调用C函数
mov rdi,[fifo_read_fd]
lea rsi,[fifo_buffer]
mov rdx,720
call fifo_read wrt ..plt
mov rdi,[fifo_write_fd]
mov rsi,[rbp-24]
mov rdx,720 ; bytes
push r11
push rcx
call fifo_write wrt ..plt
pop rcx
pop r11
我的问题是:
是什么原因导致了记录的丢失? 看起来不像是管道容量的问题,除非管道在读取每条记录时没有清空--即使是全部83条记录也需要59760个字节,低于Linux中65K的管道容量。 这可能是由于非阻塞模式,但如果管道没有满,就没有理由阻塞。
我如何在阻塞模式下打开两端(考虑到两端都冻结,各自等待另一个),阻塞模式会有什么问题吗?
我可以用读写模式打开两端,因为我的代码只在一端从一个或多个线程写上,而在另一端从1个线程(只)读上。 虽然 "POSIX没有定义这种行为",但在这种情况下,有什么理由不以读写模式打开两端吗?
我没有带着这个问题发布任何其他的代码(除了如上所述),因为我只是在寻找在我描述的情况下处理掉落记录问题的最佳方法。
你有多个写手使用一个fifo发送720字节的消息。POSIX只要求写入的 PIPE_BUF
(通常为512字节)是原子的。这意味着,较长的写入可能会被其他线程的写入交错而被破坏。
无论 PIPE_BUF
大小,管道是流,它们没有消息的概念,这意味着你需要自己对消息进行划分,而你的代码并没有做到这一点。换句话说,当有多个写入者时,你的阅读器代码不可能恢复各个消息。
你可能喜欢使用一个 Unix数据报插口 代替。每个进入Unix数据报套接字的消息都是一个原子消息,它在一次系统调用中被完整地写入和读取 (sendto
和 recvfrom
).