Разработка операционной системы на .NET Core

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

Камрады, хочу попробовать разработать свою операционную систему «с нуля». Мне в свое время понравилась идея Singularity, жалко что ее забросили. Посему, хочу запилить ее аналог.

Критерии успеха:
1. ОС может запуститься «с нуля», без каких-то вспомогательных оболочек
2. Поддержка современных процессоров (в идеале чтоб можно было запустить в Azure/AWS виртуалке).
3. Поддержка виртуализации
4. Фуниционала ОС должно быть достаточно чтоб запилить хотя бы файловый сервер.
5. Программы для ОС тоже пишутся на .NET Core.
6. Перформанс сравим или выше с перформансом линукса.

Назначение ОС — аккадемическая/экспериментальная, для изучения концепций управляемого кода в режиме ядра как части операционной системы.

Возможно ли сделать это все, используя чисто .NET Core? Что нужно будет допилить в самом .NET Core чтоб это взлетело?

PS Прошу без троллинга.

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn

Найкращі коментарі пропустити

Я так розумію, свій процесор ти вже «розробив» :)

Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

лучше на жабоскрипте

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

Начал разбираться с .NET Core. Движется медленно потому что занят на работе а также занимаюсь разработкой процессора. Не хватает времени.

Я думаю, что тебе лучше обратиться к эксперту, евангелисту, фанату и просто хорошему человеку. Зовут его Денис Попов.

Пусть он напишет в этом топике свое мнение

Если самому реализовать CLR, который будет запускаться на голом железе, то теоретически можно. Но в одиночку такой проект явно не потянуть. Думаю есть смысл пообщаться с кем-то из команды reactOS. Подобная штука вполне в их духе :)

Ты же в курсе, что Сингулярити писанна не на C#, а на Sing#, с кучей низкоуровневых конструкций?

ааа лол,,, процесор закончіл уже ? стрoй дезстар зразу :) чарівник

жениться вам надо, барин

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

Нет.

Ну, если очень выпендриться, то придумать можно. Через память.

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

А что нет памяти доступной для обоих процессоров и совсем нельзя такую организовать? А как это сделать подумай сам. Я стратег))

Я стратег))

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

Как ты мне, умник, надоел. Эту херню что вы тут обсуждаете я высрал 20 лет назад. Мне она просто не интересна. Детский сад. Вижу что много чего начитался и насмотрелся. Но, вот с пониманием почему это так сделано, а не иначе у тебя слабовато. Есть для такой термин «воинствующее невежество». Единственно, что тебя отличает, так то, что ты много нахватался. Это немного маскирует суть положения вещей, но не меняет.

Как ты мне, умник, надоел.

Надоел — проходи мимо.

Эту херню что вы тут обсуждаете я высрал 20 лет назад.

Это видно, что у тебя пещерные представление о современных архитектурах.

Но, вот с пониманием почему это так сделано, а не иначе у тебя слабовато.

Я знаю, почему нельзя модифицировать регистр другого процессора через другой процессор (4 причины могу сходу назвать), а ты — нет. Где твоё понимание?

За 40 лет в архитектуре компьютера ничего не изменилось принципиально. Твоя классическое обвинение что оппонент не знает каких-то сакральных знаний просто смехотворно. Обычно обвинения в незнаниях ты придумываешь сам. А изменить значение регистра другого процессора, если очень нужно, то можно. Через память. Хотя в общем случае через что угодно к чему есть общий доступ. ЕСЛИ ОЧЕНЬ НУЖНО!!! Подумай на досуге. А останется время подумай над словом "нельзя :)

А изменить значение регистра другого процессора, если очень нужно, то можно. Через память.

Вот ты опять не понимаешь фундаментальных вещей. Поменять без ведома другого процессора, а то что ты впариваешь shared memory — это обычное inter-core communication, когда два и более процессора хотят получить новое значение.

Умница! Только в задаче не было слов «без ведома»

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

Та все понятно. Теперь задача не та..

Основной вопрос: «Зачем?» Вот процессор занимается какой-то задачей, все регистры расписаны, и вдруг, не иначе как какой-то диверсант меняет некоторое значение. Что будет в результате?

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

Вообще, прежде чем обдумывать некоторыё моменты многоядерность, я бы рекомендовал всё-таки прочитать что-нить про ядро Linux, например Лава. Тогда хоть появится некоторая база, на которой можно будет обдумывать. На худой конец Танненбаума. Потому что сейчас это больше фантазии по мотивам, а что там может такого быть...

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

Вопрос «зачем?» не стоял. Стоял вопрос «Как?»

Вот процессор занимается какой-то задачей, все регистры расписаны

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

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

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

Это не проблема. Поскольку я контролирую оба процесса, я буду следить чтоб они не накосячили и вели себя хорошо.

