Что происходит при записи гигабайт данных в конвейер?

Цикл for не нужен. Попробуйте это:

mkdir `echo file{001..512}`

Как указано в комментариях:

There is no need for command substitution and echo.

Итак,Brace Expansionдостаточно для выполнения задачи:

mkdir file{001..512}

18
31.12.2020, 17:29
1 ответ

tl;dr :в какой-то момент yesбудет заблокирована запись, если данные не читаются на другой стороне. Он не сможет продолжать выполнение до тех пор, пока эти данные не будут прочитаны или не будет получен сигнал, поэтому обычно вам не нужно yesбеспокоиться о записи гигабайтов и гигабайтов данных.


Важно помнить, что канал представляет собой структуру данных FIFO, а не просто чистый поток, который отбрасывает данные, если они не были немедленно прочитаны получателем. То есть, хотя в большинстве случаев может показаться, что это непрерывный поток данных от записывающего приложения к приложению для чтения, для этого требуется промежуточное хранилище, и это промежуточное хранилище имеет конечный размер.*

Если мы посмотрим на канал (7 )справочную страницу , мы можем прочитать следующее о размере этого внутреннего буфера (добавлено выделение):

In Linux versions before 2.6.11, the capacity of a pipe was the same as the system page size (e.g., 4096 bytes on i386). Since Linux 2.6.11, the pipe capacity is 16 pages (i.e., 65,536 bytes in a system with a page size of 4096 bytes). Since Linux 2.6.35, the default pipe capacity is 16 pages, but the capacity can be queried and set using the fcntl(2) F_GETPIPE_SZ and F_SETPIPE_SZ operations.

Предполагая, что вы используете стандартную систему x86 _64, весьма вероятно, что вы используете страницы размером 4 КБ, поэтому верхний предел емкости канала 2^16, вероятно, является правильным, если ни одна из сторон конвейера в какой-то момент не использовала fcntl(F_SETPIPE_SZ). В любом случае принцип остается :промежуточным хранилищем между двумя сторонами трубы конечным и хранится в памяти.

В абстрактном конвейере a | bэто хранилище используется в период между aзаписью некоторых данных и bих фактическим чтением. Предполагая, что ваш makeвызов (и любые дочерние элементы, также связанные с этим каналом путем наследования ), на самом деле не пытаются читать стандартный ввод или делают это очень редко, системный вызов writeиз yesв конечном итоге просто не выйдет yesиз спящего режима, когда пространство в буфере будет исчерпано. Затем yesбудет ожидать пробуждения, либо когда буфер снова станет доступным, либо когда будет получен сигнал. **Все это обрабатывается планировщиком процессов ядра. Вы можете увидеть это в pipe_write(),который является обработчиком write()для каналов:

static ssize_t
pipe_write(struct kiocb *iocb, struct iov_iter *from)
{
    /*... */

    if (pipe_full(pipe->head, pipe->tail, pipe->max_usage))
        wake_next_writer = false;

    if (wake_next_writer)
        wake_up_interruptible_sync_poll(&pipe->wr_wait, EPOLLOUT | EPOLLWRNORM);

    /*... */
}

Когда сторона makeв конце концов завершается, yesбудет отправлено SIGPIPEв результате записи в канал, когда на другом конце ничего не осталось. Затем он — в зависимости от реализации yes— вызовет либо свой собственный обработчик сигналов, либо обработчик сигналов ядра по умолчанию, и завершится.***


*В простых случаях, когда получатель обрабатывает данные примерно с той же скоростью, с которой они записываются, эта передача также может быть нулевой -копией без промежуточного буфера с использованием виртуальной памяти для сопоставления и предоставления доступа к физической странице. из процесса записи, доступного получателю. Однако в случае, который вы описываете, в конечном итоге потребуется использовать конвейерный буфер для хранения непрочитанных данных.

**Также возможно, что запись выполняется с установленным флагом O_NONBLOCKв файловом дескрипторе, который включает неблокирующий режим -. В этом случае вы, вероятно, получите одну незавершенную запись, а затем запись вернет EAGAIN, и приложение должно будет справиться с этим самостоятельно. Скорее всего, он сделает это, приостановив или запустив какой-либо другой код по своему выбору для обработки переполнения канала. В случае каждой современной версии yes, которую я могу найти, и большинства других приложений, описание выше — это то, что происходит, поскольку они не используют O_NONBLOCK.

***Приложение может делать все, что захочет, после полученияSIGPIPE--оно может даже теоретически принять решение не завершаться. Однако все распространенные yesиспользуют обработчик SIGPIPEпо умолчанию, который просто завершает работу без выполнения каких-либо дальнейших инструкций пользовательского пространства.

34
18.03.2021, 22:39

Теги

Похожие вопросы