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

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

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

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

Насколько я прав? Благодарю.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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