在最大尺寸

时间:2018-05-01 13:11:10

标签: c linux pipe

我尝试通过一次写入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;
}

1 个答案:

答案 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