Почему ядро linux написано на c а не c

Обновлено: 07.07.2024

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

Так что же делает ядро Linux и почему оно так востребовано? Мы рассмторим архитектуру ядра, его основные задачи и интерфейсы. Это поможет понять его преимущества и недостатки.

Что такое ядро Linux

1. На чём написано ядро

Несмотря на то, что ассемблерный код позволяет достичь наилучшей производительности, его возможности весьма ограничены, поэтому большая часть кода написана на языке C, его доля достигает 98%. На ассемблере написаны только небольшие вставки, повышающие производительность, архитектурно-зависимые функции и загрузчик.

2. Архитектура ядра

Уровень доступа к ресурсам компьютера зависит от того, какое ядро использует операционная система. Привилегии ядра выше остальных приложений, а работает оно в едином адресном пространстве. В зависимости от того, сколько задач выполняется на уровне ядра, различают несколько типов ядер. Самые популярные – это монолитное (Linux), микроядро (macOS) и гибридное (Windows).


Ядро Linux монолитное, большая его часть хранится в одном файле. Однако, это не признак монолитного ядра, модули вполне могут храниться отдельно. Основная его особенность заключается в том, что оно обрабатывает все процессы, кроме пользовательских приложений. То есть управление процессами и памятью, драйверы, виртуальная файловая система, сетевой стек и многое другое – это всё заботы ядра, которые к тому же имеют самый высокий уровень доступа к аппаратной части компьютера.

Однако, это не означает то, что пользовательские приложения не могут выполнять схожие функции. Например, система инициализации Systemd помимо прочего выстраивает иерархию процессов поверх групп ядра cgroups, а демоны, вроде PulseAudio, контролируют работу устройств, расширяя функциональность драйверов.

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

Интерфейсы, имена переменных и структура каталогов системы определяются стандартами POSIX, что делает Linux UNIX-подобной системой. Линус Торвальдс, создатель ядра, выбрал UNIX по той причине, что имелась база приложений, необходимых для функционирования операционной системы, утилиты GNU. Однако, он не разделяет идеи философии UNIX, одна программа – одно действие, текстовый вывод информации как универсальный интерфейс. По его мнению они не отражают запросы современных пользователей.

3. Что делает ядро

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

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

Драйверы занимают большую часть ядра. Некоторые из них представлены сразу в виде бинарных файлов, что противоречит идеям фонда СПО. Версия ядра без закрытых драйверов называется Linux-libre, на практике его использование крайне затруднительно, так как собрать компьютер на основе комплектующих только с открытыми драйверами у вас едва ли получится.

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

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

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

Несмотря на то, что ядро контролирует все процессы, само по себе оно ничего не делает, ему нужны пользовательские программы и их процессы. Среди базовых приложений стоит отметить утилиты проекта GNU, без них не обходится ни один дистрибутив Linux. Например, командная оболочка Bash позволит вам вводить команды в консоли.

4. Версии ядра

Запись версии ядра можно представить в виде: A.B.C-D.

  • A – это версия ядра, изначально планировалось повышать номер только после значительной переработки ядра, но сейчас это делают после достаточного количества правок и нововведений примерно два раза за десятилетие.
  • B – это ревизия ядра, обновление происходит каждые 2-3 месяца. Некоторые из них получают долгосрочную поддержку (LTS – long term support). Последним таким ядром стало 5.10. Каждая ревизия имеет большой список изменений, которые сначала проверяют тестировщики.
  • C и D отвечают за небольшие правки в коде ядра. С увеличивается в том случае, если были обновлены драйверы устройств, а D – когда вышел очередной патч безопасности. Эти номера могут меняться практически каждый день.

Узнать версию ядра можно с помощью команды:

5. Где хранятся файлы ядра

8C3vNCEd+UxpcAAAAASUVORK5CYII=

Файлы ядра хранятся в каталоге /boot. Непосредственно само ядро находится в запакованном виде в файле vmlinuz, где z как раз и указывает на то, что ядро сжато для экономии места. Файл initrd.img – это первичная файловая система, которая монтируется перед тем, как подключить реальные накопители к виртуальной файловой системе VFS. Там же содержатся дополнительные модули ядра, поэтому этот файл может быть больше самого ядра. В файле system.map можно найти адреса функций и процедур ядра, что будет полезно при отладке.

Выводы

Подведём итоги. Теперь вы знаете что такое ядро Linux. Ядро — это самая привилегированная программа на компьютере. Если говорить конкретно о ядре Linux, то оно монолитное. Иными словами, в режиме ядра работает всё необходимое для управления ресурсами компьютера. В пользовательском режиме также имеются программы для управления, но они лишь расширяют возможности ядра.

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

К примеру, Android использует ядро Linux, но не утилиты GNU и в целом не пытается стать похожим на UNIX, что во многом обеспечило его популярность. Так что ядро – это лишь инструмент, а цели могут быть любыми, от запуска терминала и до создания суперкомпьютеров.

