Понимание того, как вещи выводятся на экран (cout, printf) и происхождение действительно сложных вещей, которые я не могу найти в учебниках - PullRequest
23 голосов
/ 30 марта 2010

Я всегда удивлялся этому и до сих пор не нашел ответа. Всякий раз, когда мы используем «cout» или «printf», как именно это напечатано на экране? Как текст получается таким, какой он есть ... (возможно, здесь довольно расплывчатый вопрос, плохо работаю с тем, что вы мне даете) Так в основном, как эти функции сделаны? .. это сборка ?, если так, то где это начинается ?. Это вызывает больше вопросов, например, как они сделали функции openGl / directx ..

сломайте его, люди сломают его .:)

Ответы [ 5 ]

22 голосов
/ 30 марта 2010

Вот один сценарий с сокращениями:

  1. printf или cout помещают символы в буфер в адресном пространстве программы пользователя.
  2. В конце концов буфер заполняется, или, возможно, printf просит, чтобы буфер был очищен досрочно. В любом случае библиотека ввода-вывода вызывает операционную систему, которая копирует содержимое буфера в свое собственное пространство.
  3. Предполагая, что выходной файл привязан к терминалу, операционная система доставляет символы в приложение терминала.
  4. Терминальное приложение решает, что для каждого символа в буфере необходимо рисовать пиксели на экране.
  5. Терминальное приложение устанавливает инструкции по рисованию пикселей и просит оконный менеджер сделать это от его имени. (В наши дни в Unix это обычно X-сервер.)
  6. Диспетчер окон принимает пиксели. Если окно действительно видно на экране, диспетчер окон затем обновляет буфер (называемый frame buffer ), который содержит видимые пиксели. Затем менеджер окон может уведомить операционную систему, или, более вероятно, менеджер окон находится в соприкосновении с операционной системой, и они совместно используют одну и ту же память.
  7. В следующий раз, когда экран обновляется, аппаратные средства видят новые биты в буфере кадров и по-разному рисуют экран.
  8. Voil & agrave ;! У вас есть символы на экране.

Удивительно, что медведь танцует вообще.

18 голосов
/ 30 марта 2010

Так в основном, как создаются эти функции? .. это сборка ?, если так, то где это начинается ?. Это вызывает больше вопросов, например, как они сделали функции openGl / directx.

Эти функции могут быть ассемблерными или C, они не сильно меняются (и, в любом случае, вы можете делать в C практически все, что можете делать в ассемблере). Волшебство в конечном итоге происходит в интерфейсе программного и аппаратного обеспечения. - то, как вы получаете от printf и cout <<, может быть таким же тривиальным, как несколько операций с указателями (см. Пример 286 ниже, или прочитать о cprintf ниже), или таким сложным, как прохождение через несколько Слои разнообразных системных вызовов, возможно, даже проходящих по сети, прежде чем в итоге ударить по вашему аппаратному обеспечению дисплея.

Представьте себе следующие сценарии :

  1. Я выкапываю свои старые 286 из-под пыли и запускаю MS-DOS; Я компилирую и запускаю следующую программу в режиме реального времени :

    void main(void) {
      far long* pTextBuf = (far long*)0xb8000L;
      /* Poor man's gotoxy+cprintf imitation -- display "C:" (0x43,0x3a) in
         silver-on-black letters in the top-left corner of the screen */
      *pTextBuf = 0x073a0743L;
    }
    
  2. Я подключаюсь через Windows HyperTerminal моего ноутбука к моему последовательному порту, который подключен кабелем к задней панели блока SUN, через который я могу получить доступ к консоли моего блока SUN. С этой консоли я запускаю ssh в другой сетевой блок, где я запускаю свою программу, которая выполняет printf, пропуская вывод через more. Информация printf прошла по каналу через more, затем через псевдотерминал SSH через сеть к моему блоку SUN, оттуда через последовательный кабель на мой ноутбук, через функции рисования текста GDI в Windows, прежде чем наконец появиться на моем экране.