Это не проблема.

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

Понял, здесь, похоже, тоже тупик :(

Танненбаума читаю. Но там очень поверхностно.

Идея такая. Вместо того чтоб опираться на стандартные устаревшие механизмы многозадачности, и прерывания — одно ядро процессора будет 100% выделено ядру. Едро запускает какой-то процес на другом ядре. Когда приходит время, нужно остановить и запустить другой процес. Я хочу перезаписать регистр адреса инструкции чтоб он перешел по адресу другой задачи и продолжил ее выполнение. Но это должно работать через какие-то кросс-ядерные коммуникации чтоб модифицировать значение регистра.

В этой теме перепробовали уже все. Лучше «устарелых механизмов» ничего не придумано.

> одно ядро процессора будет 100% выделено ядру.
А если оно там всего одно? Да даже если 8, 12% ресурса CPU ядру — слишком жирно. Кто-то тут хотел с Линуксом потягаться...

Problems officer?

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

Приложения будут делать выделение памяти сами без помощи ОС (слава CLR и Сборщику Мусора!)

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

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

Dear Lord, thank you for all the beautiful memory chunks you give us in this short process life and thank you for putting CPU ticks on our process dispatcher every second. And thank you for taking care of dead processes and zombies who’s no longer among us. Amen.

О, идея для православного процессора

Увы, это процессор англиканской церкви %)

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

Йа допилю CLR чтоб он сам менеджил свою память, а не просил ОС через syscall (исходники на гитхабе так что это не проблема)

Ты хочешь обходить ОС и аллоцировать память напрямую? Т.е. хочешь ранить CLR ниже OC. Кто то должен хранить таблицу алоцированной памяти- обычно это делает ОС

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

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

Нет. Фичи безопасности кода .NET обеспечиваются тем, что в языке отсутствуют возможности накосячить, и компилятор ставит проверки режима выполнения везде, где код может делать что-то плохое (например, выходить за границы массива).

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

Бубен, не неси ерунду.

Скомпилируй это и посмотри ассемблерный листинг (если, конечно, знаешь что это такое)

[MethodImpl(MethodImplOptions.NoInlining)]
int GetAt(int[] arr, int index)
{
    return arr[index];
}

Это только один из кейсов, где что-то может пойти не так.

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

Были ли попытки запустить CLR в незащищенном режиме процессора.

Бубен, я тебе больше скажу: уже есть ОС, написанная на управляемом коде (форк CLR). А ты продолжай рассказывать о том, как процессоры отлавливают выходы за пределы массива :)

И эта ось не использует защищенный режим процессора ?

У меня есть лучше ссылка. Вот тебе сорцы github.com/dotnet/coreclr
Там же есть ссылка на поиск, ищешь Win32 API функции которые основаны на сейв моде процессора, напр. VirtualProtect. Изучаешь контекст использования.

Нет времени объяснять, но это работает не совсем так в .NET Core.

компилятор ставит проверки режима выполнения везде, где код может делать что-то плохое (например, выходить за границы массива).

Разве что unsafe код полностью запретишь

компилятор ставит проверки режима выполнения везде, где код может делать что-то плохое (например, выходить за границы массива).

There are no array bounds checks! This is sometimes a worry with people new to .NET—how can we maintain memory safety without boundary checks? In this case, the JIT compiler determined that the boundary checks weren’t necessary because it knew all of the constraints of this loop.

Ага, откройте для себя такую вещь как статическая верификация кода :)

Любая дыра в которой откроет возможность для самых адовых эксплоитов (поскольку адресное пространство самой ОС не защищено от слова никак)

Волков бояться — в лес не ходить

Вообще есть почти подходящий инструмент wiki.osdev.org/...​nter-Processor_Interrupts
Но, как правило, типичная OS решает все свои проблемы взаимодействия на обычном Clock Interrupt (PIC, APIC, можно сразу идти сюда и дальше по ссылкам — wiki.osdev.org/HPET), собственно это называется thread scheduling.

пиши на JS, иначе не модно

да, посоны на курилке засмеют))

JS- это сегодня уже немодно.

Надо на TS писать а потом написать транспайлер в %выбранный_язык% — и гордиться что написал ОС на высокоуровневом языке с возможностью сборки на каком угодно языке/компиляторе.

Да, я был не прав, вариант с ТС куда круче

Як .NET? Я думав, під Ваш KPU програмують щирою українською, а тут якийсь .NET

В видите разницу между инструкциями процессора и .NET?

Артьомка, а ты уже выбрал стабильную версию и с поддержкой на долгое время? А то в недавнем топике у тебя был «разрыв» на эту тему )))) И собственно почему не на джаве?

Хиханьки хаханьками, но сделать EFI на чём-то яваобразном было бы очень удобно и полезно.

К сожалению, не верим :-)