Пишут. На ЛОРе пробегала ссылка на ядро Linux, написанное на C++. Я его даже скачивал ;).

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

Вон симбиан написали. И.

Лучше б не брались.

> На ЛОРе пробегала ссылка на ядро Linux, написанное на C++. Я его даже скачивал ;).

Тогда это уже ядро не Linux :)

> Патамушта моск у разработчиков ядра работает в нативном режиме и на полной скорости, а не эмулирует мыслительный процесс через сторонний байткод с потерей производительности в сотни раз.

А разве С++ компилируется в байт-код?? Это ведь не Java!

> Вон симбиан написали. И.

И что, плохая операционка?

BeOS тоже был на C++. Отличная штучка ;)


ожидаем глобальный переход на язык пасцал (pascal)!

>> И что, плохая операционка?

BeOS был все-таки на C, а переписывают его уже на С++. А чем же с сишным ядром плохо живется?

На Си? Не знал. Раньше слухи ходили, что это было Pure C++, и что это давало серьёзные преимущества.

А с Сишным всё нормально ) Мне просто на самом деле интересно просто, почему же не используют Си++.

> На Си? Не знал. Раньше слухи ходили, что это было Pure C++, и что это давало серьёзные преимущества.

Преимущества полагается предавать огласке. Минимум - тут, в идеале - мылом Линусу и на lkml.

> А с Сишным всё нормально ) Мне просто на самом деле интересно просто, почему же не используют Си++.

Вопрос ставится неправильно. А должен быть разбит на 3:

1. зачем пихать нечто во все щели? 2. зачем вообще использовать с++ для написания кода ядра, если есть с? 3. зачем вообще использовать что-то сложное, если есть нечто более простое, полностью покрывающее потребности?

Ну и вспоминаем собственно времена когда ядро писалось, наличие стандартов, до сих пор не устаканенный ABI, и т.п. Потом понимаем, почему таки с++ не используется. Собстна, каким хреном потом делать из glibc системные вызовы? Сначала ядро на с++, потом в него сувать врапперы для обеспечения совместимости, потом - . что получим? ;)

Патамушта C++ язык программирования более высокого уровня. А выхлоп от этого высого уровня есть только в прикладном софте. В системном софте инкапсуляция, наследование - как козе баян, да и еще сильная потеря производительности, так как системный софт - критическое место. Еще неочевидный плюс, С - устоявшийся язык, с конца 70 не менялся, а C++ - постоянно новые вариации, не меньше 2х за последнии 10 лет.


Каким боком системные вызовы glibc связаны с языком, на котором написано ядро? Там-же не прямой вызов. =)

ABI компилятора меняется не так часто, как сейчас ABI ядра.

1. Хороший, блин, вопрос. Почти даже риторический, правда C++ ни кто ни куда не пихает.

2/3. Зачем использовать C, если есть asm? Imho C++ не от нефигделать придумывали.

>В системном софте инкапсуляция, наследование - как козе баян, да и еще сильная потеря производительности

В каком именно месте потеря производительности? И почему именно в системном софте не важна инкапсуляция? Imho она везде важна, и это больше относится к проектированию.

>Еще неочевидный плюс, С - устоявшийся язык, с конца 70 не менялся, а C++ - постоянно новые вариации, не меньше 2х за последнии 10 лет.

C98? И что хорошего в том, что якобы "с конца 70 не менялся"?

Imho они оба устоялись, и уже достаточно давно.


> Патамушта моск у разработчиков ядра работает в нативном режиме и на полной скорости, а не эмулирует мыслительный процесс через сторонний байткод с потерей производительности в сотни раз.

Нет слов, одни эмоции. В том, что С++ заимствовал у Си, С++ б-и-н-а-р-н-о совместим с Си. Какой нахер байт-код, что вы курите?


> Патамушта C++ язык программирования более высокого уровня. А выхлоп от этого высого уровня есть только в прикладном софте.

Почто так. Однако Си точно так же является языком высокого уровня. Дело в том, что ядро Linux тесно взаимодействует с железом и в нем используются только встроеные типы данных и близкие к встроенным. Там уровни абстракции и принципы сокрытия данным не используются. Linux очень плотно работает с железом, в отличии от того же Solaris например и возможности С++ ядру не нужны.

> Нет слов, одни эмоции. В том, что С++ заимствовал у Си, С++ б-и-н-а-р-н-о совместим с Си. Какой нахер байт-код, что вы курите?

Ниасилил вашу логику, вообще-т я говорил про моск ;) Так что еще вопрос кто из нас и что курит. прямо с утра ;))

> Почто так. Однако Си точно так же является языком высокого уровня.

Бобер, выдыхай, с - это на самом деле прикинувшийся шлангом питон.

А если серьезно - то с сейчас является практически макроассемблером, а с++ - таки средне-высокий уровень. Ну то есть эволюция, классификации меняются, мозги скрипят. Посему видим непонятные желания типа переписи кернела на с++ и ни слова о преимуществах данного подхода.

