Андрей Смирнов
Время чтения: ~17 мин.
Просмотров: 3

Ядро операционной системы

Реализации в различных ОС

Linux

В мире Linux модули загружаются и выгружаются утилитой modprobe. Модули хранятся в /lib/modules в файлах с расширением .ko («kernel object»), начиная с версии Linux 2.6. В предыдущих версиях использовалось расширение .o. Команда lsmod показывает список загруженных модулей ядра и зависимости между ними.

Вопросы лицензирования

По мнению хранителей Linux, LKM представляет собой производную работу от ядра. Функции ядра могут помечаться как доступные только для GPL модулей.
Загрузка собственнических или GPL-несовместимых модулей устанавливает флаг в ядре. Данный флаг означает, что какие-либо проблемы или баги имеют меньшие шансы на исследование хранителями ядра. Модули фактически становятся частью работающего ядра и могут повредить внутренние структуры данных, создавая ошибки, которые не могут быть воспроизведены теми, кто не может загрузить проприетарный модуль.

Дело Linuxant

В 2004 году, Linuxant—консалтинговая компания, которая выпускает проприетарные драйверы устройств как модули—попытался ядра злоупотреблять нулевого символа в поле <код> MODULE_LICENSE</код> , как в следующем фрагменте кода:

   MODULE_LICENSE("GPL\0for files in the \"GPL\" directory; for others, only LICENSE file applies");
 

По сравнения строки кода, используемого ядра в это время пытались определить, является ли модуль под GPL был остановлен, когда он достиг нуль-символ (\0), так что будет обманывайте себя, думая, что модуль был провозглашая свое право быть просто «GPL».

FreeBSD

Модули ядра ОС FreeBSD хранятся в (модули, распространяемые с дистрибутивом) или в /boot/modules/ для модулей, установленных из FreeBSD портов и других источников. Модули ядра FreeBSD обычно имеют расширение .ko. Модули могут подгружаться командой , выгружаться . Список модулей виден по команде . Некоторые модули загружаются на этапе первичной загрузки (указываются в файле).

OS X

Некоторые загружаемые модули ядра в OS X могут быть загружены автоматически. Загружаемые модули ядра могут быть также загружены командой . Список может быть выведен командой . Загружаемые модули ядра расположены в программных пакетах с расширением .kext. Модули поставляемые с операционной системой, хранятся в каталоге , модули от третьих лиц в других каталогах.

NetWare

Модуль ядра NetWare называется загружаемым модулем NetWare (NLM). NLMs вставляются в ядре ОС NetWare с помощью команды load, и удалять при помощи командной разгрузки; в командных модулях списки загруженных модулей ядра. NLM могут находиться в любом допустимом пути поиска, назначенном на сервере NetWare .NLM как расширение имени файла.

VxWorks

Загружаемый модуль ядра проекта типа ДКМ могут быть созданы для создания , который затем может быть загружен в пространство ядра с помощью команды . Этот загружаемый модуль ядра можно выгрузить с помощью команды .

Солярис имеет настраиваемый модуль Path ядра нагрузки, ее значение: . Большинство модулей ядра жить в подкаталоги ; тех, кто не считает необходимым для загрузки системы до такой степени, что init может начать часто (но не всегда) находится в . При запуске ядра отладки система активно пытается выгрузить модули.

Монолитное ядро[править]

Основная статья: Монолитное ядро

Монолитное ядро предоставляет богатый набор абстракций оборудования. Все части монолитного ядра работают в одном адресном пространстве.

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

Достоинства: Скорость работы, упрощённая разработка модулей.

Недостатки: Поскольку всё ядро работает в одном адресном пространстве, сбой в одном из компонентов может нарушить работоспособность всей системы.

