LINUX.ORG.RU

ЯП, ОС, архитектура процессоров, инструкции

 , , , ,


1

3

Заглянув за кулисы своего компьютерного мирка, получил кучу мозаичных кусочков.
1. Вот есть у нас процессор со своей архитектурой, будь то X86 или ARM. Под нее портируют компиляторы топовых языков программирования путем использования процессорных инструкций вроде SSE. Что есть оптимизации компилятора? Вот написан код, компилятор его перековеркал для лучшей производительности на определенной архитектуре при использовании тех или иных инструкций процессора. Но всегда ли выбранный компилятором способ коверканья определенного куска кода можно считать оптимальным? Есть оптимизации на потребление оперативной памяти, производительности, размера кода, да что уж там - всяких «волшебных ключиков» просто тьма, успевай только тестировать их. Обилие этих ключиков соединяются в группы оптимизаций по параметрам. ОС пишут на компиляторе ЯП, компилятор завязан на ОС для компилирования других проектов - иначе не было бы таких разделений вроде: «поддерживаемые платформы: MS Windows, Linux, Android» и т.п. Этот список поддерживаемых платформ есть даже у самого компилятора (rust, например). Вот хочу я, например, свой процессор изобрести с уникальной архитектурой, значит ли это, что мне, минимум, придется портировать компилятор того же С? А если написать операционную систему? Ее корень - ядро, его пишут на ЯП, компилятор которого уже завязан на поддерживаемых им платформах (читай: ОС), значит ли это, что написать свое ядро на том же расте невозможно?
2. Еще мне непонятна идея генерируемого кода компиляторами. Пару слов обо мне: шарпист заинтересовался растом, значит попытаюсь понять на его примере. Так вот, у нас есть компилятор раста, который генерирует безопасный исполняемый код. В том же C или C++ генерируется код, но его безопасность исполнения не гарантируется. В C# есть сборщик мусора как решение проблемы предыдущих языков программирования. В расте сборщика мусора нет, т.е. его компилятором генерируется то же самое, что и у C или C++, но в данном случае он безопасный «на слово», в конечном виде он выглядит как и небезопасный результат компиляторов прежних 2-ух ЯП? Или компилятор раста на стадии компиляции проверяет паршивость кода, но при этом то же самое происходит при выполнении программы и от результата какой-нибудь проверки может случиться что-то иначе? Сложновато объяснил, всё сводится к следующему вопросу: какими жертвами удалось добиться безопасности растом?


Ответ на: комментарий от anonymous

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

Dulze
() автор топика

Какая каша.

ОС пишут на компиляторе ЯП

ОС пишут на языках, а не компиляторах.

Вот хочу я, например, свой процессор изобрести с уникальной архитектурой, значит ли это, что мне, минимум, придется портировать компилятор того же С?

Если будешь писать ОС на Си - да.

Ее корень - ядро, его пишут на ЯП, компилятор которого уже завязан на поддерживаемых им платформах (читай: ОС)

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

tailgunner ★★★★★
()

Вот хочу я, например, свой процессор изобрести с уникальной архитектурой, значит ли это, что мне, минимум, придется портировать компилятор того же С?

Нет, но тебе придётся написать оптимизации и кодогенерацию для, к примеру, LLVM.

какими жертвами удалось добиться безопасности растом?

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

А вообще, возьми https://class.coursera.org/compilers/lecture и книгу под названием «Engineering a Compiler».

quantum-troll ★★★★★
()

какими жертвами удалось добиться безопасности растом?

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

anonymous
()

Сложновато объяснил, всё сводится к следующему вопросу: какими жертвами удалось добиться безопасности растом?

«Безопасность» раста означает, что там нельзя «расстрелять память» или устроить «data race» (если не использовать ансейф код). Достигается это некоторым ужесточением правил обращения со ссылками и проверками в рантайме, где без этого никак.

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

сделать компилятор медленнее типичного С++-компилятора

Да ладно? По моему, компилятор раста куда быстрее, другое дело, что и оптимизаций они поменьше делают «пока что».

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

