именованные блоки при записи до максимального размера - PullRequest
0 голосов
/ 01 мая 2018

Я пытаюсь заполнить именованный канал (созданный mkfifo /tmp/pipe), записывая в него 3 байта за раз, пока функциональные блоки 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 (или 65535, если учесть, что 65536 не кратно 3).

Затем я попытался записать 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 байтов в первом случае, тогда как я могу записать во втором?

EDIT:

Дополнительная информация:

  • Моя операционная система - 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 голосов
/ 29 октября 2018

Это из-за того, что страницы размером 4 КБ заполняются записанными данными в реализации канала в ядре Linux. В частности, ядро ​​добавляет записанные данные на страницу, только если данные полностью помещаются на странице, в противном случае данные помещаются на другую страницу с достаточным количеством свободных байтов.

Если вы пишете по 3 байта за раз, страницы канала не будут заполнены полностью, потому что размер страницы (4096) не кратен 3: ближайшее кратное 4095, поэтому каждая страница заканчивается с 1 "потраченным впустую" байтом. Умножив 4095 на 16, то есть общее количество страниц, вы получите 65520.

Во втором случае использования, когда вы записываете 65520 байтов одновременно, вы полностью заполняете 15 страниц (61440 байтов), плюс оставляете оставшиеся 4080 байтов на последней странице, которая будет иметь 16 доступных байтов для последующие записи: вот почему ваш второй вызов write () с 3 байтами выполняется успешно без блокировки.

Для получения полной информации о реализации канала Linux см. https://elixir.bootlin.com/linux/latest/source/fs/pipe.c.

...