Интересно как будет свопиться память на диск ))) И еще интересно, что если прога для ОСи написана на коре то как блокировать перезапись приложением файлов самой ОСи,

Интересно как будет свопиться память на диск )))

А как она сейчас свопится? :) Интересный вопрос.

И еще интересно, что если прога для ОСи написана на коре то как блокировать перезапись приложением файлов самой ОСи,

Так же как и сейчас. Определенные файлы приложение не сможет перезаписать, что сложного?

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

Тогда уже MINIX и что-нить микроядерное... Если хочется писать драйвера на C#

сомневаюсь что драйвера в байт коде отработают)))

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

Ага, вижу на ресурсе для девелоперов для микрософта, случаем 1 примерчик на С# и тот для принтера. Что-то скрывают видать )))

Отсвапишь и будешь там мониторить мусор?

ну

По большому счёту почему бы просто не передалать линукс кернел и его окружение в минимально достаточную для запуска Нет среду и передать уже ей управление как командному процессору

Потому что цель — разработать ОС, а не пропатчить существующую, видите разницу?

А смысл писать что б повторить? Что б выстрелило нужна принципиальная идея. Или идеи. Кстати, и для обучения гораздо эффективней когда зацениваешь разницу между задуманным и уже готовым. Ручками и задницей оно гораздо доходчивей чем чтение литературы.

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

Я только за. В каком-то смысле сам этим занимаюсь.Только кроме уникальности необходимы плюшки. Причем достаточно серьезные что б заинтересовало пользоваться. В чем плюсы?

А еще начальнее, определиться кто есть плзователи )

К сожалению, это еще сложнее чем с процессором.
Ибо ценность операционки в количестве написанных под нее программ.

На яве пиши, в arm даже есть аппаратная поддержка байт кода (правда доки нет), а так, в принципе — проц могет исполнять *.class

проц могет исполнять *.class

Да нет же, прямо .zip файл! %)

Это убожество (Jazelle) дропнули 8 лет назад и правильно сделали.

Удачі з габадж-коллєкшном в кернел мод!!!

Лол! А что не так со сборкой мусора?? Какая разница кернел мод или юзер мод? Наоборот сборщик мусора позволит отказаться от ручного управления памятью (т.е. не будет небезопасного кода в режиме ядра!). Значит, ОС будет очень стабильной и не будет крашиться так часто как Windows с синими экранами.

Она часто крашилась в годах эдак 95-98. Сейчас крашится где нетривиальный код кривые драйвера, в видеокартах например. И будет у тебя тотже какой-нибудь Object reference not set on an instance на уровне ядра, ну экран покрасишь в зеленый, если синий не нравится

Та нічого не так, головне швидко порєшать що сміття, а що ні, прибратися, дефрагметувати при потребі, доки інші чекають

А когда ты new/delete[] вызываешь в Си, дефрагментировать не надо, дырки общитывать не нужно и так сойдет ?

А когда ты new/delete[] вызываешь в Си, дефрагментировать не надо, дырки общитывать не нужно и так сойдет ?

Кто дырки обсчитывает? o_O

Если у тебя освободилось 40 байт и рядом еще 40 байт, то нужно както отметить, что есть дырка для выделения не 40 + 40 байт, а 80 байт и туда можно впихнуть например 60 байт.

Мелкие аллокаторы работают с бинами, если ты осводил 40 байт, то бин, допустим, для 64 байт маркируется свободным.

А если рядом еще два по 64 байт освободилось, то видимо надо както отметить, что освободилось не просто 64+64 байт, а одним куском доступно теперь 192 байта, на случай если попросят 180 байт, например ? Короче это долгая тема, там сложные алгоритмы, на тысячи строк кода.

Нет никакого большого куска, есть два по 64. Если попросят 180, то выделишь из другого бина, где по 256 сидят куски.

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

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

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

Эээ ... ты так корову не продашь ) Если собрался операционку писать, то виртуальная память и все все алгоритмы теперь твоя головная боль.

Виртуальная память — это фишка CPU, не ОС.

Ты думаешь, CPU сам по своей воле заполняет таблицы трансляции? :-D

Ты можешь хоть петабайт одним куском выделить, если хочешь.

Не можешь, на x86_64 виртуальное адресное пространство лимитировано 256 TB.

In 64-bit Windows, the theoretical amount of virtual address space is 2^64 bytes (16 exabytes), but only a small portion of the 16-exabyte range is actually used. The 8-terabyte range from 0×000’00000000 through 0×7FF’FFFFFFFF is used for user space, and portions of the 248-terabyte range from 0xFFFF0800’00000000 through 0xFFFFFFFF’FFFFFFFF are used for system space.

