#operating-system #system-calls
Вопрос:
Я изучаю курс по операционной системе и на слайде 32: https://people.eecs.berkeley.edu/~кубитрон/курсы/cs162-S19/sp19/статика/лекции/3.pdf
профессор кратко сказал fread
и fwrite
реализовал буфер пользовательского пространства, что более эффективно, чем прямой вызов системных функций read
/ write
и может сэкономить доступ к диску, но не объяснил, почему.
Представьте себе эти два сценария: нам нужно прочитать/записать 16 байт, пользовательский буфер 4 байта, сценарий один использует fread
/ fwrite
, сценарий два направляет использование read
/ write
, который обрабатывает один байт каждый раз
Мои вопросы таковы:
- Поскольку
fread
вызовыread
ниже, сколькоread
вызовов функций будет вызвано соответственно? - Является ли передача данных, будь то один байт или 1 МБ, между буфером пространства пользователя и буфером пространства ядра, выполняемой ядром, и во время передачи не задействован переключатель режима пользователя/ядра?
- Сколько обращений к диску выполняется соответственно? Разве буфер ядра не вступит в игру во время второго сценария?
read
Функцияssize_t read(int fd, void *buf, size_t count)
также имеет параметры буфера и количества, могут ли они заменить роль буфера пользовательского пространства?
Ответ №1:
- Поскольку fread вызывает чтение снизу, сколько вызовов функций чтения будет вызвано соответственно?
Поскольку fread()
в основном это просто заполнение буфера (в пользовательском пространстве, вероятно , в общей библиотеке) перед read()
тем, как «в лучшем случае количество read()
системных вызовов» будет зависеть от размера буфера.
Например; с буфером в 8 КБ; если вы читаете 6 байтов одним fread()
или если вы читаете 6 отдельных байтов с 6 fread()
вызовами; затем read()
, вероятно, будет вызван один раз (чтобы получить до 8 КБ данных в буфер).
Однако; read()
может возвращать меньше данных, чем было запрошено (и это очень часто встречается в некоторых случаях — например stdin
, если пользователь вводит недостаточно быстро). Это означает, что fread()
он может read()
попытаться заполнить свой буфер, но read()
может прочитать только несколько байтов; поэтому fread()
ему нужно позвонить read()
позже, когда ему понадобится больше данных в своем буфере. В худшем случае (когда read()
каждый раз возвращается только 1 байт) чтение 6 байтов с одним fread()
может привести read()
к вызову 6 раз.
- Является ли передача данных, будь то один байт или 1 МБ, между буфером пространства пользователя и буфером пространства ядра, выполняемой ядром, и во время передачи не задействован переключатель режима пользователя/ядра?
Часто read()
(в стандартной библиотеке C) вызывает какую sys_read()
-либо функцию»», предоставляемую ядром. В этом случае при вызове «» происходит переключение на ядро sys_read()
, затем ядро делает все необходимое для получения и передачи данных, затем происходит один переход обратно из ядра в пользовательское пространство.
Однако; ничто не говорит о том, как должно работать ядро. Например, ядро может предоставлять только « sys_mmap()
» (и не предоставлять никаких» sys_read()
«), а read()
(в стандартной библиотеке C) может использовать» sys_mmap()
«. Для другого примера; с экзо-ядром файловые системы могут быть реализованы как общие библиотеки (с «кэшем файловой системы» в общей памяти), поэтому read()
работа библиотеки C (с данными файла, находящимися в «кэше файловой системы») может вообще не включать ядро.
- Сколько обращений к диску выполняется соответственно? Разве буфер ядра не вступит в игру во время второго сценария?
Существует слишком много возможностей. Например.:
а) Если вы читаете из канала (где данные находятся в буфере ядра и ранее были записаны другим процессом), то доступа к диску не будет (потому что данные никогда не были на каком-либо диске с самого начала).
б) Если вы читаете из файла, а ОС уже кэшировала данные файла; тогда может не быть доступа к диску.
c) Если вы читаете из файла, и ОС уже кэшировала данные файла; но файловой системе необходимо обновить метаданные (например, поле «время доступа» в записи каталога файла), то может быть несколько обращений к диску, которые не имеют ничего общего с данными файла.
d) Если вы читаете из файла, а ОС не кэшировала данные файла; тогда потребуется по крайней мере один доступ к диску. Не имеет значения, вызвано ли это fread()
попыткой прочитать весь буфер, read()
попыткой прочитать все 6 байтов сразу или ОС извлекает весь дисковый блок из-за первого « read()
одного байта» в серии из шести отдельных запросов « read()
одного байта». Если ОС вообще не кэширует, то шесть отдельных запросов « read()
по одному байту» будут по меньшей мере 6 отдельными обращениями к диску.
е) файловой системы код может потребоваться для доступа к некоторым частям диска, чтобы определить, где файл данных, на самом деле, прежде чем он сможет прочитать файл данных, и запрошенный файл данных может быть разделен между несколькими блоков/секторов на диске; так что значение 2 или более байт из файла (независимо от того, было ли это вызвано fread()
или « read()
2 или более байт») может привести несколько обращений к диску.
f) с массивом RAID 5/6, включающим 2 или более физических диска (где чтение «логического блока» включает чтение блока с одного диска, а также чтение информации о четности с другого диска), количество обращений к диску может быть удвоено.
- Функция чтения ssize_t read(int fd, void *buf, количество size_t) также имеет параметры буфера и количества, могут ли они заменить роль буфера пользовательского пространства?
Да; но если вы используете его для замены роли буфера пользовательского пространства, то вы в основном просто реализуете свой собственный дубликат fread()
.
Его чаще используют fread()
, когда вы хотите обрабатывать данные как поток байтов и read()
(или, возможно mmap()
), когда вы не хотите обрабатывать данные как поток байтов.
Для случайной пример; может быть, вы работаете с BMP-файлом, так что вы читайте на «гарантированную 14 байт в файл формата спец» в заголовке, а затем проверить/декодирования/обработки заголовка; затем (после выяснения, где он находится в файле, насколько он большой и какой формат в) вы можете seek()
получить пиксельные данные и прочитать все это в массив (тогда, возможно, породит 8 потоков для обработки пиксельных данных в массиве).
Комментарии:
1. Спасибо за ваше подробное объяснение, просто понял, что мне нужно вручную нажать на награду за ваш ответ.