Что стоит за printf в C?

Возможный дубликат:
Понимание аппаратного обеспечения printf

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

Вот что я думаю PrintfCAll → KernelModeOn → SystemCallMade → Data Put On Output Buffer of Some Sort → Output, который должен быть сброшен в некоторый буфер контроллера → Контроллер Dumps It On The Monitor → Прерывает процессор, говорящий, что работа выполнена.

Насколько я прав? Спасибо.

Изменить: Unix Может использоваться как платформа. скажем, ubuntu. И может кто-то сказать, мне, откуда поступают данные, и есть ли какой-нибудь контроллер для монитора тоже? и в какой степени приведенная выше временная шкала правильна?

Ответ 1

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

Вызов printf начинается с обычного вызова подпрограммы; режим ядра не задействован. В большинстве случаев printf является обычным кодом и может быть записано в C. Основная часть самого кода printf связана с интерпретацией строки формата, преобразованием аргументов в строки, которые должны быть записаны, и записью этих строк на выходе файл. Большая часть этой работы будет выполняться с помощью подпрограмм, вызывающих вызовы printf, таких как подпрограммы для преобразования чисел (объектов типа int или float) в числа (строки символов, которые представляют числа).

printf также, вероятно, вызывает malloc или связанную процедуру, чтобы получить память для буфера, где он подготавливает строки. Я не буду описывать вызов malloc в этом ответе.

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

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

Обычно потоки буферизуются. Итак, когда printf вызывает fwrite, fwrite проверяет, насколько полна его буфер. Если новая строка из printf помещается в буфер, fwrite просто добавляет строку в буфер и возвращает. Если буфер заполнен, то fwrite вызывает другую процедуру, чтобы фактически записать содержимое буфера в файл. (Как правило, это включает в себя заполнение буфера частью входящей строки, запись буфера в файл [и выделение буфера пустым], а затем копирование остальной входящей строки в новый пустой буфер). Некоторые другие вещи также могут триггер, записывающий буфер, например обнаружение символа новой строки во входящей строке, в зависимости от обстоятельств.

Предположим, что для записи буфера fwrite вызывает системную процедуру write. Лицом write является библиотечная процедура; fwrite выполняет обычный вызов подпрограммы для вызова write. Системные процедуры будут иметь некоторую часть, которая является обычной подпрограммой, но когда им нужно выполнить работу с nitty-gritty, есть какая-то инструкция по системному вызову (иногда называемая ловушкой).

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

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

Одним из сценариев является то, что обработчик системного вызова (программное обеспечение, вызываемое при возникновении системного вызова) считывает сохраненные регистры и память пользовательского процесса, чтобы определить, что требует процесс. В каждой системе задан некоторый метод передачи параметров системному вызову. Например, определенный регистр может содержать число, которое указывает, что такое запрос (0 означает запись, 1 означает чтение, 2 означает получение текущего времени, 3 означает карту памяти изменения и т.д.), И каждый запрос будет иметь определенные параметры, переданные в других регистров или в памяти (один регистр может содержать адрес в памяти, а другой - длину записи).

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

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

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

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

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

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

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

Ответ 2

C printf вызывает запись в стандартный выходной буфер программы. Если консоль/терминал подключен, консоль/терминал считывает эти данные и отображает их через видеодрайвер.

Ответ 3

Функция printf не является частью языка C, поскольку на языке C нет ввода или вывода. Функция printf является полезной функцией из стандартной библиотеки функций, доступной для программ C. Поведение printf определено в стандарте ANSI. Если компилятор, который вы используете, соответствует этому стандарту, тогда все функции и свойства должны быть доступны вам.