> Дело в том, что ядро Linux тесно взаимодействует с железом и в нем используются только встроеные типы данных и близкие к встроенным. Там уровни абстракции и принципы сокрытия данным не используются. Linux очень плотно работает с железом, в отличии от того же Solaris например и возможности С++ ядру не нужны.

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


> Ниасилил вашу логику, вообще-т я говорил про моск ;) Так что еще вопрос кто из нас и что курит. прямо с утра ;))

В таком случае, не нужно нести ахинею и тем самым вводить заблуждение остальных. С логикой у меня всё в поряде, вот с телепатическими способностями туго. Это у вас утро, у нас уже рабочий день к концу близится :)

> Каким боком системные вызовы glibc связаны с языком, на котором написано ядро? Там-же не прямой вызов. =)

Угу, там вызов через кроссплатформенный графический интерфейс на Tk.

> ABI компилятора меняется не так часто, как сейчас ABI ядра.

Еще одно угу. а если абстрагироваться и подумать, что есть не только gcc на свете? ABI ядра = ABI любого сишного компилятора, по сему поводу вообще думать не нужно, что есть великий плюс С.

> 1. Хороший, блин, вопрос. Почти даже риторический, правда C++ ни кто ни куда не пихает.

XML - тоже не пихают, а некоторые далбайопы даже логи в него пишут метров по 30-40, и потом xslt-парсером в рантайме разгребают. аналогия ясна? ;)

> 2/3. Зачем использовать C, если есть asm? Imho C++ не от нефигделать придумывали.

Дык не отмазка, на асме писаны критические участки кода, все в порядке с этим. С++ придумали ИМХО на свою голову именно от нечего делать, т.к. единственный его опосредованный плюс - возможность в IDE просматривать классы деревьями, но сие перебивается косяками и недостатками.

> В каком именно месте потеря производительности? И почему именно в системном софте не важна инкапсуляция? Imho она везде важна, и это больше относится к проектированию.

Угу, а ты тоже выдыхай :) Вот ты сказал "везде важна" - согласно правилам, и будь добр это доказать, т.к. что-то утверждаешь. Непонятно в каком месте? Ну давай тогда писать софт вообще без оптимизаций, хуле, железо прогрессирует, ядра становятся все толще. Глядишь там через годик напишем ядро, которое под себя будет требовать ресурсы достаточные для параллельного запуска трех Свист от мелкософта.

> C98? И что хорошего в том, что якобы "с конца 70 не менялся"?

ABI, прогрессировали оптимизаторы, etc. Стабильность всегда хороша.

> Imho они оба устоялись, и уже достаточно давно.

> В таком случае, не нужно нести ахинею и тем самым вводить заблуждение остальных. С логикой у меня всё в поряде, вот с телепатическими способностями туго. Это у вас утро, у нас уже рабочий день к концу близится :)

Офттопик. на КГ/АМ вы все же не тянете ;) И разумеется чего-то конструктивного от вас как. дождаться можно? ;)


> Нет слов, одни эмоции. В том, что С++ заимствовал у Си, С++ б-и-н-а-р-н-о совместим с Си. Какой нахер байт-код, что вы курите?

> В таком случае, не нужно нести ахинею и тем самым вводить заблуждение остальных. С логикой у меня всё в поряде, вот с телепатическими способностями туго. Это у вас утро, у нас уже рабочий день к концу близится :)

Гм. может стоит к родине поближе, чувство юмора прокачать, эээ? :)

> Оно и заметно, что скипят. :))) > > - Карэн, а срэднэ-вэсокий уровэн язэка программирования, это как? > - Срэднэ-вэсокий, Гоги, это на уровнэ груд.

Хороший вечер, дарагой, эээ? ;) Вот такие вот грудастые и предлагают писать ядро на с++ %))

Лана, если с - высокий уровень, то асм выходит - средний. И что тогда является низким? Мсье тру-хардкорный кодер в машинных кодах? :)



> В чем костыльность extern инструкций? Они нужные для однозначного определения принципа линковки смешеного кода, в принципе без них можно тоже обойтись в ущерб переносимости.

А зачем нам такие извраты как смешанный код? Т.е. фактически С++ не имеют обратной совместимости с С-коммпиляторами. Костыльность их в том, что даже такой "великолепный" и "удобный" язык, как С++ не смог обойтись без обратной совместимости поначалу, а чуть позднее - от нее избавиться, следствия очевидны - тащим еще и С-шные правила за собой, проявляя страшную непоследовательность.

> $ g++ misc.cc -o misc > $./misc > 4

А теперь `ldd ./misc` и сильно удивляемся лишней строчке.

> Для меня это проблематично, т.к. я для ядра модулей не писал. Почему бы вам самому не попробовать.

Я заранее знаю результат и в силу сего - неинтересно ;)