Кстати, забавный факт на днях обнаружил. Принесли новую платформу на CoffeeLake от интела, вендор HP — там вся память устройств начинается с границы 256Gb. Т.е. 32 бит ОС уже запустить невозможно даже в теории, она туда не дотянется даже со всеми расширениями. В первый раз увидел, как 32 бит x86 реально умер. Аминь.

Можно запустить, только некоторые устройства работать не будут.

там вся память устройств начинается с границы 256Gb.

Есть устройства, которые не поддерживают 64 бита на своей шине.
Я думаю, для них всё равно оставили совместимость с 4GB границей.
Ну и ничто в принципе не мешает переместить адреса вниз — это нудно все мосты перестраивать, но вполне возможно.

В первый раз увидел, как 32 бит x86 реально умер. Аминь.

Я думаю, он ещё поживёт в каких-то особых условиях. Хотя смысла уже, считаем, нет.

Есть устройства, которые не поддерживают 64 бита на своей шине.

Смысла всё равно нет, ну будет работать какой-нибудь древний PCI контроллер из более чем 30 устройств на шине и всё, остальные всё равно недоступны.

Ну и ничто в принципе не мешает переместить адреса вниз — это нудно все мосты перестраивать, но вполне возможно.

Размер апертуры мешает. Например в 2Gb. И расположение в одном физическом месте с оперативной памятью чревато её потерей в работе.

Не можешь, на x86_64 виртуальное адресное пространство лимитировано 256 TB.

Уже есть новая версия лимитов.

patchwork.kernel.org/patch/9475177

А на шине адреса тоже виртуальная память? А с ним работает перифирия... Сказали устройству на PCI писать по физическому адресу, а сборщик мусора упаковал и перенёс в другое место :)

Есть возможность «запинить» память, чтоб сборщик мусора ее не дефрагментировал.

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

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

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

Не получится.

Для легаси, про что ты говоришь:
github.com/kjiwa/x86-boot-sector-c

UEFI — это необычный Win PE32, генерируется точно также.

Также да не также, кусок влепили на ассемблере (машкоды), что какбе намекает о возможностях Си для бутлоадера.

// The first 3 bytes are reserved for a JMP instruction that we use to jump to
// the _start() method. The next 59 bytes are reserved for the OEM name and the
// BIOS parameter block. We set drive_index to be 0×80 to indicate we are
// booting from a hard disk drive.
asm („jmp _start”);
asm („.space 0×0021”);
asm („.byte 0×80”);
asm („.space 0×001c”);

Также да не также, кусок влепили на ассемблере (машкоды), что какбе намекает о возможностях Си для бутлоадера.

Ну и что? Разница фундаментальная ведь. В одном случае ты дампишь кем-то заранее записанную программу, во втором ты её компилишь напрямую. Чувствуешь разницу?

Для легаси, про что ты говоришь

Похоже, твой собеседник заменил ответ полностью, но я всё равно не понимаю, что ты хотел этим показать. Возможность написать это на C? Так оно неполноценно — например, зашит bios id диска в 0x80, вместо того, чтобы взять из DL.

И я там не вижу ни одной динамической аллокации — а спор шёл до этого именно о них.

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

Так не получилось же:
1. id диска не берётся — это критически важный входной параметр.
2. Без ассемблерных вставок не работает — как для вызова int 13h, так и для финального jmp. И последнее ты не заменишь на вызов функции по указателю: стек не настроен.

Это всё не более чем упражнение на генерацию 16-битного выхлопа.

Так не получилось же:

Получилось.

1. id диска не берётся — это критически важный входной параметр.

Сделай — это не рокет сайнс.

2. Без ассемблерных вставок не работает — как для вызова int 13h, так и для финального jmp.

И что? Средства высокоуровневого языка позволяют это сделать и получить на выходе желаемый результат. Альтернативу я уже описал.

И последнее ты не заменишь на вызов функции по указателю: стек не настроен.
void (*func)(void);

int main()
{
    goto *func;
}

->

        leaq        func(%rip), %rax
        movq       (%rax), %rax
        jmp        *%rax
Сделай — это не рокет сайнс.

Как? Очередной asm()? Нечестно. Конвенция вызова не содержит в DX никаких параметров.

goto *func;

И это обман. Ты не имеешь права в таком goto использовать что-то, кроме метки в той же функции (или во внешней, если данная вложенная). То, что это сработало на конкретной версии GCC, означает, что это ещё одно использование ассемблера, а не C.

Как? Очередной asm()?

register unsigned char dl asm("dl");

Нечестно.

А где написаны правила игры, чтобы считать это честным или нечестным?

И это обман.

Это С.

То, что это сработало на конкретной версии GCC, означает, что это ещё одно использование ассемблера, а не C.

Учи С.

Это С.

Неправда твоя, это GCC-специфичное расширение.

gcc.gnu.org/...​ues.html#Labels-as-Values