В этом случае компоненты операционной системы являются не самостоятельными модулями, а составными частями одной большой программы. Такая структура операционной системы называется монолитным ядром (monolithic kernel). Монолитное ядро представляет собой набор процедур, каждая из которых может вызвать каждую. Все процедуры работают в привилегированном режиме.
Таким образом, монолитное ядро – это такая схема операционной системы, при которой все ее компоненты являются составными частями одной программы, используют общие структуры данных и взаимодействуют друг с другом путем непосредственного вызова процедур. Для монолитной операционной системы ядро совпадает со всей системой.
Во многих операционных системах с монолитным ядром сборка ядра, то есть его компиляция, осуществляется отдельно для каждого компьютера, на который устанавливается операционная система. При этом можно выбрать список оборудования и программных протоколов, поддержка которых будет включена в ядро. Так как ядро является единой программой, перекомпиляция – это единственный способ добавить в него новые компоненты или исключить неиспользуемые. Следует отметить, что присутствие в ядре лишних компонентов крайне нежелательно, так как ядро всегда полностью располагается в оперативной памяти. Кроме того, исключение ненужных компонентов повышает надежность операционной системы в целом.
Монолитное ядро – старейший способ организации операционных систем. Примером систем с монолитным ядром является большинство Unix-систем.
В монолитном ядре выделяются вкрапления сервисных процедур, соответствующих системным вызовам. Сервисные процедуры выполняются в привилегированном режиме, тогда как пользовательские программы – в непривилегированном. Для перехода с одного уровня привилегий на другой иногда может использоваться главная сервисная программа, определяющая, какой именно системный вызов был сделан, корректность входных данных для этого вызова и передающая управление соответствующей сервисной процедуре с переходом в привилегированный режим работы.
Если в ОС с таким ядром вылетит какой-либо процесс, то он может быть автоматически перезапущен, а с цельным ядром в случае вылетания процесса, входящего в состав ядра, надо перезапускать всё ядро. Многие владельцы телефонов с Symbian OS, основанной на микроядре, сталкивались с этим: слетает какой-нибудь процесс (на экране появляется сообщение «Прилож. закр.: имя»), а затем этот процесс автоматически перезапускается как ни в чём не бывало.
В частности, монолитное ядро более производительно, чем микроядро, поскольку состоит не из множества разрозненных процессов, «общающихся» между собой, а работает как один большой процесс.
Монолитные же ядра используются еще и в Linux. Они оптимизированы для более высокой производительности с минимальными контекстными переключениями. Такая архитектура упрощает поддержку кода ядра для разработчиков, но требует перекомпиляции ядра при добавлении новых устройств. Следует отметить, что описанные здесь различия являются «классическими», на практике монолитные ядра могут поддерживать модульность (что зачастую и происходит), а микроядра могут требовать перекомпиляции.

Примеры: Традиционные ядра UNIX, такие как BSD и Linux; ядро MS-DOS.

Устройство компонентов ядра ОС Linux

Ядро состоит из:

  1. Файловая система
  2. Подсистема управления процессами и памятью
  3. Подсистема ввода / вывода

Файловая подсистема

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

Подсистема управления запросами

Запущенная на выполнение программа порождает в системе один или более процессов ( задач ). Подсистема управления процессами контролирует:

  1. Создание и удаление процессов
  2. Распределение системных ресурсов ( памяти, вычислительных ресурсов ) между процессами
  3. Синхронизацию процессов
  4. Межпроцессорное взаимодействие

Известно, что в общем случае число активных процессоров превышает число процессоров компьютера, но в каждый конкретный момент времени на каждом процессоре может выполняться только один процесс. Операционная система управляет доступом процессоров к вычислительным ресурсам, создавая ощущение одновременного выполнения нескольких задач.
Специальная задача ядра, называемая распорядителем или планировщиком процессов, разрешает конфликты между процессами в конкуренции за системные ресурсы ( процессор, память, периферийные устройства ). Планировщик запускает процесс на выполнение, следя за тем, чтобы процесс монопольно не захватил разделяемые системные ресурсы. Процесс освобождает процессор, ожидая длительной операции ввода/вывода, или по прошествии периода времени. В этом случае планировщик выбирает следующий процесс с наивысшим приоритетом и запускает его на выполнение.
Модуль управления памятью обеспечивает размещение оперативной памяти для прикладных задач. Оперативная память является дорогостоящим ресурсом, и, как правило, она редко «простаивает». В случае, если для всех процессов недостаточно памяти, ядро перемещает части процесса или несколько процессов во вторичную память ( как правило, для этого выделена некоторая область жесткого диска ), освобождая ресурсы для действующих процессов. Все современные системы реализуют виртуальную память: процесс выполняется в собственном логическом адресном пространстве, которое может значительно превышать доступную физическую память. Управление виртуальной памятью процесса также входит в перечень задач модуля управления памятью.
Модуль межпроцессного взаимодействия.отвечает за уведомление процессов о событиях с помощью сигналов и обеспечивает возможность передачи данных между различными процессами.