Одна из причин проста - ядро собирается с `-nostdinc`, а посему как минимум в ядро придется засунуть все С++-ные интерфейсы, и еще и либку статически загнать не забыть. А такое счастье как увеличение потребления памяти в пару раз на ровном месте разве нам нужно?

> Пардон. Да, в случае с C++ либами, Си-шный бинарник компилируется средствами C++. Это проблема?

Это проблема. См. calling consentions и т.д. и т.п. Ну и о mangling ни в коем разе не забываем, dlsym() по одной и той же строке корректно отработает в С с лохматого года, чего нельзя вообще сказать о С++.

Вообще - даже в коротеньком треде уже проблем с плюсами нашли с десяток, все еще просматривается необходимость переписывать ядро на с++?


> Каким боком системные вызовы glibc связаны с языком, на котором написано ядро? Там-же не прямой вызов. =)

>Угу, там вызов через кроссплатформенный графический интерфейс на Tk.

Поправьте, если не прав, но вызов там проходит через прерывание.

Поэтому mangling врядли особо помешает. Я говорил об этом.

>Еще одно угу. а если абстрагироваться и подумать, что есть не только gcc на свете? ABI ядра = ABI любого сишного компилятора, по сему поводу вообще думать не нужно, что есть великий плюс С.

imho 99% модулей ядра компилятся gcc(асмовые куски на gas ни чем другим не скомпилишь), хотя согласен, возможны проблеммы с закрытыми дровами.

>XML - тоже не пихают, а некоторые далбайопы даже логи в него пишут метров по 30-40, и потом xslt-парсером в рантайме разгребают. аналогия ясна? ;)

Вот XML как-раз пихают, но я тут не при чем. Я и не пытаюсь всех убедить, что все надо на плюсах.

>Дык не отмазка, на асме писаны критические участки кода, все в порядке с этим. С++ придумали ИМХО на свою голову именно от нечего делать, т.к. единственный его опосредованный плюс - возможность в IDE просматривать классы деревьями, но сие перебивается косяками и недостатками.

Пример на инкапсуляцию: Не встречали в описании функций ядра "Чтобы вызвать эту функцию зажмите спинлоки A и B, если забудите отпустить то будет жопа", может не стоит такую функцию делать public, а стоит сделать к ней wraper, зажимающий и отпускающий спинлоки?

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

Полиморфизма там и сейчас хватает, правда через жопу сделанного, но хватает.

Насчет асма: Ни кто не мешает делать вставки на асме, и особых тормозов от одного слова class тоже не прибавится. Почему из фразы "на C++" следует, фраза "без оптимизации"?

Потому что Линус Торвальдс занимается (или занимался, не уверен насчёт текущего уровня его активности) преимущественно разработкой ядра ОС, а там лишние абстракции действительно могут только мешать.

Скандинав, значит у него загадочная душа викинга


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

Потому что Линус Торвальдс сам же и говорил что он уже старый и не способен принимать новые технологии.
C++ не любят только три типа людей: Быдло кодеры, Школьники, Старики.
Более того С++ хорош тем что очень легко отсеиваются некомпетентные в нём люди. особенно такие как автор вопроса. который является простым школьником которой обозлился на С++, на котором в свою очередь, написано большая часть нативного кода. Что косается OS то Linux написана на С совершенно из-за других причин про которые чуть позже расказывал Торвальдс: "как пишу код так и генерируется, для строгих и не сложных систем это лучший вариант, будь ядро чуть посложней и сам же язык С стал бы для нас ловушкой". С++ позволяет делать огромное количество вещей в сотни а то и в тысячю раз проще особенно когда дело касается алгоритмически сложных систем такие как ядерная физика, компьютерные игры, сложный софт, компиляция.
*Базон хигса считали на С++
*Ядерные реакторы работают на С++
*Весь космос работает на С++
*Все AAA игровые движки написаны на С++
*Браузеры работают на С++
*Абсолютно всё медицинское оборудование
*Все высоко нагруженые сетевые сервисы.
*Виртуальные машины
*Автопром/AI
*Драйвера видеокарт пишутся только на С++
*Поисковые движки
*CERN
*Офис
* компиляторы
*Мат-вычислители, и идругие невероятно сложные архитектурно системы написаны на С++.
*Недавно С++ получил всемирную награду за вклад в кинематограф
C остается главным языком там где что-то потенциально устарело либо было задумано так.

