Ядро Linux в комментариях

Первый взгляд на архитектуру ядра


На рис. 3.1 показано стандартное представление Unix-подобной ОС, со всеми низкоуровневыми деталями, какие только могут понадобиться для платформенно-независимой ОС. Стоит подчеркнуть две характерные особенности ядра:

  • Ядро отделяет прикладные приложения от аппаратных средств.
  • Часть ядра учитывает специфику архитектуры и аппаратуры, другая же часть ядра является переносимой.
  • Рис. 3.1. Первый взгляд на архитектуру ядра

    Первая особенность обсуждалась в предыдущих главах, поэтому повторять ее нет необходимости. Сейчас более интересной является вторая особенность, связанная с зависимым и независимым от архитектуры кодом. Ядро достигает переносимости, частично за счет применения по отношению к себе тех же самых трюков, что и по отношению к пользовательским приложениям. Это означает, что подобно тому как ядро отделяет пользовательские приложения от аппаратных средств, определенная часть ядра обеспечивает отделение оставшейся части ядра от той же аппаратуры. Благодаря такому разделению, и приложения, и часть ядра, становятся переносимыми.

    Хотя это и не всегда очевидно в коде, независимая от архитектуры часть кода в общем случае определяет (или предполагает) интерфейс для низкоуровневой, зависящей от архитектуры части. Например, архитектурно-зависимые части кода управления памятью предполагают, что за счет включения определенного файла заголовков они, помимо прочего, получат подходящее определение макроса PAGE_SIZE (см. строку ), задающего размеры кусков, на которые аппаратура управления памятью будет разбивать пространство адресов (см. ). Независимый от архитектуры код совершенно не заботится о точном определении макроса, оставляя это в ведении архитектурно-зависимого кода. (Кстати, это более изящно и понятно, чем присутствие блоков #ifdef/#endif везде, где должен реализовываться код под конкретную платформу.)

    Таким образом, перенос ядра на новую платформу сводится к идентификации возможностей, наподобие рассмотренных выше, и реализации их, как того требует новая платформа.


    Кстати, переносимость пользовательских приложений получает дальнейшую поддержку посредством слоя между приложениями и ядром — стандартной библиотекой С (libc). Приложения никогда не взаимодействуют с ядром напрямую, а только через libc. Единственная причина, по которой на рис. 3.1 показано непосредственное взаимодействие приложений с ядром, связано с тем, что это возможно. Реально приложения этого не делают. Все, что они могут делать за счет прямого обращения к ядру, они делают через libc, причем более просто.

    Способ взаимодействия с ядром через libc является независимым от архитектуры, причем libc предохраняет пользовательский код от излишней детализации. Самое интересное, что большая часть libc также не использует упомянутую детализацию. Значительная часть libc, наподобие функций atoi и rand, вообще не взаимодействует с ядром. Оставшаяся часть libc, т.е. такие функции, как printf, выполняет существенный объем работы до и/или после взаимодействия с ядром. (printf сначала должна выполнить интерпретацию строки формата, выделить параметры, выяснить, как требуется их печатать, и вывести их во временный внутренний буфер. Только затем, для печати этого буфера, производится вызов системной функции write.) Еще одни фрагменты libc отделяются от системных вызовов весьма тонкими слоями, в связи с чем обращение к одной из таких функций транслируется непосредственно в вызов ядра, которое и выполняет большинство требуемой работы. На самом низком уровне практически вся libc направляется к ядру через своего рода «канал» с использованием механизма, описанного в .

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


    Содержание раздела