Именованные блоки конвейера при записи до максимального размера

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

Обновлено:

Больше информации :

  • Моя операционная система - 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;
}

Это две ключевые недостающие информации в вашем вопросе, который вы не доказали нам, что «В моей системе канал, кажется, ограничен 16 страницами по 4096 байтов». плюс, вы не показали нам, как вы создаете "/ tmp / pipe". А с классическим pipe() пробовали?

Stargateur 01.05.2018 15:29

"вы не показали нам, как вы создаете" / tmp / pipe ". Я сделал: я использовал команду mkfifo." В моей системе канал, кажется, ограничен 16 страницами по 4096 байт. ": Я отредактировал вопрос для что.

sebastien dontneedtoknowthat 01.05.2018 16:40

Также у меня такое же поведение с классической (безымянной) трубой.

sebastien dontneedtoknowthat 01.05.2018 16:42
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
3
854
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это связано с тем, как страницы размером 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.

Другие вопросы по теме