Потому что кресты крестьянам. а мы, бояре, пишемъ на человекочитаемых, красивых языках))

    , Аноним, 11:23 , 22-Апр-09, (1)
      , const86, 13:22 , 22-Апр-09, (2)
        , worman, 13:40 , 22-Апр-09, (3)
          , Аноним, 14:25 , 22-Апр-09, (4) , ALu, 16:35 , 22-Апр-09, (5)
          , svn, 01:56 , 23-Апр-09, (8)
            , poulch, 07:44 , 23-Апр-09, (9)
              , poulch, 08:09 , 23-Апр-09, (11)
              , svn, 21:12 , 23-Апр-09, (13)
              , DimaG, 11:22 , 05-Май-09, (16)
                , svn, 14:39 , 05-Май-09, (17)
                  , DimaG, 17:08 , 05-Май-09, (19)
                    , svn, 21:58 , 05-Май-09, (22)
                      , DimaG, 06:58 , 06-Май-09, (23)
                        , DimaG, 07:02 , 06-Май-09, ( 24 )
                          , DimaG, 08:15 , 06-Май-09, ( 25 )
                            , svn, 13:42 , 06-Май-09, ( 26 )
                            , DimaG, 17:11 , 05-Май-09, (20)
                              , poulch, 14:38 , 12-Май-09, ( 28 )
                                , worman, 06:34 , 13-Май-09, ( 29 )
                                  , poulch, 11:11 , 13-Май-09, ( 30 )
                                >Для gcc для зборки с++ надо подключать -lstdc++ .

                                А вот и не надо. Так же как нельзя подключать libc для сишного кода в ядре. Вот только какой C++ получится без libstdc++. Сомневаюсь, что выйдет что-то разумное.

                                Философский вопрос про яйца :), что первично ядро или glibc.
                                >Хочеться оргументированный ответ чем плох С++ для ядра линукса.

                                Linux creator Linus Torvalds joined in to explain:

                                "In fact, in Linux we did try C++ once already, back in 1992. It sucks. Trust me - writing kernel code in C++ is a BLOODY STUPID IDEA.

                                "The fact is, C++ compilers are not trustworthy. They were even worse in
                                1992, but some fundamental facts haven't changed: 1) the whole C++ exception handling thing is fundamentally broken. It's _especially_ broken for kernels. 2) any compiler or language that likes to hide things like memory allocations behind your back just isn't a good choice for a kernel. 3) you can write object-oriented code (useful for filesystems etc) in C, _without_ the crap that is C++."

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

                                c++ в ядре на самом деле не нужен. Мне жаль тех кто этого не понимает. Подумайте почему ядро не может быть написано на интерпретируемом языке. С++ практически тоже самое.

                                stl в ядре - тоже ржака. buffer.push_back() в обработчике прерывания собрались применять?

                                мне там не нужен stl. мне там нужны просто классы. даже эксепшен хандлинг не нужен. мне просто надо получить стабильный код драйвера через framework, учитывая постоянные мутации ядра. Кроме этого я кросс девелопер драйверов. Хочу общий код драйвера для windows и linux. Сейчас это получается только на уровне библиотек, хотя логически код дров и получается общий.
                                причем я не настаиваю на использовании С++ в ядре. Я хочу чтобы просто убрали искусственное ограничение на использование С++,а дальше уж как пойдет. народ сам решит.
                                >Linux creator Linus Torvalds.

                                Мнение Торвальдса мне известно, хотельсь мнение вменяемых людей узнать.

                                >В дравере!? STL контейнеры!? . в стране нехватка дворников - меняй профессию!

                                Если не знаю чего-либо - буду разбираться. Менять профессию - не мой путь.

                                >Ты сам приводил: << Мы все знаем чт.

                                Ага, сам. Понравилось?

                                >нифига не выйдет. ключевые слова С++ использованы как переменные и тп в коде ядра в контексте С.

                                Спосибо, не подумал об этом.

                                >Подумайте почему ядро не может быть написано на интерпретируемом языке.

                                Ну ты сравнил С++ с интерпретируемым языком. С++ практически не уступает С.

                                >stl в ядре - тоже ржака. buffer.push_back() в обработчике прерывания собрались применять?

                                Нет не в обработчике прерываний. Минус STL вижу в том что возможны в процессе работы динамическое выделение памяти. Это надо учитывать при написании кода. Бороться с этим можно.

                                -----
                                В общем С++ как вариант для драйвера больше не рассматриваю.
                                Ни одного мнения за плюсы нет.
                                Всем большое спасибо.

                                >С++ практически не уступает С.

                                И java не уступает C ))
                                Но и java и php и c++ не могут жить без рантайм библиотек. А рантайм библиотеке с++ в ядре совсем не рады.

                                Реализация шаблонов вызывает рост экспортируемых имён.
                                А зачем нужны классы если не нужны исключения или виртуальные функции? Ты видел как пишеться объектный код на C в ядре linux?

                                >В общем С++ как вариант для драйвера больше не рассматриваю.

                                +1

                                >Здравствуйте!
                                >
                                >Пишу драйвер для ucLinux и хочу реализовать драйвер на С++, а не
                                >на С.

                                Основное препятствие использования С++ в ядре (в частности в драйвере). То что ядро оперирует такими понятиями как данные (биты и байты) и действия над ними , С++ оперирует понятиями объекты и взаимодействие объектов. Совместить ни как не получится.
                                То есть в лучшем случаи создашь С код и откомпилируешь его С++ компилятором.
                                Да и потом С++ компилятор утяжеляет исполняемый код, а для ядра скорость выполнения и размер кода имеет первоочередное значение. В силу того что делаешь драйвер для uclinux.
                                А данный дистрибутив в основном применяют на микроконтроллерах.

                                P.S. Сам ставил ucLinux на микроконтроллеры и драйвера писал, желание сделать их на С++ не возникало.


                                >Наследования, шаблоны. В своих проектах (на том же BF537) использую все, кроме
                                >RTTI.

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

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

                                >Ну не совсем точно выразился (название Embedded C++ уже зарезервировано)) ). Ладно,
                                >чем хуже _разумно_ писать на Си++, чем на Си?

                                Ты читал что написано выше?
                                Если разумно писать на C++ по твоему, это означает не пользоваться его основными возможностями, то я считаю, лучше в таком случае чистый C.

                                >>Наследования, шаблоны. В своих проектах (на том же BF537) использую все, кроме
                                >>RTTI.
                                >
                                >Наследование без виртуальных методов и исключений - обычное вкладывание одной структуры данных
                                >в другую, прекрасно пишется и на C.

                                Виртуальные методы не используются в данном проекте (scmRTOS), но активно используются в других. Кстати, а чем они вас так напрягают? В ядре линукса они тоже (в сишном виде) используются.

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

                                Шаблоны раздувают код? Имеете в виду, что будет множество инстанцированний? Опять же - зависит от кривизны рук программиста. Кстати, а дефайны не раздувают? )))))

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

                                >Тут главное суть - удобство факта существования конструктора
                                >/ деструктора.

                                Если и удобно оно, то только для автоматических переменных. А учитывая, что автоматических переменных в ядре нет (экономить стек надо, в стеке только указатели на структуры), никакого удобства не получается. Явный new + delete ничем не лучше функций init + fini.


                                >Или создание объекта до main()

                                В ядре нет main. И порядок создание объектов в ядре linux очень чёткий, понятный и контролируемый.

                                >Шаблоны раздувают код? Имеете в виду, что будет множество инстанцированний? Опять
                                >же - зависит от кривизны рук программиста. Кстати, а дефайны не
                                >раздувают? )))))

                                Макросы раздувают, но они не вносят проблемы линковки. В последнее время всё чаще используются inline функции.

                                >И в чем сложность линковки? (хотя бы - лично для вас?)

                                В том что линковщик должен определить что это шаблон, и не нервничать встретив дубликата при линковке, а просто его потерять. Это сложность, не приемлемая во время линковки (загрузки) загружаемого модуля ядра.

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

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

                                В ядре однозначно лучше убрать статиком, чем прятать за префиксом.

                                >видно, что уровень владения языком С++ на уровене "знаю человека, троюродный брат которого

                                А ниже - Ваш аргумент в русле "слышал от человека про реальную ос на плюсах -- сказыал всех делает по параметру чего-то там в носу"? Хорошо, что Вы внесли Конструктифф в эту безобразную перебранку, Спасибо!

                                >В качестве примера хороших разработок - можете глянуть embedded RTOS - ScmRTOS.
                                >Написана на плюсах (ес-но, есть места на ассмемблере - без этого
                                >никак). Причем, эта операционка показывает одни из лучших параметров (размер кода,
                                >ресурсы, реакция на события) в своем классе.

                                >А ниже - Ваш аргумент в русле "слышал от человека про реальную
                                >ос на плюсах -- сказыал всех делает по параметру чего-то там
                                >в носу"? Хорошо, что Вы внесли Конструктифф в эту безобразную >перебранку,

                                Почему же? Не только слышал, но и использовал в своих работах. За что огромное спасибо автору. Кстати, ее многие железячники на электрониксе пользуют..


                                >Спасибо!

                                Пожалуйста :)

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

                                Дело не в скорости итп. основное это возможность изящно "стабилизировать" API ядра посредством разноообразных фрэймворков для драйверов, не загубив гибкости развития. но похоже пока у энтузиастов хватает сил ползать по всему коду драйвероа и патчить его под новые API.
                                >Дело не в скорости итп. основное это возможность изящно "стабилизировать" API ядра
                                >посредством разноообразных фрэймворков для драйверов, не загубив гибкости развития. но похоже
                                >пока у энтузиастов хватает сил ползать по всему коду драйвероа
                                >и патчить его под новые API.

                                Не понял что ты имеешь ввиду. Какие фреймворки.
                                Исходная задача такая была: есть микросхема для работы с потоками E1, есть ucLinux, надо реализовать протокол канального уровня. Протокол канального уровня и реализуеться в виде драйвера. Таким образом драйвер пишеться с нуля и привязан к микросхеме.
                                Был вопрос в выборе языка C или С++. Я преверженец С++ вот и интересовался как обстоит дело с С++ в ядре.

                                Пэ.Эс. Если у тебя есть пара фреймфорков реализующих телекоммуникационные протоколы, дай плиз :-)

                                UNIX-подобная операционная система

                                Что такое UNIX-подобная операционка? Это ОС, созданная под влиянием UNIX. Но прежде чем заняться написанием ядра для нее, давайте посмотрим, как машина загружается и передает управление ядру.

                                Большинство регистров x86 процессора имеют четко определенные значения после включения питания. Регистр указателя инструкций (EIP) содержит адрес памяти для команды, выполняемой процессором. EIP жестко закодирован на значение 0xFFFFFFF0. Таким образом, у процессора есть четкие инструкции по физическому адресу 0xFFFFFFF0, что, по сути, – последние 16 байт 32-разрядного адресного пространства. Этот адрес называется вектором сброса.

                                Теперь карта памяти чипсета гарантирует, что 0xFFFFFFF0 сопоставляется с определенной частью BIOS, а не с ОЗУ. Между тем, BIOS копирует себя в ОЗУ для более быстрого доступа. Это называется затенением (shadowing). Адрес 0xFFFFFFF0 будет содержать только инструкцию перехода к адресу в памяти, где BIOS скопировал себя.

                                Таким образом, код BIOS начинает свое выполнение. Сначала BIOS ищет загрузочное устройство в соответствии с настроенным порядком загрузочных устройств. Он ищет определенное магическое число, чтобы определить, является устройство загрузочным или нет (байты 511 и 512 первого сектора равны 0xAA55).

                                После того, как BIOS обнаружил загрузочное устройство, он копирует содержимое первого сектора устройства в оперативную память, начиная с физического адреса 0x7c00; затем переходит по адресу и выполняет только что загруженный код. Этот код называется системным загрузчиком (bootloader).

                                Затем bootloader загружает ядро ​​по физическому адресу 0x100000. Адрес 0x100000 используется как стартовый адрес для всех больших ядер на x86 машинах.

                                Все x86 процессоры стартуют в упрощенном 16-битном режиме, называемом режимом реальных адресов. Загрузчик GRUB переключается в 32-битный защищенный режим, устанавливая младший бит регистра CR0 равным 1. Таким образом, ядро ​​загружается в 32-разрядный защищенный режим.

                                Обратите внимание, что в случае обнаружения ядра Linux, GRUB получит протокол загрузки и загрузит ​​Linux-ядро в реальном режиме. А ядро Linux сделает переключение в защищенный режим.

                                Что нам понадобится?

                                • x86 компьютер (разумеется)
                                • Ассемблер NASM
                                • GCC
                                • ld (GNU Linker)
                                • GRUB
                                • Исходный код

                                Ну и неплохо было бы иметь представление о том, как работает UNIX-подобная ОС. Исходный код можно найти в репозитории на Github.

                                Для начала напишем небольшой файл в x86 ассемблере, который будет отправной точкой для запуска ядра. Этот файл будет вызывать внешнюю функцию на C, а затем остановит поток программы.

                                Как убедиться, что этот код послужит отправной точкой для ядра?

                                Мы будем использовать скрипт компоновщика, который связывает объектные файлы с целью создания окончательного исполняемого файла ядра. В этом скрипте явно укажем, что бинарный файл должен быть загружен по адресу 0x100000. Этот адрес, является тем местом, где должно быть ядро.

                                Первая инструкция bits 32 не является инструкцией сборки x86. Это директива для ассемблера NASM, которая указывает, что он должен генерировать код для работы на процессоре, работающем в 32-битном режиме. Это не обязательно требуется в нашем примере, однако это хорошая практика – указывать такие вещи явно.

                                Вторая строка начинается с текстового раздела. Здесь мы разместим весь наш код.

                                global - еще одна директива NASM, служит для установки символов исходного кода как глобальных.

                                kmain - это собственная функция, которая будет определена в нашем файле kernel.c. extern объявляет, что функция определена ​​в другом месте.

                                Функция start вызывает функцию kmain и останавливает CPU с помощью команды hlt. Прерывания могут пробудить CPU из выполнения инструкции hlt. Поэтому мы предварительно отключаем прерывания, используя инструкцию cli.

                                В идеале необходимо выделить некоторый объем памяти для стека и указать на нее с помощью указателя стека (esp). Однако, GRUB делает это за нас, и указатель стека уже установлен. Тем не менее, для верности, мы выделим некоторое пространство в разделе BSS и поместим указатель стека в начало выделенной памяти. Для этого используем команду resb, которая резервирует память в байтах. После этого остается метка, которая указывает на край зарезервированного фрагмента памяти. Перед вызовом kmain указатель стека (esp) используется для указания этого пространства с помощью команды mov.

                                В kernel.asm мы сделали вызов функции kmain(). Таким образом, код на C начнет выполнятся в kmain():

                                Наше ядро ​​будет очищать экран и выводить на него строку «my first kernel».

                                Для начала мы создаем указатель vidptr, который указывает на адрес 0xb8000. Этот адрес является началом видеопамяти в защищенном режиме. Текстовая память экрана – это просто кусок памяти в нашем адресном пространстве. Ввод/вывод для экрана на карте памяти начинается с 0xb8000 и поддерживает 25 строк по 80 ascii символов каждая.

                                Каждый элемент символа в этой текстовой памяти представлен 16 битами (2 байта), а не 8 битами (1 байт), к которым мы привыкли. Первый байт должен иметь представление символа, как в ASCII. Второй байт является атрибутным байтом. Он описывает форматирование символа, включая разные атрибуты, например цвет.

                                Чтобы напечатать символ с зеленым цветом на черном фоне, мы сохраним символ s в первом байте адреса видеопамяти и значение 0x02 во втором байте.

                                0 - черный фон, а 2 - зеленый.

                                Ниже приведена таблица кодов для разных цветов:

                                В нашем ядре мы будем использовать светло-серые символы на черном фоне. Поэтому наш байт атрибутов должен иметь значение 0x07.

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

                                Во втором цикле while символы строки «my first kernel» записываются в кусок видеопамяти. Для каждого символа атрибутный байт содержит значение 0x07.

                                Таким образом, строка отобразится на экране.

                                Мы собираем kernel.asm и NASM в объектный файл, а затем с помощью GCC компилируем kernel.c в другой объектный файл. Теперь наша задача – связать эти объекты с исполняемым загрузочным ядром.

                                Для этого мы используем явный скрипт компоновщика, который можно передать как аргумент ld (наш компоновщик).

                                Во-первых, мы устанавливаем выходной формат исполняемого файла как 32-битный исполняемый (ELF). ELF – стандартный формат двоичного файла для Unix-подобных систем на архитектуре x86.

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

                                SECTIONS – самая важная часть, где мы определяем разметку исполняемого файла. Здесь указывается, как должны быть объединены различные разделы и в каком месте они будут размещаться.

                                В фигурных скобках, следующих за инструкцией SECTIONS, символ периода (.) – представляет собой счетчик местоположения.

                                Счетчик местоположения всегда инициализируется до 0x0 в начале блока SECTIONS. Его можно изменить, присвоив ему новое значение.

                                Как уже говорилось, код ядра должен начинаться с адреса 0x100000. Таким образом, мы установили счетчик местоположения в 0x100000.

                                Посмотрите на следующую строку .text:

                                Звездочка (*) является спецсимволом, который будет соответствовать любому имени файла. То есть, выражение *(.text) означает все секции ввода .text из всех входных файлов.

                                Таким образом, компоновщик объединяет все текстовые разделы объектных файлов в текстовый раздел исполняемого файла по адресу, хранящемуся в счетчике местоположения. Раздел кода исполняемого файла начинается с 0x100000.

                                После того, как компоновщик разместит секцию вывода текста, значение счетчика местоположения установится в 0x1000000 + размер раздела вывода текста.

                                Аналогично, разделы данных и bss объединяются и помещаются на значения счетчика местоположения.

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

                                Существует стандарт для загрузки различных x86 ядер с использованием загрузчика, называемый спецификацией Multiboot.

                                GRUB загрузит ядро только в том случае, если оно соответствует Multiboot-спецификации.

                                Согласно ей, ядро должно содержать заголовок в пределах его первых 8 килобайт.

                                Кроме того, этот заголовок должен содержать дополнительно 3 поля:

                                • поле магического числа: содержит магическое число 0x1BADB002, для идентификации заголовка.
                                • поле флагов: сейчас оно не нужно, просто установим его значение в ноль.
                                • поле контрольной суммы: когда задано, должно возвращать ноль для суммы с первыми двумя полями.

                                Итак, kernel.asm будет выглядеть таким образом:

                                Теперь создадим объектные файлы из kernel.asm и kernel.c, а затем свяжем их с помощью скрипта компоновщика.

                                запустит ассемблер для создания объектного файла kasm.o в формате 32-битного ELF.

                                Параметр «-c» гарантирует, что после компиляции связывание не произойдет неявным образом.

                                запустит компоновщик с нашим скриптом и сгенерирует исполняемое именованное ядро.

                                UNIX-подобная ОС с ее ядром почти поддалась. GRUB требует, чтобы ядро имело имя вида kernel-<version>. Переименуйте ядро, к примеру, в kernel-701.

                                Теперь поместите его в каталог /boot. Для этого вам потребуются права суперпользователя.

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

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

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

                                Ядро в работе

                                Это ваше ядро! Оказывается, UNIX-подобная операционная система и ее составляющие не так уж сложны, верно?

                                • Всегда желательно использовать виртуальную машину для всех видов взлома ядра.
                                • Чтобы запустить это ядро на grub2, который является загрузчиком по умолчанию для более новых дистрибутивов, ваша конфигурация должна выглядеть так:

                                Если вы хотите запустить ядро на эмуляторе qemu вместо загрузки с помощью GRUB, вы можете сделать так:

                                Теперь вы имеете представление о том, как устроены UNIX-подобная ОС и ее ядро, а также сможете без труда написать последнее.

                                Читайте также: