Ускоренный курс компьютерного оборудования

Краткий курс компьютерного оборудования

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

Что такое языковая виртуальная машина?

Вы знаете, как набирать * python script.py * и происходит волшебство? Это виртуальная машина Python или интерпретатор языка, которая читает написанный вами исходный код, переводит его в байт-код, который может понять виртуальная машина Python, а затем выполняет его.

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

Убедитесь, что у вас установлен компилятор C! GCC или clang - хороший выбор. Часть кода намеренно не оптимизирована, чтобы мы могли вернуться позже и узнать о сравнительном анализе и оптимизации виртуальных машин.

Как и Frieza, программа имеет несколько форм. Когда вы начинаете кодировать один, вы пишете текст, который выглядит так:

#include <stdio.h> 
int main (void) { 
    printf("Hello World!"); 
    return 0; 
}

Ваш процессор не знает, что с этим делать. Мы должны преобразовать этот текст в то, что ЦП сможет понять и на что будет действовать: в двоичный код. Этот процесс (или последовательность процессов) часто называется компиляцией и требует дополнительных шагов.

У всех процессоров есть собственный язык, который они понимают. Это часто называют ассемблерным кодом, и он сильно зависит от процессора. Сборка, которую может понять ваш iPhone или Galaxy C4 Boom Edition, непонятна для более дешевого процессора AMD, который вы купили на NewEgg, а не для Intel, и вы совершенно не сожалеете об этом решении.

Вы можете писать ассемблерный код напрямую, хотя в наше время это редкость. Это утомительно и утомительно, как в сериале «Друзья». Ваш друг компилятор может взять ваш исходный код и выплюнуть вам ассемблерный код. Давайте возьмем наш предыдущий пример кода C и поместим его в файл с именем 01_c_hello_world.c:

#include <stdio.h> 
int main (void) { 
    printf("Hello World!"); 
    return 0; 
}

Сохраните это где-нибудь на вашем диске. Теперь из терминала запустите:

$ gcc -S 01_c_hello_world.c

Рядом с файлом .c должен быть файл с тем же именем, но с расширением .s. Посмотрим, что внутри ...

$ cat /path/to/01_c_hello_world.s

Вы должны увидеть следующую версию:

.section __TEXT,__text,regular,pure_instructions
                   .macosx_version_min 10, 13
                   .globl _main                   ## -- Begin function main
                   .p2align 4, 0x90
                   _main:                                  ## @main
                   .cfi_startproc
                   ## BB#0:
                   pushq %rbp
                   Lcfi0:
                   .cfi_def_cfa_offset 16
                   Lcfi1:
                   .cfi_offset %rbp, -16
                   movq %rsp, %rbp
                   Lcfi2:
                   .cfi_def_cfa_register %rbp
                   subq $16, %rsp
                   leaq L_.str(%rip), %rdi
                   movl $0, -4(%rbp)
                   movb $0, %al
                   callq _printf
                   xorl %ecx, %ecx
                   movl %eax, -8(%rbp)          ## 4-byte Spill
                   movl %ecx, %eax
                   addq $16, %rsp
                   popq %rbp
                   retq
                   .cfi_endproc
                                                         ## -- End function
                   .section __TEXT,__cstring,cstring_literals
                   L_.str:                                 ## @.str
                   .asciz "Hello World!"

Не паникуйте! Вам не нужно знать, что все это значит, и мы не будем это писать. Чтобы показать, как выглядит сборка.

Когда у нас есть код сборки, появляется другая программа (часто встроенная в компилятор), которая принимает сборку и преобразует ее в нули и единицы, которые может понять наш ЦП.

Чтобы увидеть ассемблер в действии, вы можете запустить:

$ gcc -c 01_c_hello_world.s -o 01_c_hello_world.o

Теперь вы должны увидеть третий файл с именем 01_c_hello_world.o. Каталог должен выглядеть так:

$ ls 
01_c_hello_world.c	
01_c_hello_world.o	
01_c_hello_world.s

То, что содержит файл .o, близко к фактическим 0 и 1, которые может выполнять ЦП. Это будет зависеть от платформы, но будет выполняться быстро.

Одним из преимуществ, которое использовалось для продвижения Java на рынок еще в то время, когда она впервые появилась на рынке, было обещание «напиши один раз, запусти где угодно». То есть написанный вами код Java может работать без изменений на любой аппаратной платформе, которая может запускать JVM. Это означало, что людям нужно было заботиться об одной программе, JVM, работающей на их оборудовании, а Sun Microsystems (позже Oracle) позаботилась об этой части.

Другие языки следуют этой модели: .NET CLR, Python, Ruby, Perl и другие.

Вы знали, что первыми программистами были женщины? Аппаратный аспект ранних компьютеров рассматривался как мужская часть компьютеров: поворот циферблата, возня со схемами и т. Д.

Написание кода считалось большей секретарской работой. Без них наш мир не существовал бы так, как сегодня. Я настоятельно рекомендую прочитать о следующих людях: Аде Лавлейс, Грейс Хоппер и Кэтрин Джонсон.

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

Note:
Yes, there are a lot of other topics to get into here, such as JIT compilers, native code extensions, and all the rest. I’m going to skip those for now.

Последнее, о чем я расскажу в этом посте, - это концепция регистров. В ЦП регистр - это специальная область для хранения данных. Для более подробного объяснения я украду из Википедии:

В компьютерной архитектуре регистр процессора - это быстро доступное место, доступное центральному процессору (ЦП) компьютера. Регистры обычно состоят из небольшого объема быстрой памяти, хотя некоторые регистры имеют определенные аппаратные функции и могут быть доступны только для чтения или только для записи. К регистрам обычно обращаются с помощью механизмов, отличных от основной памяти, но в некоторых случаях им может быть назначен адрес памяти, например DEC PDP-10, ICT 1900.

- Википедия
https://en.wikipedia.org/wiki/Processor_register

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

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

Первоначально опубликовано на blog.subnetzero.io.