Справедливости ради, нормальный С++ код всё равно будет содержать проверки в том или ином виде. Ну а если нам надо пройтись по вектору целиком, то как и в С++ будет одна проверка, а не на каждой итерации.

DarkEld3r ★★★★★
()
Ответ на: комментарий от tailgunner

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

Но где эта грань завязанности? Банальные потоки в стандартной библиотеке раста - обертка потоков ОС. Т.е. если бы новая ОС была написана на расте, то пришлось бы писать нативные потоки на расте для компилятора раста?

Dulze
() автор топика
Ответ на: комментарий от Dulze

Не для компилятора, но для стандартной библиотеки и, возможно, рантайма. Компилятор как таковой потоками не занимается.

quantum-troll ★★★★★
()
Ответ на: комментарий от Dulze

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

Но где эта грань завязанности?

ХЗ, что такое «грань завязанности». Код, который «завязывается на ОС» (обращается к ней за обслуживанием) почти всегда пишется сознательно программистом.

tailgunner ★★★★★
()
Ответ на: комментарий от quantum-troll

Нет, но тебе придётся написать оптимизации и кодогенерацию для, к примеру, LLVM.

Раст построен на LLVM, но при этом раст может компилировать сам себя. Взять тот же GCC, на некоторых платформах он очень старых версий, т.е. компилируем старым версию поновее, а ей еще новее и т.д., в итоге придем к самой последней версии компилятора GCC. Можно ли такое сказать о «поддержка LLVM = поддержка Rust»?

Dulze
() автор топика
Ответ на: комментарий от Dulze

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

Что мешает скомпилировать сразу скомпилировать свежий GCC, если он поддерживает данную платформу?

Можно ли такое сказать о «поддержка LLVM = поддержка Rust»?

В целом — да, раст доступен везде, где есть поддержка LLVM. Но стандартная библиотека зависит от ОС.

quantum-troll ★★★★★
()
Ответ на: комментарий от DarkEld3r

Ну а если нам надо пройтись по вектору целиком, то как и в С++ будет одна проверка, а не на каждой итерации.

В коде вроде такого?

for (i = 0; i < N; ++i) {
    f (arr[i]);
}
Как тут с одной проверкой? Или имелся в виду другой код?

sf ★★★
()
Ответ на: комментарий от quantum-troll

Не для компилятора, но для стандартной библиотеки и, возможно, рантайма. Компилятор как таковой потоками не занимается.

У меня всё находится на понятии курица или яйцо, портирование компилятора пока что магия. Так, архитектура X86, при включении устройства стартует загрузчик, он загружает ядро. Ядро должно что-то делать: сперва подгрузить драйвера. Куда и как их подгрузить? Работа с IO реализуется в ядре, ядро пустое. Тут вспоминаем о POSIX стандартах. Т.е. должны быть зарезервированы определенные имена для стандартных функций ОС, а также должен быть написан способ общения ядра и приложения. Этот POSIX будет заглушкой для тех же самых нативных потоков компилятора rust'а. Тут сразу встает вопрос, а что вообще можно использовать в расте для написания ядра ОС? Так, магическим образом подгрузили драйвера (хотя тот же видеодрайвер завязан на иксах или вяленом), далее запуск системы инициализации? Можно ли написать другое ядро с использованием поддержки железа от линукса? Или тут всё линуксо-специфично и придется самому портировть драйвера под новое ядро?

Dulze
() автор топика
Ответ на: комментарий от sf

Как тут с одной проверкой? Или имелся в виду другой код?

На «нормальном С++» такой код будет выглядеть примерно так:

for(auto& val : arr) {
    ...
}
Ну или явно через итераторы написать. В расте точно так же:
for val in &arr {
    ...
}

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

В расте точно так же:

Не точно так же. В С++ ты задаешь тип, в том числе - по ссылке или нет, const или нет. Заодно можно сразу приводить к нужному типу. Т.е. точно так же как при объявлении переменных. В rust же ты указываешь только метод для итерации:

for val in arr.next()

Опять язык говорит тебе - забей уже на типы для переменных, ну и пусть во всяких Java, C#, C++ и пр. так принято, кто они такие, все так пишут.

anonymous
()
Ответ на: комментарий от tailgunner

