Адреса! Запутался!

Тема в разделе "WASM.BEGINNERS", создана пользователем Luzer, 6 янв 2008.

  1. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    куда уж удобнее? ;)
    почему ограничено?
     
  2. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Фактически, в Windows LDT не используется, за редкими исключениями (см. http://www.wasm.ru/article.php?article=ntldtse)
     
  3. Freeman

    Freeman New Member

    Публикаций:
    0
    Регистрация:
    10 фев 2005
    Сообщения:
    1.385
    Адрес:
    Ukraine
    регистр fs - исключенее ;)
    что примечательно - физический

    а вообще мне нравяцо толковые и развернутые ответы SII, респект!
     
  4. Luzer

    Luzer New Member

    Публикаций:
    0
    Регистрация:
    17 июл 2005
    Сообщения:
    95
    SII спасибо огромное:) Как все в голове-то улегло?
     
  5. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    rei3er
    Ну, тут довольно много чего можно сказать... Но если кратко: намного удобнее, когда один и тот же вектор соответствует лишь одному источнику (а в IA-32 на один вектор может приходиться несколько источников -- внешнее прерывание от устройства, ловушка процессора и программное прерывание по команде INT, из-за чего возникает нужда их фильтровать), а сам вектор является обычным адресом точки входа в обработчик без всякой дополнительной дребедени. В Интеле, вероятно, надеялись введением дескрипторов сложной структуры повысить надёжность ОС, но на практике это ничего не дало: все прерывания (кроме разве что двойной ошибки) обрабатывать проще всего через шлюз прерывания, а сам обработчик выполнять на нулевом уровне привилегий. Т.е. поступать точно так же, как в машинах нормальных архитектур, не имеющих никаких "высокоуровневых" изысков. Сыграло свою роль и желание сохранить максимальную совместимость с 16-разрядными (откровенно неудачными с технической точки зрения) процессорами.

    Ну а если немного конкретики... В IDT могут находиться дескрипторы трёх видов: шлюзы задач, прерываний и ловушек. Шлюзы задач реально не используются, как и сама аппаратная многозадачность (почему от неё полностью и отказались в 64-разрядных процессорах); фактически единственным для него применением является обработка двойной ошибки.

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

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

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

    Так что есть куда удобнее и проще, причём намного :) Возможно, Вы просто не сталкивались с другими архитектурами, поэтому то, что творится в IA-32, Вам кажется вполне разумным и рациональным. (За наезд просьба не считать, это так, мысли вслух :) )
     
  6. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    Freeman
    Знаю, но зачем человеку голову заморачивать? Да и вполне можно обойтись без него, это уже мелкомягкие заморочки, тем более что рядовому программисту они незаметны.
     
  7. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    Luzer
    Э... нихт ферштейн :)
     
  8. Luzer

    Luzer New Member

    Публикаций:
    0
    Регистрация:
    17 июл 2005
    Сообщения:
    95
    Следующие модели процессоров Intel -386, -486, -586 (Pentium) были 32х разрядными. Адресное пространство было увеличено до 4Гб и в них была реализована концепция строчной виртуальной памяти, возможной только в Защищенном режиме.
    Строчная виртуальная память - это как?
     
  9. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Наверное, здесь опечатка и имелась в виду страничная.
     
  10. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    Luzer

    Стопудово страничная.

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

    Например, в 32-разрядных процессорах один сегмент имеет максимальный размер 4 Гб (что объясняется 32-разрядным смещением в сегменте). Однако, если задействовать все сегменты, получаем адресное пространство до 64 Тб минус 4 Гб (8192 сегмента, адресуемых через LDT, и 8191 -- через GDT, всего 16383 сегмента по 4 Гб каждый). А вот физическое адресное пространство оказывается значительно меньшим: в большинстве ранних процессоров даже до гигабайта дойти не могло, потому что у процессора было мало адресных ног. Правда, в конце концов дошли до 32-разрядного физического адреса и даже расширили его (фича такая -- PAE) до 36 бит, что обеспечивает до 64 Гб памяти. Благодаря PAE на некоторых особо крутых 32-разрядных серверах можно было встретить память свыше 4 Гб, но это редкость.

    В общем, размер адресного пространства -- вещь весьма относительная, и если глубоко копать, то приходится учитывать, что, когда и при каких обстоятельствах обсуждается :)
     
  11. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    SII
    в принципе да
    но ты рассуждаешь по большому счету с позиции ОС
    т. е ОС не использует полностью все возможности CPU, но при этом виновата все равно архитектура
    удобнее, но логично ли?
    архитектура просто вводит понятие об IDT и 256-ти дескрипторах
    а дальше развязаны руки, большой простор для маневра, можно реализовать много вариантов использования и т. д
    операционные системы пишутся с расчетом на переносимость, поэтому всякие дополнительные фишки CPU намерянно не используются
    если бы реальная архитектура использовалась на всю катушку, безопасность бы несомненно была бы выше
    логично использовать шлюзы ловушек для исключений
    т. к нет смысла запрещать прерывания при возникновении исключения
    в данном случае мы не сможем использовать адреса > 4Гб даже если будем использовать все доступные сегменты (PAE и страничную адресацию тут не рассматриваем)
    мы даже не получим 64-х терабайтного адресного пространства, потому как часть из них будет описывать подмножество адресов, входящих в другие сегменты
     
  12. Luzer

    Luzer New Member

    Публикаций:
    0
    Регистрация:
    17 июл 2005
    Сообщения:
    95
    Что еще за ловушки?
     
  13. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    шлюз ловушки аналогичен шлюзу прерывания за исключением того, что при вызове через него обработчика не происходит сброс IF в регистре флагов
    шлюз ловушки - системный дескриптор, обеспечивающий процессор информацией о сегменте и смещении кода обработчика в нем, а также права доступа к нему
     
  14. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    rei3er
    Но можно задать вопрос: а почему ОС не использует эти супер-пупер-мега-гипер-крутые возможности аппаратуры? ИМХО, основная причина в том, что использование этих возможностей усложняет жизнь разработчикам осей, но не даёт каких-либо существенных преимуществ (если вообще даёт хоть какие-то практические, а не высосанные из пальца; к последним можно отнести, например, рассуждения о пользе сегментации для индивидуальной защиты различных структур данных -- вроде как раз в интеловском талмуде они есть, но точно не помню, а смотреть лень).

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

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

    Я соглашусь с тем, что в ОС, рассчитанных на переносимость (и Унихы, и Винда НТ именно таковыми и являлись), это является довольно веским, но не абсолютным аргументом (полной переносимости без малейших переделок в системном ПО всё равно не добьёшься, а переносимость на уровне прикладного ПО в любом случае придётся обеспечивать за счёт перекомпиляции: ОпенОфис, скомпилированный для IA-32, всё равно не пойдёт на zServer, хотя Линух есть и для того, и для другого). Поэтому я бы сказал так: эта проблема есть, но её влияние преувеличено.

    Опять употреблю моё любимое слово: чушь :) Возможностей, существовавших уже у процессоров 1960-х годов (два режима -- системы и пользователя, и наличие простейшей страничной защиты памяти без преобразования адресов, т.е. ещё без виртуализации памяти) совершенно достаточно для создания абсолютно надёжной системы (абсолютно в том смысле, что, если в её реализации не будет ошибок, она будет неуязвима для любых действий рядовых пользователей и запускаемых ими программ). В этом плане дополнительные два режима процессора, аппаратные ограничения на межуровневые вызовы подпрограмм и прочее, реализованное в 80286 и IA-32, ничего дополнительного к надёжности не дают, скорей наборот: во всём этом куда легче запутаться, чем в двух режимах и простых страницах, и понаделать лишних ошибок.

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

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

    ИМХО, можно получить даже без PAE. Правда, для этого системе придётся извращаться. Идея такая: если в данный момент времени задача использует сегмент А, то остальные сегменты не нужны, и их дескрипторы можно объявить недействительными. Когда же задача попробует использовать селектор недействительного сегмента, возникнет прерывание, по которому ось сформирует правильный дескриптор, подрузит нужный сегмент, а ранее использованный объявит недействительным. Хотя 64 Тб действительно не получим (задача же в один момент времени может работать с шестью сегментами одновременно -- по числу сегментных регистров; а их суммарный размер не может превышать 4 Гб из-за ограничений линейного адреса).

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

    Luzer
    rei3er уже, в общем-то, объяснил. По сути, разница между шлюзом прерывания и шлюзом ловушки в том, что если обработчик прерывания вызывается через шлюз прерывания, то прерывания будут запрещены (флаг IF сброшен), а вот если через шлюз ловушки -- они могут быть и запрещены, и разрешены (состояние флага IF не меняется). Значит, обработчики прерываний, не допускающие прерывания собственной работы, должны вызываться через шлюз прерывания, ну а допускающие могут вызываться и через шлюз ловушки.
     
  15. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    лимит стека в соответствующем дескрипторе можно использовать для защиты от переполнения (мы как то об этом уже говорили) в режиме ядра
    что будет если возникнет #PF в режиме ядра? насколько я знаю - BSOD
    а если использовать для детекта этого события #SS - можно четко отличить переполнение от обычного #PF и не генерировать BSOD
    имхо, отказоустойчивость только возрастет
    т. е ты предлагаешь создать несколько IDT для разных событий. Так?
    но стараются минимизировать зависимую от платформы часть кода
    в этом вся суть
    если есть два варианта реализации определенной функции (програмный и аппаратный), то в большинстве случаев будет выбран програмный способ, а он зачастую менее быстр и надежен
    это все равно, что сравнивать виртуальную машину на основе VMX и без дополнительных расширений
    вроде эффект тот же, а в реализации VMX куда более логичен и менее подвержен ошибкам засчет большой автоматизации операций, которые иначе пришлось бы реализовывать вручную, опираясь на некоторый базис
    так или иначе, но все события IDT обрабатываются последовательно
    что-то все равно будет обрабатываться первым, а что-то вторым
    так все равно, чтобы там не подгрузилось, это что-то будет лежать в диапазоне 0 - 4Гб
    даже если в любой момент времени активны 6 сегментов, то мы 6 * 4 = 24Гб не получим, поскольку адрес 32-ух разрядный
    АП как было 32-ух разрядным, так и останется
     
  16. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    Ничто не мешает ОС посмотреть, по какому именно адресу и при какой команде возник #PF, и выполнить соответствующие действия. Но, если говорить строго, нехватки стека в ядре не должно возникать ни при каких обстоятельствах: если такое происходит, это говорил либо о банальной ошибке в коде ядра (и тогда BSOD всё равно неизбежен), либо о кривой "идеологии" ядра.

    На самом-то деле, если ядро грамотно спроектировано, то под стек вполне хватает от нескольких сотен байт до нескольких килобайт. В моей любимой RSX-11 (полноценная 16-разрядная многозадачная ОС реального времени) максимальный размер стека был, если склероз не изменяет, 112 байт, при этом при запрещённых прерываниях ядро практически не работало -- просто оно было грамотно спроектировано "идеологически" и хорошо реализовано практически. Конечно, в современных осях со значительно увеличившимся количеством внешних устройств число параллельно работающих обработчиков прерываний возросло, но отнюдь не в десятки раз.

    В общем, насчёт отказоустойчивости не убедили: в ядре просто не должны возникать ни #PF, ни #SS.

    Я предлагаю полностью отделить векторы прерываний разных источников друг от друга. Кстати говоря, описанная мною в предыдущем посте схема разделения взята из жизни -- я её скоммуниздил у IBM, где она была использована в мэйнфреймах System/360 ещё в 1960-х годах (правда, там было ещё одно прерывание -- повторного пуска, которое возникало при нажатии оператором ЭВМ соответствующей кнопки на пульте управления машины и задумывалось как средство перезапуска ОС после, например, сбоя процессора). У каждой из указанных причин был свой вектор, ну а конкретную причину пояснял код прерывания (например, если происходило прерывание ввода-вывода, то дополнительно сохранялись адрес устройства и так называемое слово состояния канала, которое описывало конкретную причину прерывания).

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

    Не всегда менее быстр (в 80386, помнится, задачу быстрее было переключить ручками, чем с помощью TSS -- потому что можно было избежать кучи лишних действий), ну а надёжность зависит от криворукости разработчиков ПО и аппаратуры (что в процессорах регулярно обнаруживаются глюки, думаю, мало для кого является большим секретом).

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

    Например, одна из первых, если не первая система виртуальных машин -- IBM VM/370 -- работала на самых что ни на есть рядовых мэйнфреймах System/370, не имеющих никаких аппаратных изысков: обычные два режима работы процессора (с полным, однако, запретом доступа к чему-нибудь системному со стороны прикладных программ) и обычная виртуальная память на основе страниц. Конкретные процессоры, правда, могли содержать специальные расширения, упрощающие жизнь монитору виртуальных машин (центральной части VM/370) и повышающие производительность, однако всё чудесно работало и без них.

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

    Прочитайте внимательнее, что я по этому поводу писал ;)
     
  17. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    не должны и не возникают - это разные вещи
    даже если предположить, что само ядро безошибочно, то про драйвера в общем случае такого не скажешь
    а валить всю систему из-за одного кривого драйвера глупо (естественно, при некоторых обстоятельствах другого решения нет)
    небольшая рекурсия вполне может привести к переполнению, хотя код будет без ошибок
    рекурсия зло?
    да и просто большая вложенность функций не гарантирует безошибочность выполнения
    невозможно расчитать из кода на ЯВУ, какой объем стека используется
    мне не понятен сам момент программирования этих векторов
    если нет разделения на разные таблицы событий
    как раз из-за минимизации
    все аппаратно-независимое обычно пишется на С, что подразумевает полный перенос кода на другие платформы с большей разрядностью без его модификации
    сравнение некорректно
    сравнивать надо при прочих равных условиях
    т. е если програмно реализовывать логику переключения через TSS, то вряд ли это будет быстрее
    нет полностью пряморуких разработчиков, иначе у нас сейчас были бы идеальные безошибочные системы
    и второе, все таки, имхо, глюки в процессорах скорее большое исключение, чем правило
    какие, например, регистры? IDTR?
    в чем невозможность? на худой конец пошаговую трассировку с выявлением всех таких инструкций еще никто не отменял
    хорошо, а как на таких системах виртуализировался привилегированный уровень?
    трассировкой или подменой уровня? в любом случае геморрой еще тот
    аппаратная же виртуализация специфицирует этот процесс и в разы упращает его
    мы же договорились, что шлюзы ловушек только для исключений
    обработка исключения может быть отложена, это не критично (кроме #NMI, #MC
    , но там свои механизмы действуют)
    на пальцах можно, а то либо я, либо вы чего-то не понимаете ;)
    без страничной адресации и PAE мы никак не опишем АП > 4Гб, сколько бы для этого сегментов не использовали
     
  18. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    rei3er
    Если драйвер -- часть ядра, то иного выхода в любом случае нет: упал драйвер -- вали систему. Если же драйвер выполняется в собственном пространстве как задача -- другое дело. Но по-любому не только ядро, но и драйвера не должны быть кривыми. В идеале, конечно :)

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

    Вопрос качества проектирования системы. Плюс пишите на ассемблере. Насчёт трудоёмкости в том, что касается низкоуровневых вещей -- чушь, трудоёмкость возрастает процентов на 20, не больше, а количество ошибок по сравнению с Си уменьшается. Единственный реальный минус -- отсутствие переносимости. Но нужна ли она? ИМХО, переносимость системного ПО -- один из величайших обманов наших дней, дающий в результате ненадёжные и неэффективные системы, непригодные для ответственных применений.

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

    Ошибки допускают все, только вот не все способны даже свои собственные ошибки отловить и исправить. И последних становится всё больше. Вот и придумывают механизм сборки мусора в C#, т.е. борются со следствием (утечки памяти), а не причиной (ошибки в коде, приводящие к этим самым утечкам). Но если для бизнес-софта подобный подход ещё терпим, то для какой-нить там АСУ ядерным реактором -- увольте.

    Не всегда. Чтобы добиться 100% переносимости, мало писать на ЯВУ, надо ещё и определённую дисциплину программирования соблюдать. Хотя это в общем-то не шибко сложно.

    А что непонятного-то? Убейте, не пойму :)

    Например, в той же System/360 (наша ЕС ЭВМ) некое внешнее устройство заканчивает ранее начатую операцию ввода-вывода. Естественно, на радостях оно вырабатывает запрос прерывания. 64-разрядное слово состояния процессора (PSW; там хранятся основные биты управления режимами, в частности, бит система/задача и маски прерываний, а также адрес выполняемой команды; на ПК аналогом PSW будет связка EFLAGS + EIP) сохраняется в специальной области памяти, так называемом старом PSW ввода-вывода (стека на System/360 попросту нет); кроме этого, в других заранее определённых ячейках памяти сохраняются адрес устройства и слово состояния канала (CSW). После чего из области нового PSW ввода-вывода в PSW загружается новое значение, что приводит к передаче управления на программу обработки прерываний ввода-вывода. Эта программа анализирует CSW, чтобы понять, что случилось (закончилась операция, устройство подало сигнал "Внимание", кто-то умер...), и выполняет соответствующую обработку. Если обработка зависит от конкретного типа устройства, вызывается соответствующая подпрограмма, если же нет -- всё обрабатывается, так сказать, универсальным обработчиком (в System/360 ввод-вывод сильно унифицирован, что и позволяет значительную часть операций по управлению им производить независимо от типа устройства: хоть ввод с перфокарт, хоть магнитный диск, хоть дисплюй).

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

    Таким образом, в случае с System/360 имеем программно-аппаратную идентификацию источника прерывания: класс прерывания идентифицируется аппаратно (у каждого класса свой вектор, а значит, и свой обработчик), а конкретную причину приходится определять программно анализом соответствующего кода (причём назначение всех битов и байтов описано в фицияльном руководстве, и гадать на кофейной гуще, что к чему, не приходится). Это значительно удобнее, чем в ПК, где для однозначного определения источника прерывания нужно устраивать танцы с бубнами (так, с помощью дескрипторов нужно блокировать возможность выполнения задачами команд INT с номерами, соответствующими векторам, используемым для обработки исключений и внешних прерываний (а в реальном режиме вообще хрен их разделишь); в случае внешних прерываний -- опрашивать устройства, чтобы определить конкретный источник, если одна линия IRQ разделяется несколькими устройствами, ну и т.д.). Однако можно сделать ещё удобнее: для каждого отдельного типа прерывания -- свой уникальный вектор. Тогда обработчику не придётся даже код прерывания анализировать, поскольку сразу будет вызываться именно тот обработчик, какой нужен.

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

    Например, IDTR :) А Вы представляете, какая будет скорость при пошаговой трассировке? Но это ещё не всё: чтобы гарантировать работоспособность любой программы на виртуальной машине, надо её именно трассировать всё время, а не пускать на выполнение на реальном железе (ведь нельзя заранее гарантировать, что программа не содержит самомодифицирующийся код, что все ветви выполнения были оттрасированы и никаких "подводных камней" в них нет и т.д.). Потому-то полноценная система виртуальных машин на IA-32 и невозможна без специальной аппаратной поддержки, возможно лишь создание эмуляторов -- а это всё ж другая опера.

    Гостевая ОС работала как обычная задача, и при попытке выполнения ею привилегированных вещей происходило прерывание (по недопустимой инструкции, например), и соответствующие вещи эмулировались монитором виртуальных машин. Естественно, это довольно геморройно, но отнюдь не катастрофически. Ну а аппаратная виртуализация действительно упрощает реализацию системы виртуальных машин, но не является для этого необходимой: всё работает и без неё. А на ПК -- фигвам, без неё не обойтись.

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

    Наверное, я плохо объяснил...

    Во-первых, в каждый конкретный момент времени задача действительно может иметь доступ максимум к 4 Гб неперекрывающегося виртуального адресного пространства, поскольку эта величина ограничивается размером линейного адреса (после отработки сегментации мы имеем 32-разрядный линейный адрес).

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

    Ну а в-третьих, представьте себе такую картину. Для упрощения расчётов предположим, что FS и GS не используются.

    У задачи имеется 1024 сегмента по 1 Гб каждый (их дескрипторы находятся в LDT или GDT -- это не суть важно, хотя проще в LDT, потому что тогда проще переключать подобные задачи). Получается, что в каждый момент времени задача может использовать 4 любых сегмента суммарным размером до 4 Гб, однако, переходя с сегмента на сегмент, ей будет доступно 1024 * 1Гб = 1Тб памяти. Возникает, однако, вопрос: как это представить с помощью 32-разрядного линейного адреса? Очень просто (на бумаге :) ).

    Все неиспользуемые в данный момент сегменты должны выгружаться из линейного адресного пространства (т.е. никакие линейные адреса на них не отображаются, что достигается соответствующей настройкой страничного механизма; в частности, они могут быть выгружены на диск), а их дескрипторы -- объявляться недействительными. Тогда все 4 Гб линейного адресного пространства оказываются в распоряжении тех сегментов, которые действительно используются в данный момент (чьи селекторы загружены в сегментные регистры), поэтому их суммарная длина может составлять эти самые 4 Гб.

    Если задача загружает в сегментный регистр новый селектор, происходит прерывание по попытке использования недопустимого дескриптора (происходит ли это в момент загрузки селектора либо при попытке первого доступа к этому сегменту -- не суть важно, это детали, не влияющие на общий принцип). ОС делает недоступным сегмент, который ранее использовался через этот регистр: объявляет недействительным дескриптор этого сегмента и освобождает занимаемое им линейное адресное пространство (сам сегмент может быть переписан на диск или же оставаться в ОЗУ, если объёма памяти достаточно -- это уже другой вопрос). После этого система делает действительным сегмент, к которому хочет обратиться задача, корректирует в его дескрипторе базовый адрес, а заодно соответствующим образом настраивает таблицы страниц, чтобы этот сегмент теперь использовал линейные адреса, ранее принадлежавшие только что "отключенному" сегменту.

    В результате получается, что сегменты размером 1 Гб каждый используют общее линейное адресное пространство в 4 Гб, а поскольку они чередуются, суммарное доступное адресное пространство превосходит 4 Гб.
     
  19. rei3er

    rei3er maxim

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    917
    Адрес:
    minsk
    а вот и нет
    Linux тому подтверждение
    #PF в режиме ядра не валит систему, а локализует и останавливает выполнение кода модуля (драйвера)
    есть два случая, когда возникает kernel panic: в момент #PF текущим процессом была захвачена 1 или более блокировок или #PF возникло в контексте аппаратного прерывания
    в корне не согласен
    откуда взята цифра 20? почему количество ошибок уменьшается? при написании кода на С вероятность появления "глупых" ошибок по сравнению с кодом на ассемблере уменьшается пропорционально размеру кода
    насчет переносимости, извините, но это чушь ;)
    если все ядро писать на ассемблере под каждую платформу, то во-первых, это будет крайне долго (а следовательно есть вероятность того, что скорее появится другая архитектура и другие процессоры, чем ядро будет дописано), а во-вторых - код получится трудномодифицируемый
    что ни говорите, а код на С модифицируется проще
    ясно
    не знаю, по-моему и сейчас не надо ничего гадать
    выставляем DPL во-всех шлюзах (кроме шлюза системных вызовов) равным 0 и все
    0 - 31 исключения (APIC программируется на вектора > 31)
    остальные шлюзы для внешних прерываний и шлюза системных вызовов
    заранее оговариваем, что INT на нулевом кольце не используется
    но в принципе согласен, модель разделения на классы вполне эффективна
    а можно отлавливать самомодификацию путем защиты кода от записи
    а если 4Гб?
    тогда с диска придется подгружать все 4Гб
    т. е модифицировать всю физическую память
    т. е модифицировать код, который будет модифицировать физическую память
     
  20. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    rei3er
    Ерудна. Надёжность Линуха столь же отстойная, что и Винды. Для офиса или там сервера -- вполне годится, для управления ответственными техпроцессами -- нет. А сбойнувший драйвер может запороть всё, что угодно, поэтому обязательно надо обрушивать всю систему, а не пытаться работать дальше, делая вид, что всё хорошо. Если же хотите более-менее приемлемую надёжность при кривых драйверах -- делайте микроядерную систему и выносите драйверы за пределы адресного пространства ядра (а заодно и лишайте их "ядерных" привилегий).

    Цифра -- из всяких умных статей и по собственному опыту.

    Насчёт ошибок. Си поощряет их возникновение из-за своего дурацкого синтаксиса и отсутствия нормального контроля типов (программисту приходится прикладывать весьма значительные усилия, чтобы этих ошибок не было, и не использовать целый ряд возможностей языка). Конечно, в асме контроля типов нет вовсе (не считая контроля за размерами переменных, и то далеко не на каждом асме), но там внимательней относишься к самому написанию программы, ну а любой ЯВУ в этом плане расхолаживает. Написал "a = b + c" -- и доволен, если компилятор не ругнулся (а сишный ругается сравнительно редко, Паскаль или Ада хрен пропустят какие-либо нарушения, которые для Си являются нормой), хотя на асме, скорей всего, внимательней относился бы к тому, какие операнды и операцию указываешь.

    Задачи обработки информации (в широком смысле -- хоть бухгалтерия, хоть 3D-рендеринг, хоть СУБД) действительно проще и удобнее писать на ЯВУ, но когда идут сплошные манипуляции битами-байтами, логические проверки, работа с адресами и т.п., ассемблер часто оказывается не менее удобным, чем ЯВУ, и уж точно более адекватным задаче (грубо говоря, запись if ((a && 0x80) == 0) ничуть не более читабельна, чем идущая подряд пара команд test и jne; ну а по мне -- даже менее читабельна, поскольку скобки и спецсимволы затрудняют "мгновенное" восприятие смысла операции). А низкоуровневое системное программирование -- именно такое: проверить бит, переслать байт, перейти туда-то...

    Размер же кода в смысле длины исходного текста вообще не имеет прямой связи с количеством ошибок. Тем более что для того, чтобы текст на ЯВУ был читабельным, его самого надо упрощать, и особенно это касается "шифрограмм" на Си (исходник на Паскале читается куда легче из-за внятного синтаксиса).

    Извиню, но чушь говорите Вы :-P Всю ОС писать на чистом асме -- да, долго, нудно и неоправданно (к примеру, на кой писать на ассемблере графическую оболочку или компилятор?). А вот ядро -- весьма компактная вещь, пишется достаточно быстро и достаточно просто (при наличии готового проекта -- а проектирование не связано с используемым языком программирования -- вполне реально написать полнофункциональное ядро за несколько недель, но, есно, без драйверов и прочей поддержки внешних устройств -- только многозадачность, виртуальная память и т.п.). Если, конечно, не валить в ядро всё подряд, как это сделано в Винде и Линухе. Что, например, делают в ядре высокоуровневые функции ввода-вывода (поддержка файловых систем или там сетевых протоколов)? Это дело самый раз вынести за пределы ядра, оставив в нём лишь то, что работает с железом или выполняет низкоуровневые операции управления процессами, потоками, памятью, синхронизацией.

    Код на Си модифицируется ничуть не проще, потому что разобраться в программе на Си не легче, чем на ассемблере. Если, конечно, не писать на Си в стиле Паскаля -- тогда будет несколько проще, но не шибко. Да и не меняется ядро через каждый месяц. Когда последний раз в Линухе добавляли новые функции АПИ?

    Хотя в любом случае спор о том, что лучше -- Си, Паскаль или ассемблер -- является в данном случае оффтопом, да и вообще не слишком продуктивен (поскольку во многом носит "идеологический", а не чисто технический характер). Если желание есть -- можно пообсуждать, но где-нибудь в другом месте :)

    Ну так в том-то и дело, что в Интеле в конце концов дали программистам средства для разделения источников прерываний, но так и не сделали "автоматическое" разделение на уровне аппаратуры -- уже из-за сохранения совместимости с кривой архитектурой 8086. Я ж не утверждаю, что подобное разделение произвести невозможно -- Вы, собственно, и описали, как это надо делать. Но делать это приходится программисту, потому что создатели процессора о такой мелочи не позаботились. Поэтому и говорю, что IA-32 -- уродская архитектура.

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

    Мы спорили о том, удастся ли обеспечить задаче виртуальное адресное пространство свыше 4 Гб ;) Я и показал, как это можно сделать за счёт сегментации на 32-разрядной системе. Хотя при этом ни один из сегментов не может иметь размер даже в 4 Гб, их придётся делать меньше. Ну а с учётом массового появления 64-разрядных процессоров моя "идея" носит лишь академический, так сказать, характер: как демонстрация потенциальных возможностей IA-32, которые так и остались неиспользованными (в первую очередь, думаю, из-за сложности и тормознутости такой модели, ведь это без конца сегменты придётся с диска в память и обратно гонять, если у тебя нет PAE и большого объёма физической памяти).