Особенно обрати внимание на

You can get the address of a label defined in the current function (or a containing function) with the unary operator ‘&&’. The value has type void *.

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

Учи С.

_Ты_ учи.

register unsigned char dl asm("dl");

asm, о чём я и говорю.

Неправда твоя, это GCC-специфичное расширение.

А у тебя есть другие? GCC — это дефакто индустриальный стандарт. Все остальные компиляторы либо поддерживают расширения (llvm, icc), либо уходят с рынка. Computed gotos существуют уже лет 20.

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

Это значит, что ты читаешь там где светлее, а не то, что надо. Computed gotos всегда имели undefined behavior. Это работает, если ты знаешь, что ты делаешь.

asm, о чём я и говорю.

И что?

А у тебя есть другие? GCC — это дефакто индустриальный стандарт.

1. Скажи это майкрософту :) а мы сейчас больше в его контексте из-за корневого топика, чем в Unix.
2. Сколь бы он ни был стандарт де факто, это — расширение C, а не собственно C.

Computed gotos всегда имели undefined behavior.

Внутри функции — безусловно нет, если синтаксис вообще разрешён, то они работают.

А между функциями... может, ты можешь себе позволить залагаться на интерпретацию undefined behavior конкретным компилятором, но я обычно — не могу, и большинство тоже не может.

Это работает, если ты знаешь, что ты делаешь.

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

И что?

А просто очередной хак.

Горчак пишет Нетчу

Учи С.

Ничего более ржачного не читал уже очень давно

Ничего более ржачного не читал уже очень давно

Не смог удержаться! %) Сугубо технические аргументы «это нечестно» и «это обман» были выше всяких похвал %)

Они действительно технические — это был не C, а другой язык, основанный на C.

Ну Горчак реально спец в C/C++ лучше меня. Но его регулярно заносит куда-то «не туда».

Видать глюк форума, это кусок ветки отсюда:
dou.ua/...​rums/topic/24606/#1385241

Именно не пула, про что ты сейчас рассказываешь, а честного аллокатора.

Причём тут честные аллокаторы? Может всем теперь использовать честную пузырьковую сортировку, потому что это классика? %)

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

Слишком общие слова, никакой конкретики.

Ці причини аж ніяк не звʼязані з його швидкістю в порівнянні із «звичайними» алокаторами, в яких треба явно вивільняти памʼять.

Так, але наслідками є оверхед по часу

Ось тут — ні. Оверхед по памʼяті — так, в рази (типово від 2 до 5). По часу — ні, високоякісні збирачі тратять менше часу, ніж алокатори стилю malloc/free (враховуючи миттєве виділення).

Конечно, ты можешь пойти по принципу «всё до размера страницы — бинами, выше — на уровне страниц виртуальной памяти всё разрулится».

Реально см. например glibc malloc — бинами идут относительно мелкие куски (до 64 байт), дальше до 16 или 16KB — таки аллокаторы с учётом на цепочках и деревьях, и только со следующей границы это переваливается на mmap().

Ну а учитывая тормоза поддержки всяких битовых карт распределений, если памяти гигабайты, а страницы по 4KB — всё равно лучше потратиться на держание крупных блоков.

Сборка мусора — это микросекунды. А вытащить влешку — минимум одну секунду займет.

Про микросекунды это ты загнул. Разве что собирать нечего. Если есть, то обычно десятки миллисекунд.

На моей последней работе бывало, что сборка мусора занимала единицы минут.
Java8 и десятки гигабайт данных.

Стоп, а как же поддержка KPU? Или вы так и не довели идею до ума из вашего позапрошлом топика?

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

Элементарно. .NET CLR встраивается в бутлоадер, дальше для .NET все привычно. .NET не требует ОС для выполнения сам по себе.

Требует. Как он с железом и драйверами работает? Тот же андроид хоть и на яве и со своей спец jvm но идёт поверх линукса.

А как С с железом и драйверами работает? :)

С компилирует в машинный код, в отличие от дотнета. :)
(хотя не знаю, может .NET Core научился в машинный код компилировать — было бы конечно круто)

.NET компилировался в машинный код в далеком 2000 году, вы что-то пропустили :)

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

Теоретически, конечно, можно сделать гибрид ядра ОС и CLR (и вроде бы даже сами Microsoft одно время порывались двигаться в эту сторону), но это туева хуча работы, которая вряд ли под силу одному человеку.

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

Компилятор много чего делает. В первую очередь подразумевает какое-то выделение памяти и расположение всего скомпилированного. Универсальности здесь не предполагается.

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

какой смысл? Уже куча поисковиков есть

Ну во-первых не куча, а больших и толковых всего несколько.
Во-вторых, это более Science/ML/AI задача, чем техническая. С операционкой тебе просто прийдется решить огромное количество рутинных, скучных технических задач.