Подсистема ввода/вывода

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

Что делает ядро Windows

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

Для примера, код Windows Vista включает в себя более 50 миллионов строк. В Windows 7, в результате оптимизации кода, их число сократилось до «всегою 40 миллионов.

Работая каждый день в Windows невозможно понять сложность ядра. Оно действует в фоновом режиме и отвечает, среди прочего, за взаимодействие между внутренними компонентами ПК, а также внешними устройствами.

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

Начиная с Windows Vista за правильную работу с мультимедиа отвечает служба под названием Multimedia Class Scheduler Service (MMCSS). Мы можем управлять её параметрами с помощью панели управления службами. Используйте сочетание клавиш Win + R и в окне Запуск программы введите команду services.msc, а затем нажмите Enter.

Multimedia Class Scheduler заботится о том, чтобы мультимедийные приложения всегда имели в распоряжении соответствующие ресурсы процессора. Если аудио или видео файл воспроизводится с помехами, рекомендуется в панели управления службами дважды щелкнуть на службе MMSCS (Планировщик классов мультимедиа), проверить тип запуска и, возможно, изменить его на Автоматический.

Задаем IDT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

structIDT_entry{

unsignedshortintoffset_lowerbits;

unsignedshortintselector;

unsignedcharzero;

unsignedchartype_attr;

unsignedshortintoffset_higherbits;

};

structIDT_entry IDTIDT_SIZE;

voididt_init(void)

{

unsignedlongkeyboard_address;

unsignedlongidt_address;

unsignedlongidt_ptr2;

keyboard_address=(unsignedlong)keyboard_handler;

IDT0x21.offset_lowerbits=keyboard_address&0xffff;

IDT0x21.selector=0x08;/* KERNEL_CODE_SEGMENT_OFFSET */

IDT0x21.zero=;

IDT0x21.type_attr=0x8e;/* INTERRUPT_GATE */

IDT0x21.offset_higherbits=(keyboard_address&0xffff0000)>>16;

write_port(0x20,0x11);

write_port(0xA0,0x11);

write_port(0x21,0x20);

write_port(0xA1,0x28);

write_port(0x21,0x00);

write_port(0xA1,0x00);

write_port(0x21,0x01);

write_port(0xA1,0x01);

write_port(0x21,0xff);

write_port(0xA1,0xff);

idt_address=(unsignedlong)IDT;

idt_ptr=(sizeof(structIDT_entry)*IDT_SIZE)+((idt_address&0xffff)<<16);

idt_ptr1=idt_address>>16;

load_idt(idt_ptr);

}

Модульное ядро[править]

Основная статья: Модульное ядро

Модульное ядро — современная, усовершенствованная модификация архитектуры монолитных ядер операционных систем компьютеров.

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

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

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

Модульные ядра предоставляют особый программный интерфейс (API) для связывания модулей с ядром, для обеспечения динамической подгрузки и выгрузки модулей. В свою очередь, не любая программа может быть сделана модулем ядра: на модули ядра накладываются определённые ограничения в части используемых функций (например, они не могут пользоваться функциями стандартной библиотеки С/С++ и должны использовать специальные аналоги, являющиеся функциями API ядра). Кроме того, модули ядра обязаны экспортировать определённые функции, нужные ядру для правильного подключения и распознавания модуля, для его корректной инициализации при загрузке и корректного завершения при выгрузке, для регистрации модуля в таблице модулей ядра и для обращения из ядра к сервисам, предоставляемым модулем.

Не все части ядра могут быть сделаны модулями. Некоторые части ядра всегда обязаны присутствовать в оперативной памяти и должны быть жёстко «вшиты» в ядро. Также не все модули допускают динамическую подгрузку (без перезагрузки ОС). Степень модульности ядер (количество и разнообразие кода, которое может быть вынесено в отдельные модули ядра и допускает динамическую подгрузку) различна в различных архитектурах модульных ядер. Ядра Linux в настоящее время имеют более модульную архитектуру, чем ядра *BSD (FreeBSD, NetBSD, OpenBSD).