Не правильно слово употреблено: не завязан, а поддерживается. Сюда же поддержка терминала. Лучше бы прокомментировал 2 предложения перед процитированным тобою куском текста, оно даже жирным выделено.

Dulze
() автор топика
Ответ на: комментарий от anonymous

Не точно так же.

В плане проверок выхода за границы, а речь именно о них шла, именно так же. Хватит уже «воевать за явные типы» везде.

в том числе - по ссылке или нет, const или нет.

В расте точно так же. Плюс можно сам контейнер «переместить». Тип переменной действительно явно задать нельзя.

Опять язык говорит тебе - забей уже на типы для переменных

И много ты видел «range based for» с указанием типа в С++? Все пишут auto (в том числе в книгах) и правильно делают. Так что там язык говорит?

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

В расте точно так же. Плюс можно сам контейнер «переместить». Тип переменной действительно явно задать нельзя.

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

Все пишут

http://st-im.kinopoisk.ru/im/kadr/1/1/7/kinopoisk.ru-Interstate-60-1171203.jpg

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

anonymous
()
Ответ на: комментарий от anonymous

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

Не понял, объясни подробнее, что тебя смущает.

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

Дык, я поинтересовался часто ли ты там указание типа видел. Мой опыт говорит, что никто его там не пишет. Опять же, про «навязывание» - в книгах предлагают писать именно так.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Не понял, объясни подробнее, что тебя смущает.

В for идет по сути объявление переменной. Как мы их объявляем?

let mut x : isize

Как мы пишем для параметров замыканий?

|mut x: isize| или (mut x: isize)

Как мы пишем для параметров функций?

(mut x: isize)

А для for сделали по-другому. Причем если говорить про C++, там можно написать:

auto f = []( auto i ) ->auto { return ++i; };
auto v = f( 1 );

А в rust нет.

Дык, я поинтересовался часто ли ты там указание типа видел

Да, хотя на самом деле еще далеко не везде range for вообще используется, совместимость со старыми компиляторами и все такое.

anonymous
()
Ответ на: комментарий от Dulze

Ядро должно что-то делать: сперва подгрузить драйвера. Куда и как их подгрузить?

в память, конечно, куда ж еще.

Можно ли написать другое ядро с использованием поддержки железа от линукса? Или тут всё линуксо-специфично и придется самому портировть драйвера под новое ядро?

вообще да,

тут всё линуксо-специфично

но теоретически

написать другое ядро с использованием поддержки железа от линукса

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

линуксо-специфично

что это значит? фактически, это набор соглашений, которым следуют и ядро, и драйвера.

что вообще можно использовать в расте для написания ядра ОС?

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

CryAngel
()
Ответ на: комментарий от anonymous

А в rust нет.

Хотя нет, то меня клинит, иду спать.

anonymous
()
Ответ на: комментарий от Dulze