ML/AI, говоришь?
Ну даже если абстрагироваться от того, что надо по началу как минимум сотни nod’ов запустить, следует послушать, почему Andrew Ng знает, как сделать Гугл или Амазон, но не взялся бы за эту задачу
youtu.be/NKpuX_yzdYs?t=14m40s

Смотря какой поисковик писать. Как пример, мой например мне собирает кубатуру с ресурсов
booben.com/...​1+0&s=online&a=search&p=1
или фейсбучину
booben.com/...​1+0&s=online&a=search&p=1
Это примеры из нестандартных возможностей, которые через гугль (пока что) не получить.

Ну что ж, успехов!
Кста, я попробовал поискать по слову letyourmoneygrow — с dou он находит некоторые топики (не все), с ФБ — ничего. И внизу — много страниц выдачи, но уже начиная с «2» — пусто.

Спасибо, да там все достаточно сыро. Набор разной ресерч ф-сти, надеюсь когдато допилить.

Коментар порушує правила спільноти і видалений модераторами.

и чтобы работала в вебраузере

Не знаю, вопрос серьезный или троллинга ради. Но отвечу серьезно:
ДА github.com/charliesome/jsos

Только там 60% кода оси написана на Си и только 30-35% на js)

я канєшно не експерт, але як я розумію, якась частина Singularity була на c++ та асм. Я так думаю це рантайм для .net. Думаю для кора аналогічно. Потрібно завантажувач ну і .net core runtime

они придумали компилятор с#-> native

Этот компилятор называется JIT компилятор, и они его придумали и сделали еще в 2000 году :)

вы немного не в теме, джит — это мсил в нэйтив при загрузке кода в апп домен, а там писала компилятор другая команда и там был с# подобный язык

Возможно ли сделать это все, используя чисто .NET Core?

Нет.

Что нужно будет допилить в самом .NET Core чтоб это взлетело?

Реактивный ранец.

Реактивный ранец уже завезли и доставили в продакшин
www.codeproject.com/...​D-performance-vs-Intel-IS

Замечательно. Мандельброт теперь быстрый, а остальные 100500 вещей? %)

а что еще должно быть у высокоуровневого языка? или я что-то пропустил и рендеринг графики это типичная задача, с которой справляються managed языки уже давно с производительностью native кода? :)
Впринципе во многих моментах, где надо в .net core уже добавили low-alloc API во фреймворке — как результат быстрые TCP сокеты, быстрые апи для работы с byte буферами и стандартными типами — как факт топовые позиции в бенчмарках в профильных нишах. Если еще где-то охота еще что-то запилить рантайм и компилятор допили так что бы можно было не парясь об указателях работать с native memory/stack из managed кода —
было так(unmanaged — c# код)

unsafe
{
  byte* tmp = stackalloc byte[length];
}
сейчас тоже самое выглядит так
Span<byte> bytes = stackalloc byte[length];
С учетом того, что все это было сделано за последние полтора года с момента зарождения идеи и до конца мая 2018(когда значительная часть стандартного .net api вышла в прод на этих вещах) позволю предположить, что и сопровождаемость и стабильность кода будет очевидно куда лучше чем аналогичные вещи делать на C++. На системный вещи само собой платформа претензий не имеет — но среди конкурентов прямых выглядит очень привлекательно.
или я что-то пропустил и рендеринг графики это типичная задача, с которой справляються managed языки уже давно с производительностью native кода? :)

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

Возможно ли сделать это все, используя чисто .NET Core?
Нет.

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

Формально можно, ибо ничто не мешает из менеджед кода генерить машинные комманды.

Ну сгенирируй мне команду WRMSR

Создаешь консольное приложение на шарпе, открываешь экзе файл на запись, пишешь pe32 заголовок, пишешь машкоды по

WRMSR

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

запускаешь с шарповой консоли то что у тебя получилось.

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

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

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

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

Когда ты загружаешь комп, комп не умеет компилированный С++ выполнять в экзе файлах.

Ну так линкуй не в экзе, а сразу куда надо.

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

А что мешает эти машкоды скопировать в свою программу?

открой для себя линкер скрипты и objcopy

Даже если у него полноценный freestanding, какой-то рантайм и стек должны быть созданы. Это линкер-скриптами не создать, а если создать, то набиванием машкодами, которые тоже надо откуда-то взять.
Для Unix это изображается в виде содержимого crtbegin.o и crt1.o (или аналогов), которые имеют заметные ассемблерные куски.

Легко, только скажи что ты ожидаешь от ее выполнения?

Запись в MSR регистр, а чего ещё? Например, как ты собираешься настраивать LAPIC контроллер прерываний?

Будет дополнительный класс, который может выполнять такие команды.

Например, хочеш записать 20 в регистр, пишешь:

