Вывод strace из этой команды показывает, что полные 12288 (0x3000) байтов записываются за один раз:
$ strace python3 -c 'print("A"*0x3000)' | ./test
...
write(1, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 12288) = 12288
...
Использование strace в команде ./test
на другом конце канала показывает, чтосчитывается только 4096 (0x1000) байтов:
$ python3 -c 'print("A"*0x3000)' | strace ./test
...
read(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 12288) = 4096
...
Если стандартный ввод для ./test
происходит из input.bin
(который содержит 12288 'A, за которыми следует новая строка), strace показывает, что 6144 (0x1800)читаются байты:
$ strace ./test < input.bin
...
read(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 12288) = 6144
...
Я думаю, что объяснение можно найти, только углубившись в код ядра для чтения из канала в функции pipe_read
в "fs / pipe.c".Внутренне канал содержит циклический массив struct pipe_buffer
, каждый из которых содержит указатель на страницу данных.Функция pipe_read
проходит через эти буферы, копируя содержимое для пользователя до тех пор, пока все запрошенные данные не будут прочитаны, в канале ничего не останется (после ожидания большего количества данных от устройства записи в режиме блокировки) или не произойдет ошибка, когдакопирование данных пользователю.Причина, по которой возвращается всего 4096 байт вместо 6144 при возникновении сбоя, по-видимому, связана с этим битом кода:
written = copy_page_to_iter(buf->page, buf->offset, chars, to);
if (unlikely(written < chars)) {
if (!ret)
ret = -EFAULT;
break;
}
ret += chars;
buf->offset += chars;
buf->len -= chars;
(приведенный выше фрагмент взят из версии ядра ядра Linux 4.19.)
Здесь chars
- это объем, который нужно скопировать из текущего буфера канала, written
- это объем, который был фактически скопирован, а ret
- это возвращаемое значение функции, которое обычно является числом прочитанных байтов.,Начальное значение ret
равно 0. written
обычно устанавливается на chars
, если только не произойдет ошибка адреса, в этом случае она будет меньше chars
.
Ошибка возникает при копированиивторая страница из трубы.Первая страница была успешно скопирована, поэтому ret
будет 4096. При копировании второй страницы в буфере пользователя возникает ошибка адреса после 6144 - 4096 = 2048 (0x800) байтов, поэтому достигается оператор break;
для разрывацикла до достижения оператора ret += chars;
.ret
не установлено на -EFAULT
, потому что оно ненулевое.Однако частичное копирование 2048 байтов со второй страницы канала в пользовательский буфер было проигнорировано, поскольку указывается возвращаемое значение функции pipe_buf
.(Пропущенные данные не удаляются из канала. Позиция в канале перемещается только после успешного копирования, поэтому последующий вызов pipe_read
будет продолжен с той же точки.)
Обратите внимание, что хотяread
вернул только 4096, похоже, что он фактически скопировал 6144 байта в пользовательский буфер.Это может быть подтверждено проверкой содержимого буфера с p + 4096
и далее после возврата из вызова read
.
Я уверен, что соответствующая часть функции pipe_read
может быть переписана для правильного учетачастично скопированная часть, но я не знаю, считают ли разработчики ядра ядра ошибкой, которую стоит исправить.Например, исправление может заменить приведенный выше код чем-то вроде следующего (непроверенного) кода:
written = copy_page_to_iter(buf->page, buf->offset, chars, to);
ret += written;
buf->offset += written;
buf->len -= written;
if (unlikely(written < chars)) {
if (!ret)
ret = -EFAULT;
break;
}
Я почти уверен, что вашей программе ./test
удастся прочитать 6144 байта из канала с этимизменить код ядра.