我在Linux下使用管道时遇到问题。我想填充一个管道,以进一步写入呼叫阻止。另一个进程应该能够从管道中读取一些允许其他进程写入的字符。
示例代码:
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int pipefd[2];
int size = 65535;
int total = 0;
// Create the pipe
if(pipe(pipefd) == -1)
{
perror("pipe()");
exit(EXIT_FAILURE);
}
// Fill in (almost full = 65535 (full - 1 byte))
while(total < size)
{
write(pipefd[1], &total, 1);
total++;
}
// Fork
switch(fork())
{
case -1:
perror("fork()");
exit(EXIT_FAILURE);
case 0:
// Close unused read side
close(pipefd[0]);
while(1)
{
// Write only one byte, value not important (here -> total)
int ret = write(pipefd[1], &total, 1);
printf("Write %d bytes\n", ret);
}
default:
// Close unused write side
close(pipefd[1]);
while(1)
{
int nbread;
scanf("%4i", &nbread);
char buf[65535];
// Read number byte asked
int ret = read(pipefd[0], buf, nbread);
printf("Read %d bytes\n", nbread);
}
}
return 0;
}
我不明白下面的行为。该过程最后一次写入,因为我没有完全填充管道,正常。但之后,写入被阻塞(管道已满),任何读取都应该解锁等待的写入调用。
test@pc:~$./pipe
Write 1 bytes
4095
Read 4095 bytes
1
Read 1 bytes
Write 1 bytes
Write 1 bytes
Write 1 bytes
Write 1 bytes
Write 1 bytes
Write 1 bytes
...
相反,只有在读取了4096个字节后,才会解锁写入调用...为什么????
通常,在X字节成功read
后,管道中应该有X个字节的空间,因此write
应该能够写入多达X个字节,不是吗?
我怎么能有“读取1个字节,写1个字节等”的行为,而不是“读取1个字节,读取1,读取10,读取2000,......(直到4096个字节读取),写入4096”?
答案 0 :(得分:3)
基本上我理解的是你的管道与某种内核缓冲区链表相关联。只有在清空其中一个缓冲区时,才会唤醒等待写入管道的进程。碰巧在你的情况下,这些缓冲区大小为4K。
请参阅:http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/pipe.c?id=HEAD
具体行:281在缓冲区大小的测试完成的位置和行:287,其中决定唤醒其他进程。
管道缓冲区的大小确实取决于内存页面大小,请参阅man fcntl
将fd引用的管道容量更改为至少arg字节。 无特权的流程可以将管道容量调整为之间的任何值 系统页面大小和/ proc / sys / fs / pipe-max-size中定义的限制 (见proc(5))。尝试将管道容量设置为低于页面大小的是 默默地向上舍入到页面大小。尝试通过无特权的过程来实现 将管道容量设置为高于/ proc / sys / fs / pipe-max-size yield中的限制 错误EPERM;特权进程(CAP_SYS_RESOURCE)可以覆盖 限制。在为管道分配缓冲区时,内核可能会使用a 容量大于arg,如果方便实现。该 F_GETPIPE_SZ操作返回实际使用的大小。试图设置 管道容量小于当前使用的缓冲区空间量 存储数据会产生错误EBUSY。
您尝试实现的模式是经典。但它是用来解决的。人们从空管开始。处理等待事件,read
空管道。想要发出事件信号的进程,将一个字节写入管道。
我想我在Boost.Asio中看到了这一点,但我懒得找到正确的参考。
答案 1 :(得分:0)
管道使用4kB页面作为缓冲区并且写入被阻止,直到有一个空页面进行写入,然后在它再次满满之前不会阻塞。 fjardon's answer对此进行了详细介绍。如果您想使用管道进行信号传输,那么您正在寻找相反的情况。
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int pipefd[2];
// Create the pipe
if(pipe(pipefd) == -1)
{
perror("pipe()");
exit(EXIT_FAILURE);
}
// Fork
switch(fork())
{
case -1:
perror("fork()");
exit(EXIT_FAILURE);
case 0:
// Close unused write side
close(pipefd[1]);
while(1)
{
char c;
// Read only one byte
int ret = read(pipefd[0], &c, 1);
printf("Woke up\n", ret);
fflush(stdout);
}
default:
// Close unused read side
close(pipefd[0]);
size_t len = 0;;
char *str = NULL;
while(1)
{
int nbread;
char buf[65535];
while (getline(&str, &len, stdin)) {
if (sscanf(str, "%i", &nbread)) break;
};
// Write number byte asked
int ret = write(pipefd[1], buf, nbread);
printf("Written %d bytes\n", ret);
fflush(stdout);
}
}
return 0;
}