26

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

Я правильно понимаю, что при запуске, её код будет отправлен на исполнение процессором? Код будет последовательно извлекаться процессором из RAM и исполняться команда за командой. Если это так, то как система контролирует права доступа? Как ограничивает исполнение команд, доступных только привилегированному пользователю?

Что помешает коду, запущенному от пользователя без особых прав приказать процессору обратиться напрямую к жесткому диску, достать данные, доступные только root пользователю, обработать и перезаписать их, если код без изменений попадает на выполнение прямо в процессор?

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

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

Объясните, как работает механизм ограничения прав или поправьте, если я не правильно понимаю как происходит исполнение машинного кода.

  • Комментарии не предназначены для расширенной дискуссии; разговор перемещён в чат. – ЮрийСПб Jul 12 '18 at 11:36
  • @ЮрийСПб какой лимит на комментарии? Что бы знать в будущем сколько комментариев ещё норм, а когда уже слишком –  Jul 12 '18 at 11:52
  • 2
    20 штук - после этого в модераторской админке появляется уведомление) – ЮрийСПб Jul 12 '18 at 12:04
  • 2
    Почему-бы на этом сайте не оформлять комментарии сразу как чат? Хотя бы для того, чтобы не напрягать админов лишней работой по перемещению комментариев в чат? А всех остальных не заставлять нажимать 100500 ссылок, чтобы поглядеть обсуждение. :-( – pepsicoca1 Jul 12 '18 at 15:12
  • Наверняка так можно, но это надо ресурсы под это выделять и миграции данных, наверняка проводить и решать как со старыми комментами поступить... А можно оставить как есть и понадеяться на модераторов - всё таки у нас, на РУ версии таких случаев (20+ комментов под постом) в день 1-2. Можно и вручную обработать. А в 99% случаев много комментариев не образуется и скрытие их только повредит. А так - можно на основной мете выдвинуть предложение - может его и рассмотрят если будет большой запрос. – ЮрийСПб Aug 10 '18 at 23:41
  • У Linux kernel обширная документация можете глянуть https://www.kernel.org/doc/ BSD проект немного постарше и с хорошей документацией https://www.freebsd.org/docs.html – Hellseher Aug 11 '18 at 21:41

1 Answers1

40

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

UPD1:

И да, попытка полезть в чужую память ограничивается не приоритетом команды, а так называемым устройством управления памятью (MMU - Memory Management Unit). Каждой задаче выделяется кусок памяти и в MMU прописываются границы этой памяти. При каждом обращении к памяти MMU аппаратно контролирует эти границы. При попытке записать/считать вне выделенных границ MMU генерирует исключение и управление попадет обратно к операционке. А насчет команд обращения к портам - они таки могут запускаться только из высокоприоритетных задач.

UPD2:

А если программе нужно прочитать файл, как это происходит? Опишите процесс

Так как напрямую к портам контроллера диска задача обратиться не может, то она вынуждена выполнять так называемый системный запрос к операционке. В случае чтения файла программа обращается к драйверу файловой системы (который потом обращается к драйверу диска). Драйвер диска работает на уровне приоритета ядра (или на уровне приоритета драйверов) и ему позволено обращаться к регистрам контроллера диска. Также программа поступает (выполняет системный запрос к операционке, так называемый system call) когда ей надо вывести что-то на экран или обратиться к сети или к порту USB. Так как управление передается операционке, то операционка может проанализировать все уровни привилегий данной программы и выполнить запрос или отказать в выполнении запроса, если привилегий недостаточно или, скажем, системный файл недоступен на уровне этого пользователя. Также операционка организует разделение доступа к данным, например если несколько программ пытаются одновременно писать в один и тот же файл.

UPD3:

И да, уровни приоритета о которых идет речь это не уровни приоритета процессов и потоков в операционке. Это аппаратные уровни приоритета в процессоре. В разных архитектурах это организовано по-разному. Иногда это два уровня - супервизор и пользователь/пользователи. Иногда три уровня - супервизор, драйверы и пользователь/пользователи. А уровни приоритета процессов и потоков в операционке существуют отдельно от аппаратных приоритетов, существующих в процессоре.

UPD4:

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

Честно говоря, я не знаю, где есть хорошие популярные обзоры по этому вопросу. Но где-то они, безусловно, есть попробуйте погуглить. Сам я довольно много программировал контроллеры на низком уровне без всякой операционки, прямо по "голому" железу. Чтобы это делать, приходилось читать даташиты на процессоры. Оттуда я и почерпнул эту науку. Однако порекомендовать другим этот путь я не могу, ибо типовой даташит на процессор содержит 500-1000-1500 страниц на английском.

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

UPD5:

Я смотрю, ответ набирает популярность. Что же, добавлю еще свои пять копеек. Существуют процессоры, в которых нет аппаратной поддержки управления памятью (то есть нет MMU) и нет разделения режимов работы на режим супервизора и пользователя. Эти процессоры предназначены для работы в так называемых встроенных системах (embedded system), в которых нет поддержки диска, нет аппаратной поддержки свопинга (которую традиционно осуществляет тот же MMU), нет загрузки новых задач во время работы, а все задачи изначально работают в ROM. Таковы, например, PIC процессоры от Microchip, сигнальные процессоры от Analog Device и многие другие. Но есть и процессоры, ориентированные на работу под ОС общего назначения, таких как Windows и Linux с полной поддержкой многозадачности и многопользовательности. В этих процессорах обязательно есть и MMU и разделение на режим супервизора/пользователя. К таковым процессорам относятся прежде всего x86 от Intel, ARM, любимый производителями смартфонов, и еще многие процессоры разной степени известности и коммерческой успешности.

UPD6:

"В случае чтения файла программа обращается к драйверу диска." - тут две ошибки. Во-первых, сначала обращение идет к драйверу ФС. Во-вторых, программа не выбирает к какому драйверу она обращается... – Pavel Mayorov 2 часа назад

@Pavel Mayorov Тут нет никаких ошибок потому что:

  1. В данном случае не важно, что сначала отрабатывает драйвер файловой системы, так как драйвер файловой системы не обращается к контроллеру диска. А вопрос был именно о том, как осуществляется разделение доступа к контроллеру диска.

  2. Когда программа осуществляет system call, то она тем самым выбирает, к какому драйверу ОС перенаправит это обращение. То есть выбирая вид системного запроса (к диску, к экрану, к порту USB), программа тем самым выбирает конечный драйвер контроллера, который будет вызван. Безусловно, перед тем как отработает драйвер контроллера, будет выполнено очень много килобайтов кода самой ОС, включая (в случае обращения к диску) контроль прав доступа к файлу, контроль занятости файла другой программой, возможно код сжатия/разжатия диска в случае архивированного тома, возможно код сетевой подсистемы в случае удаленного диска и еще куча всяких чудес включая код виртуальной машины (в случае нахождения программы на виртуальной машине). Но вопрос был не об всех этих чудесах, а о том, как в целом с точки зрения аппаратуры организован в ОС доступ из многих программ к аппаратным ресурсам, которые в системе существуют в единственном экземпляре. И какая аппаратная поддержка ОС существует в современных процессорах.

UPD7:

Тут просили указать литературу по обсуждаемому вопросу. Я вспомнил одно название:

«Архитектура микро-процессора 80286» авторы С. П. Морс, Д. Д. Алберт.

Книга древняя, 1990 года издания. Да и процессор древний. Но там как раз рассказано, как в 80286 организована аппаратная поддержка нормальной ОС, а не MSDOS, которая, как известно, работала в реальном режиме процессора Intel. У меня эта книга была в бумажном виде. Конечно 80286 это немного не то, что потом сделал Интел в 80386 и дальше, но хоть что-то. Да и книга, конечно, коряво написана. А может это перевод такой. Но, как говорится, на безрыбье и рак рыба. Еще важно понимать, что архитектура x86 это не идеал, есть и другие подходы.

UPD8:

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

UPD9:

В книге «Архитектура микро-процессора 80286» надо смотреть главу 5. Там описано какие у процессора 80286 есть аппаратные средства для поддержки ОС.

pepsicoca1
  • 5,019
  • 1
    А если программе нужно прочитать файл, как это происходит? Опишите процесс –  Jul 11 '18 at 14:18
  • Спасибо за ответ. Посоветуйте литературу или другие источники, где можно узнать об этом более подробно –  Jul 11 '18 at 17:35
  • @Петр, вот хороший цикл статей (правда, ссылка на середину) о том, как работает компьютер. Почитайте – avp Jul 11 '18 at 20:48
  • 1
    "В случае чтения файла программа обращается к драйверу диска." - тут две ошибки. Во-первых, сначала обращение идет к драйверу ФС. Во-вторых, программа не выбирает к какому драйверу она обращается... – Pavel Mayorov Aug 15 '18 at 05:09
  • @Pavel Mayorov Тут нет никаких ошибок потому что: 1. В данном случае не важно, что сначала отрабатывает драйвер файловой системы, так как драйвер файловой системы не обращается к контроллеру диска. А вопрос был именно о том, как осуществляется разделение доступа к контроллеру диска. 2. Когда программа осуществляет system call, то она тем самым выбирает, к какому драйверу ОС перенаправит это обращение. То есть выбирая вид системного запроса (к диску, к экрану, к порту USB), программа тем самым выбирает конечный драйвер контроллера, который будет вызван. – pepsicoca1 Aug 15 '18 at 07:40
  • @pepsicoca1 да? Ну вот, к примеру, возьмем системный вызов sys_open/NtOpenFile. Это запрос к драйверу диска или порту USB? – Pavel Mayorov Aug 15 '18 at 07:58
  • @Pavel Mayorov Спасибо Pavel Mayorov что не даете мне скучать. Не все системные запросы заканчиваются обращением к аппаратным ресурсам. Современные ОС имеют кучу всякого мусора, навешенного на систему. Начиная от перекодировки Юникода в UTF-8 и кончая системными аналогами atoi. Опять же повторю, что вопрос топик стартера был в том, как организован в ОС доступ к аппаратным ресурсам, а вовсе не о всех видах системных запросов, которые существуют на этой планете. – pepsicoca1 Aug 15 '18 at 08:15
  • Какой хороший вопрос/ответ. Жаль только что в такой объёмной теме один ответ кажется ну очень уж непозволительно приближённым. –  Aug 15 '18 at 22:27
  • @Other Я старался как мог. Все, кто хочет уточнить, дополнить, исправить ответ могут создать свой ответ и в нем уточнить, дополнить, исправить или вообще написать свой вариант ответа. Ведь тем и прекрасен сайт SO, что на нем каждый может озвучить свою версию ответа на заданный вопрос. – pepsicoca1 Aug 16 '18 at 03:10
  • @pepsicoca1, Я про то, что ответ на этот вопрос достоин отдельной книги (хотел бы почитать), поэтому ответ на пару тысяч символов не может охватить всё, поэтому могут быть вопросы вроде PavelMayorov. –  Aug 16 '18 at 03:24
  • @Other По этому вопросу написаны десятки тысяч книг. И профессионально-теоретических, и профессионально-практических, и популярных. Написано это все людьми, которые реально занимаются разработкой процессоров. Более того, все написанное широко используется в сотнях архитектур разных процессоров. А PavelMayorov зачем-то толсто троллит меня, причем уже не раз на этом сайте он до меня докапывался. У него никакие не вопросы, а попытки найти типа ошибку у меня в ответе. Я ему уже посоветовал самому написать ПРАВИЛЬНЫЙ ответ, но админы стерли этот совет. – pepsicoca1 Aug 16 '18 at 04:31
  • @pepsicoca1, Есть хорошая литература на примете? Такая, которая не расписывает на 1000 страниц один аспект как спецификация, а для просто любопытных. Гуглить могу, но пока гугл не выводит качество каждой книги, а Вы, вроде, в теме. Админов у нас нет, есть модеры, но они вряд ли стали бы стирать комментарий, если только он действительно не плох. Сейчас тестируется автоматика на удалении, может Павел нашёл её слабое место и так атакует Вас, не знаю. –  Aug 16 '18 at 04:39
  • Я уже писал в ответе, что сам я изучал эту науку по даташитам на процессоры. Наверное можно найти на русском обзор архитектуры 80386 или 80486 машины. Позже, скорее всего, перестали переводить. Если прочесть обзор архитектуры даже для 80386, то станет примерно понятно, как устроена аппаратная поддержка ОС в процессорах. Еще важно понимать, что 80386 не идеал, есть и другие подходы. – pepsicoca1 Aug 16 '18 at 04:47
  • 1
    @Other Опять же, даже в популярном изложении эта наука довольно бесполезна для прикладного программиста. Если Вы не пишете драйверов, не пишете свою ОС общего назначения, не портируете Юникс, то Вам все это не нужно. Для программиста-прикладника достаточно программной модели, которую ему дает ОС. Есть код, есть ОЗУ, есть heap, есть запросы к ОС, называемые сейчас API. Вот и все, что нужно знать о взаимодействии с ОС. Так же, как не надо знать теорию трансляторов, чтобы пользоваться GCC. Так же, как не надо знать физику твердого тела, чтобы пользоваться транзисторным приемником. – pepsicoca1 Aug 16 '18 at 04:52
  • @pepsicoca1, Я любопытный и хочу знать фундаментальные ответы всего, даже если мне это не нужно. А может и будет нужно, никогда не знаешь куда тебя отправит судьба. –  Aug 16 '18 at 04:56
  • @Other Когда судьба отправит, тогда и выучите все, что надо. Как говорится, "давайте решать проблемы по мере их появления". А вот, вспомнил одно название. «Архитектура микро-процессора 80286» авторы С. П. Морс, Д. Д. Алберт. У меня она была в бумажном виде. Конечно 80286 это немного не то, что потом сделал Интел в 80386 и дальше, но хоть что-то. Да и книга, конечно, коряво написана. А может это перевод такой. Но, как говорится, на безрыбье и рак рыба. – pepsicoca1 Aug 16 '18 at 05:01
  • @pepsicoca1, Когда отправит, тогда метаться уже поздно. Ну у меня обычно так. Посмотрю, благодарю. –  Aug 16 '18 at 05:06
  • 1
    @Other [Когда отправит, тогда метаться уже поздно.] Вот выучите Вы поддержку свопинга в 80286, а судьба отправит Вас на ловлю лосося на Дальнем Востоке. Жалко будет времени потраченного. – pepsicoca1 Aug 16 '18 at 05:08
  • 1
    @pepsicoca1, Буду ловить зная это и радоваться этому. Или, что ещё лучше, поняв смысл этих действий, можно придумать автоматическую ловушку, использовав палки и камни. –  Aug 16 '18 at 05:50