Общей тенденцией развития современных модульных архитектур является всё большая модуларизация кода (повышение степени модульности ядер), улучшение механизмов динамической подгрузки и выгрузки, уменьшение или устранение необходимости в ручной подгрузке модулей или в переконфигурации ядра при изменениях аппаратуры путём введения тех или иных механизмов автоматического определения оборудования и автоматической подгрузки нужных модулей, универсализация кода ядра и введение в ядро абстрактных механизмов, предназначенных для совместного использования многими модулями (примером может служить VFS — «виртуальная файловая система», совместно используемая многими модулями файловых систем в ядре Linux).
Курсивное начертание

Прерывания

Теперь, прежде чем мы вернемся к написанию драйвера, нам нужно понять, как процессор узнает, что какое-то из устройств выполнило операцию.

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

За прерывания в архитектуре x86 отвечает чип под названием Programmable Interrupt Controller (PIC). Он обрабатывает хардверные прерывания и направляет и превращает их в соответствующие системные прерывания.

Когда пользователь что-то делает с устройством, чипу PIC отправляется импульс, называемый запросом на прерывание (Interrupt Request, IRQ). PIC переводит полученное прерывание в системное прерывание и отправляет процессору сообщение о том, что пора прервать то, что он делает. Дальнейшая обработка прерываний — это задача ядра.

Без PIC нам бы пришлось опрашивать все устройства, присутствующие в системе, чтобы посмотреть, не произошло ли событие с участием какого-то из них.

Давай разберем, как это работает в случае с клавиатурой. Клавиатура висит на портах 0x60 и 0x64. Порт 0x60 отдает данные (когда нажата какая-то кнопка), а порт 0x64 передает статус. Однако нам нужно знать, когда конкретно читать эти порты.

Прерывания здесь приходятся как нельзя более кстати. Когда кнопка нажата, клавиатура отправляет PIC сигнал по линии прерываний IRQ1. PIС хранит значение , сохраненное во время его инициализации. Он добавляет номер входной линии к этому отступу, чтобы сформировать вектор прерывания. Затем процессор ищет структуру данных, называемую «таблица векторов прерываний» (Interrupt Descriptor Table, IDT), чтобы дать функции — обработчику прерывания адрес, соответствующий его номеру.

Затем код по этому адресу исполняется и обрабатывает прерывание.

GRUB и мультизагрузка

Теперь все наши файлы готовы к сборке ядра. Но поскольку мы будем загружать ядро при помощи GRUB, остается еще один шаг.

Существует стандарт для загрузки разных ядер x86 с помощью бутлоадера. Это называется «спецификация мультибута». GRUB будет загружать только те ядра, которые ей соответствуют.

В соответствии с этой спецификацией ядро может содержать заголовок (Multiboot header) в первых 8 килобайтах. В этом заголовке должно быть прописано три поля:

magic — содержит «волшебное» число 0x1BADB002, по которому идентифицируется заголовок;

flags — это поле для нас не важно, можно оставить ноль;

checksum — контрольная сумма, должна дать ноль, если прибавить ее к полям и .

Наш файл kernel.asm теперь будет выглядеть следующим образом.

kernel.asm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

bits32

section.text

;multiboot spec

align4

dd0x1BADB002;magic

dd0x00;flags

dd-(0x1BADB002+0x00);checksum

globalstart

extern kmain

start

cli

mov esp,stack_space

call kmain

hlt

section.bss

resb8192

stack_space

Ядро на C

В файле мы вызвали функцию . Так что в коде на C исполнение начнется с нее.

kernel.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

voidkmain(void)

{

constchar*str=»my first kernel»;

char*vidptr=(char*)0xb8000;

unsignedinti=;

unsignedintj=;

while(j<80*25*2){

vidptrj=’ ‘;

vidptrj+1=0x07;

j=j+2;

}

j=;

while(strj!=’\0′){

vidptri=strj;

vidptri+1=0x07;

++j;

i=i+2;

}

return;

}

Все, что будет делать наше ядро, — очищать экран и выводить строку my first kernel.

Первым делом мы создаем указатель vidptr, который указывает на адрес 0xb8000. В защищенном режиме это начало видеопамяти. Текстовая экранная память — это просто часть адресного пространства. Под экранный ввод-вывод выделен участок памяти, который начинается с адреса 0xb8000, — в него помещается 25 строк по 80 символов ASCII.

