- Что нужно знать, чтобы написать операционную систему
- Что нужно знать, чтобы написать операционную систему
- Что такое ОС
- Кратко об истории операционных систем
- Составляющие операционной системы
- Ядро (англ. kernel)
- Системные программы
- Пользовательские программы
- Что необходимо знать
- Изучение информатики
- Изучение программирования
- Изучение языков программирования
- Язык ассемблера для x86
- Язык Cи
- Разработка ОС
- Сообщества, посвященные разработке ОС
- Заключение
- Написание собственной Операционной Системы №2
Что нужно знать, чтобы написать операционную систему
Что нужно знать, чтобы написать операционную систему
Создание операционной системы — одна из сложнейших задач в программировании, поскольку требует обширных и комплексных знаний о работе компьютера. Каких именно? Разбираемся ниже.
Что такое ОС
Операционная система (ОС) — это программное обеспечение, которое работает с компьютерным железом и его ресурсами и является мостом между аппаратной и программной частью компьютера.
Компьютеры первого поколения не имели операционных систем. Программы на первых ЭВМ включали в себя код для непосредственной работы системы, связи с периферийными устройствами и вычислений, для выполнения которых эта программа и писалась. Из-за такого расклада даже простые по логике работы программы были сложны в программной реализации.
По мере того как компьютеры становились более разнообразными и сложными, писать программы, которые работали и как ОС, и как приложение, стало попросту неудобно. Поэтому, чтобы программы было легче писать, владельцы компьютеров начали разрабатывать программное обеспечение. Так и появились операционные системы.
ОС предоставляет всё необходимое для работы пользовательских программ. Их появление означало, что теперь программам не нужно контролировать весь объём работ компьютера (это отличный пример инкапсуляции). Теперь программам нужно было работать именно с операционной системой, а система уже сама заботилась о ресурсах и работе с периферией (клавиатура, принтер).
Кратко об истории операционных систем
Первая операционная система была создана в 1956 году компанией General Motors для единственного компьютера IBM. Остальные владельцы этих компьютеров последовали примеру и начали создавать собственные ОС.
Как можно догадаться, первые ОС сильно отличались друг от друга. Почти на каждом компьютере стояла своя система. Хоть они и облегчали написание программ, использовать такие программы можно было только на одном компьютере.
В 1960-х годах IBM стала первой выпускающей компьютеры компанией, которая взяла на себя процесс написания ОС под свои же компьютеры. Компания начала распространять компьютеры вместе со своей операционной системой.
В эти же годы компании Control Data Corporation, Computer Sciences Corporation, Burroughs Corporation, GE, Digital Equipment Corporation и Xerox тоже начали реализовывать свои ОС.
В конце 60-х была разработана первая версия ОС Unix. Написанная на Си, свободно доступная первые годы, Unix привлекала всё больше внимания и аудитории. Она легко портировалась на новые системы и начала набирать обороты.
Многие современные ОС, включая Apple OS X и все разновидности Linux-систем, являются дальними потомками Unix.
Microsoft Windows, в свою очередь, была написана для ряда персональных компьютеров IBM.
Первая ОС от Microsoft называлась не Windows, а MS-DOS. Эта система была создана в 1981 году, когда систему 86-DOS купили у компании Seattle Computer Products и модифицировали её под требования IBM.
Всем привычное название Windows появилось в 1985, когда MS-DOS обзавелась графическим интерфейсом.
Apple OS X, Microsoft Windows и ряд Linux-систем (включая Android) сейчас контролируют большую часть рынка операционных систем.
Составляющие операционной системы
ОС состоит из двух основных частей:
- ядро системы;
- системные программы.
Ядро (англ. kernel)
Сердце операционной системы. Именно оно запускается первым при включении компьютера (не считая BIOS и загрузчик). Ядро контролирует ресурсы компьютера и обрабатывает запросы от системных программ.
Системные программы
Работают поверх ядра. Такие программы нужны, в целом, не для пользователя, а для связи ядра с пользовательскими приложениями и периферией. Примеры системных программ: драйвера устройств, файловая система, сетевые программы, утилиты для дефрагментации диска.
Пользовательские программы
Не являются частью операционной системы. Именно эти программы уже имеют конкретное назначение. Текстовые редакторы, браузеры, медиа-плееры — всё это пользовательские программы. Они контролируются ядром и используют системные программы для доступа к периферии.
Что необходимо знать
Список вещей, которые необходимо знать для создания собственной ОС, очень длинный. Но в нём есть три основных пункта, на которые нужно обратить внимание в первую очередь:
- базовое понимание компьютерной науки (информатики);
- теория и опыт в программировании;
- знание высоко- и низкоуровневых языков программирования.
Изучение информатики
Разработка ОС — это не тоже самое, что, скажем, веб-разработка. Тут нельзя будет просто и быстро разобраться в базовых вещах. Для начала нужно получить крепкий базис информатики и только потом переходить к другим темам.
Coursera: Computer Science 101 — это курс, который подойдёт для тех, кто только начал осваиваться в информатике и программировании. Если у вас уже имеются элементарные знания в в этой области, то пропустите этот курс и переходите к следующему.
Udacity: Intro to Computer Science использует веб-разработку в качестве подхода к обучению компьютерной науке. Хоть курс и не направлен на непосредственную разработку ОС, он даёт мощный базис в области программирования.
edX: Introduction to Computer Science — этот курс является самым исчерпывающим и углублённым в этом списке. Несмотря на это, он полностью бесплатен. Курс был разработан в Гарвардском Университете. В нём вас научат алгоритмам, структурам данных, управлению ресурсами, разработке ПО, а так же познакомят с такими языками программирования, как C, PHP и JavaScript.
Подборка книг для самостоятельного изучения Computer Science.
Изучение программирования
С крепкими знаниями в области информатики и хотя бы базовым пониманием программирования вам нужно набраться опыта в разработке проектов.
Udacity: Software Development Process — отличный курс для тех, кто ещё не принимал участие в крупных проектах. Тут вас ознакомят с подробностями рабочего процесса и работой с инструментарием (например Git), а также расскажут о настройке интегрированной среды разработки.
Изучение языков программирования
Для разработки операционной системы вам понадобится освоить минимум два языка программирования:
- низкоуровневый язык ассемблера;
- высокоуровневый язык программирования.
Первый используется для работы напрямую с процессором. Процессор «понимает» только машинный код, и для каждого типа процессора есть только один соответствующий язык. Одной из самых популярных архитектур процессора является x86. Она была разработана компанией Intel и на текущий момент широко используется многими производителями компьютерного железа, включая AMD. По этой причине в этой статье акцент будет именно на архитектуру x86.
Высокоуровневые языки программирования, в свою очередь, работают сразу на нескольких архитектурах. Самый популярный из таких языков — Cи. Чаще всего именно на нём и пишутся операционные системы. Но это не означает, что этот язык единственный. Для написания ОС можно использовать и более высокоуровневые языки, например C++ или Python.
Прим. перев. Есть пример написания «игрушечных» операционных систем на C#. Cosmos — некий конструктор ОС. В этой статье на практическом примере показывают написание системы всего в нескольких десятках строк.
Язык ассемблера для x86
«x86 Assembly Guide» — неплохо подойдёт для начального изучения ассемблера. Несмотря на то, что эта статья коротка для полного курса, в ней содержится всё необходимое для дальнейшего углубления.
Книга «Programming from the Ground Up» Джонатана Бартлетта является одной из основных по изучению ассемблера. В ней основами языка программирования подкрепляются базисы информатики.
Для изучения ассемблера есть ещё одна показательная книга — «The Art of Assembly Language», написанная Рэнди Гайдом. Изначально книга писалась специально для курсов, которые вёл автор в Калифорнийском университете Cal Poly и UC Riverside. В середине 90-х годов книга была признана определяющей в этой сфере.
Если вдруг вышеописанные материалы вам не подошли, вот ещё пара отличных книг:
Язык Cи
Как уже упоминалось выше, для написания ОС есть несколько высокоуровневых языков программирования. Однако самый популярный из них — Си.
Начать изучать этот язык можно отсюда. Этот ресурс ознакомит вас с базовыми понятиями и подготовит к более сложным задачам.
«Learn C the Hard Way» — название ещё одной книги. Кроме привычной теории в ней собрано много практических решений. Этот учебник расскажет обо всех аспектах языка.
Либо же можете выбрать одну из этих книг:
Разработка ОС
После освоения всего необходимого, что касается информатики, языка ассемблера и Cи, вам стоит прочесть хотя бы одну или две книги про непосредственную разработку ОС. Вот несколько ресурсов для этого:
«Linux From Scratch». Здесь рассматривается процесс сборки операционной системы Linux (учебник переведён на много языков, в том числе и на русский). Тут, как и в остальных учебниках, вам предоставят все необходимые базовые знания. Полагаясь на них можно попробовать себя в создании ОС. Чтобы сделать программную часть ОС более профессиональной, присутствуют дополнения к учебнику: «Beyond Linux from Scratch», «Automated Linux from Scratch», «Cross Linux from Scratch» или «Hardened Linux from Scratch».
«The little book about OS development» Эрика Хэйлина и Адама Ренберга. Этот учебник разработан в рамках продвинутого курса информатики и на момент его написания авторы являлись студентами Королевского Института Технологий в Стокгольме. В этой книге рассматривается разработка ОС для архитектуры x86 (причём с самого начала — с установки среды разработки). В этом курсе рассказывается о многих интересных темах, включая многозадачность, управление памятью и разработку файловой системы.
«Operation System Development Series» Broken Thorn Entertainment — серия из 25 уроков, посвящённых разработке ОС с нуля.
Примечание Эти уроки рассчитаны на то, что вы уже знакомы с IDE и владеете как языком ассемблера, так и Cи.
Вот ещё три книги, которые помогут вам ознакомиться с дисциплиной разработки ОС:
Сообщества, посвященные разработке ОС
Заниматься разработкой ОС в одиночку смело, но сложно. Гораздо проще найти единомышленников, которые, как и вы, решили попытать удачу в этом нелёгком деле. Существует пара подходящих мест:
- OSDev.org — это Вики с исчерпывающей информацией о разработке ОС и отличным форумом, где вы можете попросить о помощи или же наоборот сами кого-нибудь выручить.
- OS Development Channel на Реддите. Канал, также посвящённый разработке ОС.
- Computer Science, Programmers, StackOverflow от StackExchange — площадки, где вы можете задавать различные технические вопросы.
Заключение
Обучение разработке ОС — достаточно сложная задача. Однако в процессе вы разберётесь в работе процессора и аппаратного уровня, что поможет лучше понимать работу и оптимизацию высокоуровневых приложений и их взаимодействие друг с другом. Ну и в глазах коллег вы наверняка будете выглядеть очень крутым (но это не точно).
Написание собственной Операционной Системы №2
Для начала я бы хотел устроить небольшой разбор полетов. В прошлой части я забыл
написать, что исходные коды программ на ассемблере я предполагаю компилировать в
Nasm в виде бинарных файлов, а запись полученного результата отправлять на
отформатированную дискету с помощью, скажем, программы RawWrite. При том
записаны они должны быть сразу друг за другом, поэтому в первый загрузчик можно
включить в самом конце incbin ‘name2.bin’ (name2 — название второго загрузчика),
соответственно тогда он включит в себя текст второго. И сразу отвечу на вопрос
читателей, который мне после этой статьи задавали. Я понимаю, что описать
написание ОС в нескольких статьях невозможно, потому я бы предложил читателю для
начала прочитать следующие книги(помимо Зубкова, указанного ранее):
а уж потом браться за прочтение этих статей, в которых собрано в единое целое
то, что необходимо при написании ОС.
Тем не менее сейчас я дополнительно опишу минимум теории, необходимый для
прошлой статьи, который я из нее убрал. Процессор может работать в двух режимах
— реальный и защищенный (их гибрид я не учитываю). Защищенный режим предназначен
для того, чтобы можно было использовать такие возможности как многозадачность,
виртуальная память и пр. Основная структура данных PM (protected mode) —
дескриптор (8 байт), необходимый для контроля сегментации, аппаратной
многозадачности, обработки прерываний. В этом режиме память делится на
защищенные части — сегменты. В дескрипторе сегмента содержатся данные о его
типе, базе (адресе), размере, уровне привилегий и пр. Сегментная память
используется для того, чтобы защитить программы при их исполнении от других
(благодаря привилегиям).
Для перевода процессора в PM используется глобальная таблица дескрипторов, в
которой хранится для каждого сегмента отдельный дескриптор. Его вид:
0,1 байты — размер
2-4 байты — база
5 байт — бит наличия сегмента, уровень привилегий, тип сегмента (0010 — данные,
1010 — код)
6 байт — 1, разрядность (0 — 16, 1 — 32), 0, 0, размер
7 байт — база
Итак, задача для перехода в PM такова: построить GDT с дескрипторами для кода и
данных (база — 0, размер — 4 Гб), включить адресную линию A20 (она отключена для
совместимости со старыми процессорами), изменить в регистре CR0 один бит в 1
(флаг того, что мы в PM), после чего прыгнуть в 32-битный сегмент.
Итак, в прошлой части
мы подготовили плацдарм для наших действий. Теперь перед нами обширные
горизонты, от которых надо постараться не потерять голову. Давайте немного
подумаем, какие первоочередные задачи сейчас перед нами стоят? Во-первых, мы
умеем только загружать нашу ОС, у нас нету так ядра, которому бы передавалось
управление. Далее наша ОС должна иметь минимальный набор функция для контакта с
пользователем, а потому перед нами встает еще задача обеспечения функций работы
с железом. Для этого нам будет необходимо написать таблицу прерываний, а так же
научить наше детище работать, скажем, для начала с клавиатурой и жестким диском.
Начнем с ядра. Кстати, немного о его концепции. Оно бывает нескольких видов:
микроядро, монолитное, экзоядро. Различаются они по делению исполняемых задач по
привилегиям. Так в микроядре на правах супервизора работает крайне малая часть
функций, остальное — подключаемые библиотеки. Монолитное же ядро очень велико,
все основные функции ОС сосредоточены именно в нем. Микроядерная архитектура
имеет огромные преимущества над традиционной монолитной и только один серьезный
недостаток — пониженное быстродействие. Экзоядерная архитектура превосходит все
другие архитектуры по производительности в несколько раз, однако она слишком
сложна. Она отличается тем, что если микро и монолитные ядра обеспечивают задачи
распределения ресурсов и создания виртуальной машины, то экзоядра эти задачи
разделают. В каком смысле? Например, дать возможность пользовательским
программам обращаться к конкретной ячейке памяти, конкретному сектору диска,
конкретным портам других внешних устройств. Мы будем рассматривать микроядро,
т.е. в ядре будет сосредоточен базовый минимум функций, всю остальную работу
будет обеспечивать набор драйверов. Хотя надо заметить, что в ближайших двух
статьях ссылки на тип ядра у нас не будет.
Ближе к делу. Как вы помните, написанный в предыдущем выпуске загрузчик обладает
возможностью загружать и выполнять код, находящийся в файле kernel.bin и
скомпонованный по адресу 0x200000. Теперь мы уже можем работать на С. Первой же
проблемой, с которой мы столкнемся при создании kernal.bin, будет то, что
содержимое функции main() вынесется компилятором на самый верх программы, т.е.
мы будем из загрузчика передавать управление в никуда. Для того, чтобы от этого
избавиться, надо передавать управление ядру через переходник (c.asm):
[BITS 32]
[EXTERN k_main]
[GLOBAL _go]
_go:
mov esp, 0x200000-4
call k_main
Само ядро у нас будет выглядеть вот так (kernel.c):
Большое, не правда ли? Перейдем к работе с железом. Для начала нам потребуется
написать несколько функций, позволяющих работать с портами и прерываниями, после
чего можно будет уже самостоятельно описывать прерывания (что мы рассмотрим на
примере клавиатуры).
При возникновении прерывания, процессору известен только номер прерывания.
Поскольку сам по себе этот номер ничего не говорит о том, где именно находится
процедура-обработчик, эту информацию процессор должен почерпнуть из специальной
таблицы (таблица прерываний). В режиме реальных адресов таблица прерываний
находится по абсолютному адресу 0:0 — 0:0x400 и представляет из себя 256
абсолютных четырехбайтных адресов процедур-обработчиков. Процессору остается
только взять из этой таблицы адрес соответствующего номеру прерывания
обработчика, и выполнить переход на этот адрес. В защищенном режиме таблица
прерываний называется таблицей дескрипторов прерываний IDT (Interrupt
Descriptors Table) и на ее местонахождение указывает регистр IDTR (interrupt
descriptors table register). Уже по названию можно судить, что в ней находятся
не просто адреса обработчиков, а дескрипторы обработчиков прерывания. В качестве
таких дескрипторов могут выступать дескрипторы трех типов: шлюз прерывания, шлюз
ловушки и шлюз задачи. На данном этапе нас интересуют первые два. Они отличаются
тем, что при выполнении обработчика прерывания все прерывания сразу запрещаются
до завершения обработчика, а флаг трассировки сбрасывается.
Дескриптор представляет собой следующую запись:
0,1 байты:
2,3 байты: селектор сегмента
4: пустой
5:
0-3 содержит тип шлюза
(0110 — 16 битный шлюз прерывания, 0111 — 16 битный шлюз ловушки, 1110 — 32
битный шлюз прерывания, 1111 — 32 битный шлюз ловушки)
4 — тип дескриптора
5,6 — уровень привилегий сегмента
7 — наличие сегмента
6,7: биты смещения процедуры-обработчика
Итак, в таблице должно быть 256 дескрипторов (по количеству возможных
прерываний), каждый из которых будет иметь свой личный номер, использующийся для
его вызова при соответствующем прерывании. При вызове обработчика прерываний
процессор помещает в стек регистр флагов и адрес возврата
Начнем мы с функций (inter.c), которые позволят нам устанавливать обработчики
прерываний, а также запрещать и разрешать обработку прерываний:
#define IT 0x100000
#define IR 0x100800
#define SCS 0x8
// В inst() some является обработчиком intr, тип
шлюза задаем параметром kind; де-факто мы создаем/редактируем дескриптор в
таблице.
void inst(unsigned char int, void (*some)(), unsigned char kind)
<
char * it=IT;
unsigned char i;
unsigned char a[8];
a[0]= (unsigned int)some & 0x000000FF;
a[1]=( (unsigned int)some & 0x0000FF00) >> 8;
a[2]=SCS;
a[3]=0;
a[4]=0;
a[5]=kind;
a[6]=( (unsigned int)some & 0x00FF0000) >> 16;
a[7]=( (unsigned int)some & 0xFF000000) >> 24;
for(k=0;k Загружаем IDTR
void int_l()
<
unsigned short *limit = IR;
unsigned int *place = IR+2;
*limit = 256*8 — 1;
*place = IT;
// Запрещаем прерывания
void int_d()
<
asm(«cli»);
>
Необходимо так же помнить и о работе с портами, иначе прерывания нам становятся
не нужны. Создадим port.c, главными строками в котором будут:
asm(«outb %b0,%w1»:: «a»(value), «d»(port));
asm(«inb %w1, %b0»: «=a»(value): «d»(port));
(value — простое число, я надеюсь, что скелет вы напишете сами, раз читаете эту
статью)
Собственно, теперь мы готовы заниматься самими прерываниями. Как я и говорил,
начнем мы с клавиатуры, однако не стоит забывать и о таймере, который тикает у
нас каждую 1/18,3 секунды. Перед выполнением задачи обработчик прерывания должен
сохранить регистры, а по завершении обработки — послать байт 0x20 в порт 0x20
(сигнал «конец прерывания» контроллеру прерываний), после чего восстановить
регистры. Дабы не использовать в тексте каждый раз загромождений кодом из asm, я
введу отдельный макрос:
#define IRQ_HANDLER(func) void func (void);\
asm(#func «: pusha \n call _» #func » \n movb $0x20, %al \n outb %al, $0x20 \n
popa \n iret \n»);\
void _ ## func(void)
(можно не пытаться в него вникнуть, главное понимайте зачем он нужен)
Возьмем code.h (содержит таблицу символов):
‘, 0, ‘|’, ‘Z’, ‘X’,
‘C’, ‘V’, ‘B’, ‘N’, ‘M’, ‘ ‘, ‘?’, 0, ‘*’, 0, ‘ ‘, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, ‘-‘, 0, 0, 0, ‘+’, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>;
// 8 — backspace, на ctrl, alt и пр. стоят заглушки
— 0.
Теперь напишем следующий finter.c:
#include «code.h»
// Я говорил вам о таймере, здесь пока будет
простая заглушка, можно потом будет ввести сюда более серьезные функции.
Например, подсчет времени.
IRQ_HANDLER(irq_time)
<
>
// Работа с клавиатурой довольно проста — мы должны
считывать с нее коды клавиш, при этом не забывая, что у нас может быть два
состояния — с shift и без него.
char sh = 0;
IRQ_HANDLER(irq_kb)
<
unsigned char code, asc;
unsigned char reg;
// С 60-го порта вы получаем данные
code = ipb(0x60);
// ipb, opb — мои названия функций из port.c
switch(code) <
// С shift
case 0x36:
case 0x2A:
sh = 1;
break;
// Без shift
case 0x36 + 0x80:
case 0x2A + 0x80:
sh = 0;
break;
default:
// Если клавишу отпустили, то не делать ничего, а
если нажали, то в зависимости от значения shift преобразовать скан-код в в
разные символы ASCII
if(code >= 0x80) <
> else <
if(sh) <
asc = codes_sh[code];
> else <
asc = codes[code];
>
if(asc != 0) <
// pc — выводит на экран символ, эту функцию я
опишу ниже.
pc(asc);
>
>
break;
>
// После чего надо показать, что работа по
считыванию символа проделана. Для этого считываем состояние клавиатуры (61
порт), преобразуем в нем старший бит в 1, после чего запишем назад.
reg = ipb(0x61);
reg |= 1;
opb(0x61, creg);
>
// Теперь установка обработчиков прерываний
void iint()
<
inst(0x20, &irq_time, 0x8e);
inst(0x21, &irq_kb, 0x8e);
int_l();
int_e();
>
Вот, теперь у нас есть два прерывания. Надо бы проверить их
работоспособность, да и вообще хотелось бы, чтобы наша ОС умела нам хоть что-то
показывать, потому необходимо описать функции для работы с монитором. Это делать
мы будет напрямую через видеопамять (video.c).
#define VW 80 // Ширина экрана
#define VH 25 // Его высота
#define RAM 0xb8000 // Видеопамять
int cur; // Где находится курсор
int param; // Параметры
// Идея проста — «бегаем» по экрану при прочтении
очередного символа, заносим этот символ в видеопамять, меняем параметры
символа.
void initial()
<
cur = 0;
param = 7;
>
void color(char c)
<
param = c;
>
void clear()
<
char *vid = RAM;
int k;
for (k = 0; k Единственная хоть чем-то интересная для нас
функция, здесь мы как раз и записываем символ, меняем положение курсора.
void pc(char c)
<
char *vid = RAM;
int k;
switch (c) <
case ‘\n’:
cur+=VW;
cur-=cur%VW;
break;
default:
*(vid + cur*2) = c;
*(vid + cur*2+1) = param;
cur++;
break;
>
// Здесь необходимо описать что делать, если
положение курсора вышло за пределы экрана. Решение простое — сдвиг экрана на
одну строку.
if(cur>VW*VH) <
for(i=VW*2;i
На сегодня остался лишь один вопрос. У нас много кода (я описал все за
исключением функций работы с портами), но его же надо как-то заставить работать.
Как это сделать? Стоит добавить в k_main вызовы функций: initial(), iint(), а
так же clear(). Компилируется все это довольно просто, достаточно следующей
последовательности команд:
gcc -ffreestanding -c -o keyboard.o keyboard.c
gcc -ffreestanding -c -o inter.o inter.c
gcc -ffreestanding -c -o finter.o finter.c
gcc -ffreestanding -c -o port.o port.c
gcc -ffreestanding -c -o video.o video.c
gcc -ffreestanding -c -o kernel.o kernel.c
nasm -felf -o c.o c.asm
ld —oformat binary -Ttext 0x200000 -o kernel.bin c.o video.o inter.o port.o
finter.o kernel.o
nasm -fbin -o boot2.bin boot2.asm
nasm -fbin -o all_image.bin boot1.asm
(В текст второго загрузчика в конце теперь надо добавить kernel_binary: incbin ‘kernel.bin’)
Итак, с этой частью статьи все. Мы научились работать с железом на простейшем
уровне, смогли наконец-то заставить работать набор всех этих кодов. В следующей
(уже более объемной и серьезной) части будет рассматриваться работа с жестким
диском и файловой системой.