Linux pipe():从管道读取并不总能解锁写入器
Linux pipe(): Reading from a pipe doesn't always unblock writers
我在 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 字节后才会解除阻塞...为什么????
通常,在 read
成功 X 字节后,管道中应该有 X 字节的 space 可用,因此 write
应该能够写入 X字节,不是吗?
我怎样才能拥有 "read 1 byte, write 1 byte, etc" 而不是 "read 1 byte, read 1, read 10, read 2000, ...(until 4096 byte read), write 4096" 的行为?
为什么它不像你想的那样工作
所以基本上我的理解是你的管道与某种内核缓冲区链表相关联。只有当其中一个缓冲区被清空时,等待写入管道的进程才会被唤醒。碰巧在您的情况下,这些缓冲区的大小为 4K。
参见:http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/pipe.c?id=HEAD
具体行:281 Where the test on the buffer size is done and line: 287 唤醒其他进程的决定已完成。
管道缓冲区的大小确实取决于内存页面大小,参见man fcntl
F_SETPIPE_SZ(整数;自 Linux 2.6.35 起)
Change the capacity of the pipe referred to by fd to be at least arg bytes.
An unprivileged process can adjust the pipe capacity to any value between
the system page size and the limit defined in /proc/sys/fs/pipe-max-size
(see proc(5)). Attempts to set the pipe capacity below the page size are
silently rounded up to the page size. Attempts by an unprivileged process to
set the pipe capacity above the limit in /proc/sys/fs/pipe-max-size yield
the error EPERM; a privileged process (CAP_SYS_RESOURCE) can override the
limit. When allocating the buffer for the pipe, the kernel may use a
capacity larger than arg, if that is convenient for the implementation. The
F_GETPIPE_SZ operation returns the actual size used. Attempting to set the
pipe capacity smaller than the amount of buffer space currently used to
store data produces the error EBUSY.
如何让它发挥作用
您尝试实现的模式是经典。但它被用来绕过。人们从空管开始。等待事件的进程,read
空管道。进程想要发出事件信号,将单个字节写入管道。
我想我在 Boost.Asio 中看到了,但我懒得找到正确的参考。
Pipe 使用 4kB 页面作为缓冲区,写入会被阻塞,直到有一个空页面可供写入,然后再阻塞,直到它再次变满。 fjardon's 中对其进行了详细描述。如果您想使用管道发送信号,您正在寻找相反的情况。
#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;
}
我在 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 字节后才会解除阻塞...为什么????
通常,在 read
成功 X 字节后,管道中应该有 X 字节的 space 可用,因此 write
应该能够写入 X字节,不是吗?
我怎样才能拥有 "read 1 byte, write 1 byte, etc" 而不是 "read 1 byte, read 1, read 10, read 2000, ...(until 4096 byte read), write 4096" 的行为?
为什么它不像你想的那样工作
所以基本上我的理解是你的管道与某种内核缓冲区链表相关联。只有当其中一个缓冲区被清空时,等待写入管道的进程才会被唤醒。碰巧在您的情况下,这些缓冲区的大小为 4K。
参见:http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/pipe.c?id=HEAD
具体行:281 Where the test on the buffer size is done and line: 287 唤醒其他进程的决定已完成。
管道缓冲区的大小确实取决于内存页面大小,参见man fcntl
F_SETPIPE_SZ(整数;自 Linux 2.6.35 起)
Change the capacity of the pipe referred to by fd to be at least arg bytes. An unprivileged process can adjust the pipe capacity to any value between the system page size and the limit defined in /proc/sys/fs/pipe-max-size (see proc(5)). Attempts to set the pipe capacity below the page size are silently rounded up to the page size. Attempts by an unprivileged process to set the pipe capacity above the limit in /proc/sys/fs/pipe-max-size yield the error EPERM; a privileged process (CAP_SYS_RESOURCE) can override the limit. When allocating the buffer for the pipe, the kernel may use a capacity larger than arg, if that is convenient for the implementation. The F_GETPIPE_SZ operation returns the actual size used. Attempting to set the pipe capacity smaller than the amount of buffer space currently used to store data produces the error EBUSY.
如何让它发挥作用
您尝试实现的模式是经典。但它被用来绕过。人们从空管开始。等待事件的进程,read
空管道。进程想要发出事件信号,将单个字节写入管道。
我想我在 Boost.Asio 中看到了,但我懒得找到正确的参考。
Pipe 使用 4kB 页面作为缓冲区,写入会被阻塞,直到有一个空页面可供写入,然后再阻塞,直到它再次变满。 fjardon's
#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;
}