Каждый символ в текстовой памяти представлен 16 битами (2 байта), а не 8 битами (1 байтом), к которым мы привыкли. Первый байт — это код символа в ASCII, а второй байт — это . Это определение формата символа, в том числе — его цвет.

Чтобы вывести символ зеленым по черному, нам нужно поместить в первый байт видеопамяти, а значение 0x02 — во второй байт. 0 здесь означает черный фон, а 2 — зеленый цвет. Мы будем использовать светло-серый цвет, его код — 0x07.

В первом цикле программа заполняет пустыми символами с атрибутом 0x07 все 25 строк по 80 символов. Это очистит экран.

Во втором цикле символы строки my first kernel, оканчивающейся нулевым символом, записываются в видеопамять и каждый символ получает attribute-byte, равный 0x07. Это должно привести к выводу строки.

Драйвер устройства как элемент микроядра

Драйверы устройств часто выполняют прямой доступ к памяти (DMA) и поэтому могут выполнять запись в произвольные места физической памяти, включая различные структуры данных ядра. Хотя запуск драйвера устройства в пользовательском пространстве не обязательно уменьшает ущерб, который может причинить неправильный драйвер, на практике это выгодно для стабильности системы при наличии неисправных (а не злонамеренных) драйверов: нарушения доступа к памяти со стороны самого кода драйвера ( в отличие от устройства) все еще может быть перехвачено аппаратным обеспечением управления памятью. Кроме того, многие устройства не поддерживают DMA, их драйверы нельзя доверять, запустив их в пространстве пользователя. В последнее время все большее количество компьютеров оснащаются IOMMU , многие из которых можно использовать для ограничения доступа устройства к физической памяти. Это также позволяет ненадежным драйверам пользовательского режима.

Драйверы пользовательского режима на самом деле предшествуют микроядрам. Система Michigan Terminal System (MTS), в 1967 году, при поддержке пользовательских драйверов (включая поддержку файловой системы), первую операционную систему , которая будет разработана с этой возможностью. Исторически сложилось так, что драйверы были меньшей проблемой, так как число устройств было небольшим и в любом случае им доверяли, поэтому их размещение в ядре упростило конструкцию и позволило избежать потенциальных проблем с производительностью. Это привело к традиционному стилю «драйвер в ядре» в Unix, Linux и Windows NT. С распространением различных видов периферийных устройств объем кода драйвера возрос и в современных операционных системах доминирует в ядре по размеру кода.

Настраиваем GRUB и запускаем ядро

GRUB требует, чтобы название файла с ядром следовало конвенции . Так что переименовываем файл — я назову свой .

Теперь кладем ядро в каталог . На это понадобятся привилегии суперпользователя.

В конфигурационный файл GRUB grub.cfg нужно будет добавить что-то в таком роде:

1
2
3

title myKernel

root(hd0,)

kernelbootkernel-701ro

Не забудь убрать директиву hiddenmenu, если она прописана.

GRUB 2

Чтобы запустить созданное нами ядро в GRUB 2, который по умолчанию поставляется в новых дистрибутивах, твой конфиг должен выглядеть следующим образом:

1
2
3
4

menuentry’kernel 701′{

set root=’hd0,msdos1′

multibootbootkernel-701ro

}

Благодарю Рубена Лагуану за это дополнение.

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

Это и есть твое ядро!

wiki.osdev.orgosdever.netMultiboot spec

ХЭЛ – посредник между ядром и оборудованием

Неразрывно с ядром Windows связан, так называемый, слой абстрагирования оборудования (англ. HAL – Hardware Abstraction Layer).

В сущности, речь идет об универсальном наборе инструкций, обеспечивающим обмен информацией между системой и различными аппаратными компонентами. Благодаря этому разработчики Windows не вынуждены каждый раз настраивать код для другой аппаратной конфигурации пользователя. Достаточно, чтобы компьютер выполнял требования, а об остальном позаботится ХЭЛ.

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

Рейтинг автора
5
Материал подготовил
Максим Иванов
Наш эксперт
Написано статей
129
Ссылка на основную публикацию
Похожие публикации