Добавление более подробной информации к Нормандский ответ , надеюсь, больше в направлении вашего исходного вопроса:

  • printf и cout << обычно выполняют записи в stdout - обычно буферизированные записи, но это не всегда имело место
    • В свое время различные поставщики компиляторов (Borland, Microsoft), особенно для DOS, предоставили вам такие функции, как cprintf, которые записывали непосредственно в видеопамять , не делая любые системные вызовы, стиль memcpy (см. мой пример 286 выше) - подробнее об этом ниже
  • запись в stdout - системный вызов, будь то write в * nix, WriteFile или WriteConsole в Windows, INT 21, 9 под DOS и т. Д.
  • преимущество прохождения через stdout абстракцию заключается в том, что она позволяет операционной системе выполнить некоторые внутренние операции и выполнить перенаправление (будь то дескриптор tty, канал, файл), на последовательный порт, на другую машину через сокет и т. д.)
    • это также косвенно позволяет сосуществовать нескольким приложениям stdout на одном экране, например в разных окнах - что было бы гораздо сложнее сделать, если бы каждое приложение пыталось записывать данные непосредственно в видеопамять самостоятельно (как cprintf делал в DOS - не , что сегодня можно назвать истинным или многозадачная операционная система.)
  • в настоящее время графическое приложение , такое как ваше rxvt консольное окно приложения, клиент PuTTY telnet / ssh, консоль Windows и т. Д. Будет:
    • прочитайте ваше приложение stdout:
      • из дескриптора tty (или эквивалентного) в случае rxvt или консоли Windows
      • из последовательного порта, если вы используете что-то вроде Realterm для подключения к встроенной системе или к более старой консоли SUN box
      • из сокета, если вы используете PuTTY в качестве клиента telnet
    • отображает информацию путем визуализации ее графически, пиксель за пикселем , в графический буфер окна приложения / контекст устройства / и т.д.
      • это обычно делается через еще один уровень абстракции и системные вызовы (такие как GDI, OpenGL и т. Д.)
      • информация о пикселях в конечном итоге заканчивается в линейном кадровом буфере , то есть выделенном диапазоне памяти (еще во времена 8 МГц процессоров, задолго до AGP, эта область могла находиться в системной памяти, в настоящее время) это могут быть мегабайты и мегабайты двухпортового ОЗУ на самой видеокарте)
      • видеокарта (то, что раньше называлось RAMDAC ), периодически считывала бы диапазон кадровой памяти (например, 60 раз в секунду, когда ваш VGA-адаптер был установлен на 60 Гц ), отсканируйте после развертки (возможно, выполняя поиск palette ) и передайте его на дисплей в виде аналоговых или цифровых электрических сигналов
  • когда-то или даже сегодня, когда вы загружаете * nix box в однопользовательском режиме или работаете в полноэкранном режиме в консоли Windows, ваш графический адаптер фактически находится в текстовом режиме
    • вместо линейного кадрового буфера один (будь то реализация cprintf или ОС) записывает в гораздо меньший размер 80x25 или 80x50 и т. Д. текстовый буфер * массив 1138 *, где (например, в случае VGA) только два байта необходимы для кодирования каждого символьного значения, такого как A или или (1 байт), а также его цветовых атрибутов (1 байт) - то есть его передний план (4 бита или 3 бита + бит яркости) и цвета фона (4 бита или 3 бита + бит моргания)
    • для каждого пикселя на каждой строке развертки, RAMDAC:
      • будет отслеживать, к какому столбцу текста и какой строке текста принадлежит этот пиксель
      • будет искать значение и атрибуты символов этой позиции столбца / строки
      • будет выглядеть значение символа в сравнении с простым определением растрового шрифта
      • увидит, должен ли отображаемый пиксель в определении растрового изображения глифа значения символа быть установлен на передний план или фон, и какой цвет будет основан на атрибуте символа в этой позиции
      • возможно перевернуть передний план и фон на четные секунды, если был установлен бит моргания или курсор показывает и находится в текущей позиции
      • нарисуйте пиксель

Начните с страниц История видеокарт и GPU в Википедии, чтобы более детально взглянуть на то, как мы оказались там, где мы есть сегодня.

Также посмотрите на Как работают графические процессоры и Как работают графические карты .

1 голос
/ 30 марта 2010

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

Терминология Windows и Unix / Linux весьма различна, особенно концепцияоболочки не совсем то же самое в каждом.Но использование вызовов read () и write () в обоих случаях довольно схоже.

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

0 голосов
/ 30 марта 2010

Волшебство действительно происходит в драйвере устройства. ОС предоставляет интерфейс для программистов приложений. Это несколько массажируется (например, буферизируется), а затем отправляется на устройство. Затем устройство принимает общее представление и преобразует его в сигналы, которые может понять конкретное устройство. Таким образом, ASCII отображается в приемлемом формате на консоли, или в файле PDF, или на принтере, или на диске, в форме, подходящей для этого устройства. Попробуйте что-то кроме ASCII (или UTF8), которое драйвер не понимает, и вы поймете, о чем я говорю.

Для вещей, которые ОС не может обработать (например, специальные графические карты), приложение записывает данные непосредственно в память устройства. Вот как работает что-то вроде DirectX (для упрощения).

Каждый драйвер устройства отличается. Но каждый из них одинаков с точки зрения того, как они взаимодействуют с ОС, по крайней мере для каждого класса устройств (диск, сетевая карта, клавиатура и т. Д.).

0 голосов
/ 30 марта 2010

Взломайте исходный код для glibc и убедитесь сами.

Краткий ответ, много кода на C, время от времени посыпанного каким-то ассемблером.

...