System.CPU.GetCurrentCPU().Registers.MSR = 20
Например, как ты собираешься настраивать LAPIC контроллер прерываний?

Пока не знаю, нужно будет спецификацию курить.

Например, хочеш записать 20 в регистр, пишешь:
System.CPU.GetCurrentCPU().Registers.MSR = 20

Мне интересно как JIT узнает, что ты хочешь команду процессора WRMSR.

Так же как он узнает что я хочу

mov eax ebx

Есть специальные методы, которые помечены специальными атрибутами. У них нет C# кода, вместо этого JIT их подменяет на определенные ассемблерные комманды.

Talk is cheap, show your code! :)

blogs.msdn.microsoft.com/...​ow-inline-il-in-c-vb-net

Вот пример inline IL code в C#. Компилятор IL GIT лежит на гитхабе, ничего не мешает добавить нужные инструкции.

Т.е. ты добавишь свою IL команду, которая потом откомпилируется в нужную команду процессора? И твоя ось будет собираться только кастомным компилятором. А ведь она не одна, тебе надо будет таких два десятка тащить!

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

Вообще для тьюринг полного процессора нужно меньше 10 ассемблерных комманд

Достаточно одной команды.

Зачем вторая?

Базовый необходимый набор команд с одним параметром. 1-я команда — присвоение значения, 2-я-прибавление 1, и третья переход по 0. В твоей ссылке эти три команды заменяются манипуляциями с тремя параметрами.

Команда одна? Одна. Какие претензии?

Формально одна. И вообще не о претензиях речь, а о понимании.

Есть еще вариант «одной команды» с тремя операндами из которых два операнда, а третий номер команды. И конструируй что угодно. Хоть весь набор ассемблера. Твоя ссылка просто детский лепет по сравнению с моим предложением.

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

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

А так сделать можно, если есть много свободного времени. Но не взлетит.

Если бы я нашёл бы безумного инвестора, то наверное, писал микроядро на чистом C, а уже всё сверху накрывал бы .NET.

Ну а допилить в .NET Core надо всё, что связано с ядром: примитивы синхронизации в ядре (спинлоки и т. д.), выделение памяти (куча атрибутов, специфичных для ядра), обработка прерываний, потоки/процессы, файловая система, ...

Хочется человеку .NET. А Rust, такое впечатление, надо допиливать. Там аннотация меток заточена под один аллокатор.

примитивы синхронизации в ядре (спинлоки и т. д.),

В .NET Core вроде бы уже добавили все функции синхронизации, которые работают в user mode (а значит, они будут работать и в режиме ядра, не опираясь ни на что)

выделение памяти

Есть встроенные менеджер памяти. Зачем еще выделение?

обработка прерываний

Можно заменить async/await

потоки/процессы

Есть System.Threading, можно еще вроде в .NET сделать юзермод потоки.

файловая система

С этим скорее всего будет затык, нужно будет найти способ портировать драйверы из Линукс/Windows или написать свои.

В .NET Core вроде бы уже добавили все функции синхронизации, которые работают в user mode (а значит, они будут работать и в режиме ядра, не опираясь ни на что)

Захвати залоченный мьютекс в обработчике прерывания! %)

Зачем O_O? В обработчике прерывания просто записать инфу в ring buffer (неблокирующий), который может обрабатываться даже из юзер мода

А как юзер мод узнает о прерывании? Наверное, какие-то примитивы синхронизации придётся таки разрешать в ядре и в обработчике прерывания? :) Например те же спинлоки, которые в нормальном режиме работают как мьютексы, а в обработчиках прерываний, как настоящие спинлоки. Теперь это твоя головная боль %)

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

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

а если не получилось — все равно выполнить код который я хотел, не ждать же вечно?

Именно что ждать вечно. Или же обломиться и вывалиться с ошибкой, переложив на кого-то другого — так делала старая солярка.
А просто лезть в данные при незахваченном локе ты не имеешь права ни в каком случае, кроме read-only анализа глазами для отладки, что же там происходит (или аналогичных действий вроде создания crash dump).
Да, это грустная специфика такой синхронизации, ничего не поделаешь.

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

Никакого отдельного «для надёжности» тут не может быть в принципе, спинлок или захватывается, или нет.

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

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

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

Спин-локи это просто джентельменский договор.

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

Что мне мешает построить протоколы через память на структурах, которые не требует блокировок? Например, на базе ring. Экслюзивные блокировки пилят потому что запилить нормальный протокол сложнее.

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

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

