我尝试通过一次写入3个字节来填充命名管道(由mkfifo /tmp/pipe
创建),直到write()
功能块为止。
在我的系统上,管道似乎限制为16页4096字节。因此管道可以包含65536个字节。
我使用以下C代码执行此操作:
int main ()
{
pid_t child;
child = fork ();
if (child == 0)
{
ssize_t ret;
ssize_t total = 0;
unsigned char *datat = malloc (65536);
assert (datat != NULL);
int fd = open ("/tmp/pipe", O_WRONLY);
assert (fd != -1);
while (1)
{
printf ("Trying writting\n");
ret = write (fd, datat, 3);
assert (ret != -1);
total += ret;
printf ("write : %ld.\n", total);
}
}
else
{
int fd = open ("/tmp/pipe", O_RDONLY);
assert (fd != -1);
while (1); //prevent closing the pipe.
}
return 0;
}
通过这种方式,我成功填充管道直到65520字节。我不明白为什么65520而不是65536(如果我们认为65536不是3的倍数,则为65535)。
然后我尝试写入65520个字节,之后写入3个字节:
int
main (int argc, char *argv[])
{
pid_t child;
child = fork ();
if (child == 0)
{
ssize_t ret;
ssize_t total = 0;
unsigned char *datat = malloc (65536);
assert (datat != NULL);
int fd = open ("/tmp/pipe", O_WRONLY);
assert (fd != -1);
while(1)
{
printf ("Trying writting\n");
ret = write (fd, datat, 65520);
assert (ret != -1);
total += ret;
printf ("Trying writting\n");
ret = write (fd, datat, 3);
assert (ret != -1);
total += ret;
printf ("write : %ld.\n", total);
}
}
else
{
int fd = open ("/tmp/pipe", O_RDONLY);
assert (fd != -1);
while (1); //prevent closing the pipe.
}
return 0;
}
我期望第二次写入阻止,但事实并非如此,我写了65523字节。
问题是:为什么我不能在第一种情况下写入超过65520个字节,而在第二种情况下我可以写入?
修改
更多信息:
我的操作系统是Linux archlinux 4.16.5-1-ARCH
man 7 pipe
提供有关管道大小(等于65536字节)的信息,并由fcntl确认:
int
main (int argc, char *argv[])
{
int fd = open ("/tmp/pipe", O_WRONLY);
printf ("MAX : %d\n", fcntl (fd, F_GETPIPE_SZ));
return 0;
}
答案 0 :(得分:2)
这是因为在Linux内核的管道实现中,用写数据填充4KB页的方式。更具体地说,仅当数据完全适合页面时,内核才会将写入的数据追加到页面,否则将数据放入具有足够可用字节的另一页面中。
如果您一次写入3个字节,则管道页面将无法完全填充,因为页面大小(4096)并非3的倍数:最接近的倍数是4095,因此每页将结束加上1个“浪费”的字节。将4095与16(即总页数)相乘,得出65520。
在第二个用例中,一次写入65520个字节时,您将完全填充15个页面(61440个字节),再将剩余的4080个字节放在最后一页中,这16个字节仍可用于随后的写操作:这就是为什么第二个3字节的write()调用成功而没有阻塞的原因。
有关Linux管道实现的完整详细信息,请参见https://elixir.bootlin.com/linux/latest/source/fs/pipe.c。