Драйвера — части ядра. Ядро же обращается к оборудованию напрямую. Для обращения к оборудованию есть инструкции процессора, которые вызываются кодом на С или расте через ассемблер.
Приложения обращаются к ядру через системные вызовы (например, http://docs.cs.up.ac.za/programming/asm/derick_tut/syscalls.html). Которые, для кроссплатформенности и удобства использования обёрнуты в [стандартную] библиотеку. Которую ты и используешь в своём коде.
Компилятор ко всему этому отношения не имеет, он просто генерирует бинарный код для заданной архитектуры процессора.
Короче, читай «Современные операционные системы» Таненбаума.

quantum-troll ★★★★★
()
Ответ на: комментарий от quantum-troll

Драйвера — части ядра

ИМХО так говорить некорректно, все же многие драйвера могут быть собраны в виде модулей. опять же, есть сторонние драйвера, которые частью ядра уж точно не являются (та же NVidia)

Для обращения к оборудованию есть инструкции процессора

для обращения к оборудованию уже успели наделать специальных инструкций? в те времена, когда я интересовался ассемблером и устройством процессоров (это было всякое вроде Z80 и 8086) я о таких не слышал. ну, разве что такими инструкциями считать IN/OUT, но, ЕМНИП, большая часть работы с оборудованием происходит через выделенные участки памяти.

CryAngel
()
Ответ на: комментарий от CryAngel

ИМХО так говорить некорректно, все же многие драйвера могут быть собраны в виде модулей. опять же, есть сторонние драйвера, которые частью ядра уж точно не являются (та же NVidia)

Модуль и есть часть ядра.

slovazap ★★★★★
()

Вот хочу я, например, свой процессор изобрести с уникальной архитектурой, значит ли это, что мне, минимум, придется портировать компилятор того же С?

И только C. И добавить новую архитектуру — не портировать компилятор. И это будет так легко, что будет твоей наименьшей проблемой.

Ее корень - ядро, его пишут на ЯП, компилятор которого уже завязан на поддерживаемых им платформах (читай: ОС), значит ли это, что написать свое ядро на том же расте невозможно?

Не читай «ОС». Возможно.

но в данном случае он безопасный «на слово»

«сгинь с лора троль» ©

t184256 ★★★★★
()
Ответ на: комментарий от CryAngel

разве что такими инструкциями считать IN/OUT

Да, именно их я и вспомнил.

большая часть работы с оборудованием происходит через выделенные участки памяти

Кстати, как эти участки памяти привязывают к оборудованию?

quantum-troll ★★★★★
()
Ответ на: комментарий от slovazap

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

CryAngel
()

что за говно я только что недочитал? еще одна заедушная макака прошла курс по операционным системам и ничего не поняла?

unt1tled ★★★★
()
Последнее исправление: unt1tled (всего исправлений: 1)
Ответ на: комментарий от quantum-troll

как эти участки памяти привязывают к оборудованию?

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

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

CryAngel
()
Ответ на: комментарий от Dulze

Лучше бы

В данном случае мне лучше знать, что советовать.

прокомментировал 2 предложения перед процитированным тобою куском текста, оно даже жирным выделено.

На Rust уже написана ОС - она учебная, но тем не менее. Есть и другие ядра на Rust. Читай учебники.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

На Rust уже написана ОС

в этом ключе мне стало интересно: а существует ли язык (из более-менее живых), на котором еще не написана ОС

CryAngel
()
Ответ на: комментарий от CryAngel

а существует ли язык (из более-менее живых), на котором еще не написана ОС

UNIX shell %)

А вообще - я не слышал об ОС на Python/Ruby/Perl/Lua/PHP, на Scala.

tailgunner ★★★★★
()

Сложновато объяснил

потому что ты ничего не понимаешь

anonymous
()
Ответ на: комментарий от tailgunner

ОС на Python

pycorn, например. на Lua тоже, вроде, какие-то попытки были. такое впечатление, что существует какая-то олимпиада, участники которой должны написать ОС, но ЯП при этом не должен повторяться.

CryAngel
()
Ответ на: комментарий от quantum-troll

Кстати, как эти участки памяти привязывают к оборудованию?

DMA. Специальный контроллер

anonymous
()

Сразу видно ЛОРовыкидыша, у которого в башке нет знаний, только слова, заимствованные из местных слабеньких тредов таких же бездарей.

anonymous
()
Ответ на: комментарий от DarkEld3r

На «нормальном С++» такой код будет выглядеть примерно так:
for(auto& val : arr) {
...
}

Хорошо, сколько тут проверок?

sf ★★★
()

ЯП, компилятор которого уже завязан на поддерживаемых им платформах (читай: ОС)

Завязка компилятора на ОС достаточно мала. Она выражается, ЕМНИП, в двух вещах:

1) Заголовок исполняемого файла. Его добавляет линковщик в последний момент и это легко изменить. Как пример - кросскомпиляция.

2) Соглашения о вызовах функций ОС. Тоже чисто механическая тасовка байтов.

German_1984 ★★
()
Ответ на: комментарий от anonymous

А у тебя от этого проблемы? Или ты забыл принять лекарство?

anonymous
()
Ответ на: комментарий от CryAngel

ОС на Python

pycorn, например

Насколько я могу судить, это не ОС, а просто Python на голом железе. Такие есть, конечно - MicroPython, например. И версии Lua такие есть.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.