Более того, там, где она возможна, lock-free имеет все те же свойства, что и спинлок:
1) Никто не отменяет предельной аккуратности в кодировании всех случаев, восстановлении из всех ситуаций,
2) конкретное действие может сорваться, и надо идти на новую попытку.
а, кроме того,
3) при неаккуратной реализации можно получить livelock, причём иногда факторы, что на это влияют, за пределами управления программистом,
4) усложняется (вплоть до невозможности) корректная приоритизация в очереди на воздействие: если на спинлоке организовать очередь с FIFO уже проработано лет 20 как, на lock-free это сильно тяжелее, и кому раньше повезёт — часто непредсказуемо.

Частично для решения указанных проблем предполагается транзакционная память, но в x86, например, она только-только пошла в массы, и то с ограничениями — залагаться в ОС на неё нельзя.

4) усложняется (вплоть до невозможности) корректная приоритизация в очереди на воздействие: если на спинлоке организовать очередь с FIFO уже проработано лет 20 как, на lock-free это сильно тяжелее, и кому раньше повезёт — часто непредсказуемо.

Это имеется в виду multiconsumer/multiproducer? Попробую реализовать

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

1. В юзермоде у нас только вытесняемый режим (ядро может в любой момент прервать выполнение нашего кода). Посему при синхронизации мы часто передаём управление ядру, а уже оно потом будет разбираться. В случае ядра у нас может быть вытесняемый процесс, а может быть невытесняемый. И юзермодные примитивы не работают и почти не используются. Далее по ссылке.

2. Для юзермода память одинаковая для всего. Память в ядре различается. Нам может потребоваться вытесняемая память (которая может быть засвопнута где-нить), а может потребоваться невытесняемая (например, в обработчике прерывание обращение к странице, которая сейчас в свопе это kernel panic). С учётом DMA нам память может понядобится выровненная по определённой границе (зависит от оборудования). И т. д. и т. п. Опять же смотрим kmalloc и разные флаги, которые могут понадобиться.

3. Обработка прерываний realtime со многими своими заморочками. Часто разделяется на две части: первая — обработка неотложных действий связанных с железом, второе — продолжение обработки с меньшим приоритетом Аналогично и потоки/процессы в юзермод и в кернелмод отличаются. В кернелмоде код может выполнятся в контексте процесса или без оного, ...

1. В юзермоде у нас только вытесняемый режим (ядро может в любой момент прервать выполнение нашего кода). Посему при синхронизации мы часто передаём управление ядру, а уже оно потом будет разбираться. В случае ядра у нас может быть вытесняемый процесс, а может быть невытесняемый. И юзермодные примитивы не работают и почти не используются. Далее по ссылке.

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

2. Для юзермода память одинаковая для всего. Память в ядре различается. Нам может потребоваться вытесняемая память (которая может быть засвопнута где-нить), а может потребоваться невытесняемая (например, в обработчике прерывание обращение к странице, которая сейчас в свопе это kernel panic). С учётом DMA нам память может понядобится выровненная по определённой границе (зависит от оборудования). И т. д. и т. п. Опять же смотрим kmalloc и разные флаги, которые могут понадобиться.

Например, если мы разрабатываем ОС для определенного компьютера/класса компьютеров, то половина этих ограничений отваливается сама по себе. Я не собираюсь поддерживать Intel Pentium 2 или AMD Duron.

3. Обработка прерываний realtime со многими своими заморочками. Часто разделяется на две части: первая — обработка неотложных действий связанных с железом, второе — продолжение обработки с меньшим приоритетом Аналогично и потоки/процессы в юзермод и в кернелмод отличаются. В кернелмоде код может выполнятся в контексте процесса или без оного, ...

Если не удастся решить проблему прерываний — тогда я их просто все заблокирую и буду решать все вопросы без прерываний.

Если не удастся решить проблему прерываний — тогда я их просто все заблокирую и буду решать все вопросы без прерываний.

Тянет на шутку дня.

Например, если мы разрабатываем ОС для определенного компьютера/класса компьютеров, то половина этих ограничений отваливается сама по себе. Я не собираюсь поддерживать Intel Pentium 2 или AMD Duron.

Ничего из описанного не зависит от процессора (в пределах последних 20 лет), а вводить ограничение, например, только «кошерными» сетевухами — путь к нулевой клиентской аудитории.

Смысл поддерживать древние сетевухи? Тем более если ОС рассчитана для облачных вычислений, то зоопарка не будет

то зоопарка не будет

Наивный ты ещё. У нас в компании работает 4 человека, которые только обслуживают современный зоопарк от интела.

Смысл поддерживать древние сетевухи?

Из какого пальца ты высосал, что они древние? я такого не говорил.

Тем более если ОС рассчитана для облачных вычислений, то зоопарка не будет

Именно на high performance маркет аппаратные средства как раз выставляют массу ограничений типа «буфера выравнивать на размер страницы». Зато и скорость получается высокая, а не на уровне NE2000.

Я так розумію, свій процесор ти вже «розробив» :)

Підписатись на коментарі