Как дисковые операции ввода-вывода обычно выглядят в сборке на уровне ядра? - PullRequest
4 голосов
/ 01 апреля 2012

В пользовательском пространстве выполнить дисковый ввод-вывод так же просто, как связать с библиотекой C или, если вы любите приключения, выполнить системный вызов напрямую. Мне интересно, как само ядро ​​выполняет IO.

Другими словами, предположим, что я гипотетически запускал приложение на голом железе в привилегированном режиме. Как бы я получил доступ к дисковому оборудованию, подключенному через, скажем, соединение SATA? Я выполняю загрузку с заранее определенного адреса? Есть ли какая-то инструкция, связанная с io?

Ответы [ 2 ]

5 голосов
/ 01 апреля 2012

В Linux есть функция трассировки вызовов. Я предлагаю вам проследить запрос ввода-вывода.

Предупреждение: следующее было написано мной, не зная истинных подробностей.

В основном вам нужно использовать PCI API для связи с дисковым устройством для настройки прямого доступа к памяти, потому что вы не хотите читать блоки диска (или кадры Ethernet) по одному байту за раз. Таким образом, вы сообщаете аппаратному обеспечению, что некоторая область памяти (начиная с адреса X и длиной N байтов) является областью DMA. Вы также настраиваете кэширование памяти, чтобы знать, что данные в этой области ОЗУ могут изменяться без записи ЦП в нее, поэтому даже если вы являетесь однопроцессором, они изменчивы.

Предположим, что аппаратное обеспечение поддерживает только одну транзакцию DMA одновременно. Затем вы передаете команды типа «прочитать 512-байтовый номер сектора X (т. Е. Байты X << 9 - ((X + 1) << 9) -1 диска)) и поместить его в область DMA. сделано, запустить прерывание ". Контроллер диска делает свое дело (у него есть процессор ARM и все остальное), через PCI соединяется с концентратором северного моста и через него с оперативной памятью, минуя процессор. Когда запись завершена (или с ошибками), прерывание срабатывает. Пока это происходит, вы ждете (ну, ядро ​​запускает другие процессы, пока ваш процесс спит). Спустя миллионы циклов ЦП (10 мс - это вечность для чипа 2 ГГц), прерывание срабатывает. ОС уведомляется, что чтение было завершено. ОС может видеть данные в оперативной памяти. Затем он либо копирует его в память пользовательского процесса, либо находится на общей странице, и пользовательский процесс может прочитать его оттуда. Пользовательский процесс возобновляется (ну, ставится в очередь, готовая к запуску, и в конце концов запускается, когда планировщик чувствует себя так). </p>

Записывает работу путем копирования данных в пространство DMA и передачи команды «записать данные из области DMA в сектор номер X на диске и запустить прерывание по завершении». Затем диск может запустить прерывание по окончании записи или сразу после считывания данных из ОЗУ, и в этом случае fsync действительно не работает, а ваша база данных и файловая система повреждены из-за сбоя питания.

Кэш блоков ОС работает на целых страницах памяти объемом 4 КБ, поэтому он читает 8 секторов за раз, но идея та же. Новые диски имеют собственный API, который работает с 4KB секторами , но идея та же. USB отличается от PCI, но идея та же. Различное высокопроизводительное оборудование имеет продуманные API-интерфейсы для ускорения всего этого, имеет несколько транзакций в полете одновременно и различные средства управления их заказами.

Сетевые интерфейсы, которые разгружают TCP / IP, вероятно, имеют API вокруг пакетов вместо кадров Ethernet, поскольку сетевая карта понимает заголовок TCP / IP.

Блочные устройства, которые действительно являются сетевыми устройствами, где-то скрывают перевод (часть аппаратного обеспечения, часть встроенного программного обеспечения, часть программного обеспечения).

В Linux для моего оборудования, я думаю, это выглядит так:

Когда модуль sata_piix загружен , он сообщает ОС идентификаторы устройств PCI устройств, которые он поддерживает, и обратные вызовы, которые должна использовать ОС, все описаны в структуре. Общий код топологии PCI ОС обнаруживает устройство с идентификатором 8086: 27c0, ICH7 и находит его в таблице драйверов , поэтому ОС решает, что это правильный драйвер для данного оборудования. В этой таблице драйвер обнаружит, что он должен рассматривать это устройство как устройство SATA ICH6 , позже. Поскольку драйвер сообщает, что поддерживает устройство, ОС регистрирует устройство с драйвером.

Оттуда выделены области управления устройства и подготовлено . DMA настроен . Управление шиной PCI включено (это позволяет контроллеру самостоятельно инициировать передачу данных PCI в ОЗУ (когда у него есть данные) вместо ожидания, пока ЦП инициирует передачу) , Установлены обработчики прерываний .

Код является общим и поддерживает многие поколения оборудования в хронологическом порядке:

  1. PIO
  2. Single / Multi-word DMA
  3. UDMA
  4. SATA
  5. AHCI

Так что трудно читать.Трассировка сделает это намного проще.

1 голос
/ 01 апреля 2012

Это зависит от многих вещей. Я не очень разбираюсь в ARM, но думаю, что есть какая-то инструкция outportb, которая позволяет отправлять данные по шине. Связь с устройством ввода-вывода зависит от поставщика, но обычно ядро ​​может отображать в памяти (в случае кадровых буферов графического процессора) регистры устройства или просто отправлять запросы по шине, которую устройство ввода-вывода может распознать (обычно это зависит от устройства) , А запросы, как правило, представляют собой просто сериализованную информацию о структуре, содержащую начальный сектор, длину и адрес, по которому необходимо передать данные ввода-вывода шины.

Если вы хотите делать такие вещи в ядре (например, в Linux), уже есть функции для создания структур ввода / вывода и передачи их на определенное устройство, поэтому вам не нужно делать это самостоятельно. Если вы пишете свою собственную ОС, я бы порекомендовал взглянуть на исходный код Linux, в котором есть примеры того, как сделать это на уровне сборки для многих архитектур.

...