11. Как *не* работят компютрите
11. Как *не* работят компютрите
12 май 2014
Операционни системи
- Парчета код, които управляват хардуера
- Позволяват изпълнението на потребителски програми
- Има ги разни (Windows/Linux/OS X)
Kernel
- Ядрото (duh) на операционната система
- Има пряк достъп до всичко в компютъра ви
- Грижи се за потребителските програми
Драйвъри
- Парчета код в ядрото, които управляват конкретен хардуер
- Могат да се слагат отделно от ОС-а
Userland
- "GNU" частта от "GNU/Linux"
- Полезните програмки, без които една ОС е безполезна
- Текстови редактори, web browser-и, терминали, графични среди, etc
User space / Kernel space
- Userland частта вървят в първото
- Kernel-a върви във второто
- Това е грубо казано, демек е омешано, но така е в общия случай
- Няма общо с root (Linux)/Administrator (Windows)
- Защо е това разделение?
- Сигурност: между отделните процеси, както и между тях и ядрото
... а какво всъщност са процеси?
- Това са потребителските програми
- Всеки е разделен от всички останали
- Всеки има свое виждане за паметта
... а какво всъщност е виртуална памет?
- Паметта, до която имат достъп процесите
- Те НЯМАТ достъп до истинските адреси във физическата памет
- Всеки процес има собствена като те не се застъпват
- Всеки процес има право да достъпи пълното адресно пространство
- Виртуалната памет може да не е във физическата (swap file, memory mapped files, etc)
Как User space достъпва kernel space
- През така наречените System Calls
- Това са обикновени функции, които можем да викаме
- Намират се в стандартната С блиотека, която върви с всяка ОС
- Те вътрешно ще направят магията
Как изглежда един процес във виртуалната памет?
- Всеки процес, очевидно, има изпълним код
- Stacks, heaps, read-only segments
- Отделно има и mapped files, etc
Изпълнимите файлове на твърдия диск
- Съдържа кода на приложението
- Може да съдържа кода на външни библиотеки
- Или просто имената на тези външни зависимости
- Примерна зависимост е стандартната С библиотека
Как получаваме изпълними файлове?
- Чрез компилация и линкване
- Компилацията превежда отделен файл с програмен код към машинни инструкции
- Линкването "свързва" отделни компилирани единици
- Какво правим с външни зависимости, които не са наш код?
- Като например printf от стандартната С библиотека?
DLLs/so's
- В изпълнимия файл има единствено имена на библиотеки и функции
- Пример: libc.so:printf или msvcrt.dll:printf
- ОС-а се грижи да "зареди" библиотеката и да навърже адресите
Изпълнение на код
- Компютрите "говорят" на машинен език
- Ние програмираме на човешко-четим език
- Трябва някой по някакъв начин трябва да го обърне до машинен, за да може да бъде изпълнен от компютъра
- Има (грубо-казано) 2 начина за това
Compilation / Interpretation
- При първия, една програма "превежда" човешкия език на машинен преди изпълнението му
- Тя се нарича компилатор
- Включва процеса на linking, за който говорихме предния път
-
- При втория, една програма директно изпълнява човешкия език
- Тя се нарича интерпретатор
- Работи на принципа read-eval-print (REPL)
Плюсове и минуси
- Компилацията отнема време (C++, anyone?)
- Компилираният код се изпълнява по-бързо
- Времето за зареждане на интерпретиран код може да е голямо
- Интерпретируемите езици нямат нужда от build системи
- По-често (и лесно) се ползват за rapid prototyping
- Компилираният код остава същия през времето
- Това може да е проблем, когато хардуерът става по-добър
JIT
- Идеята е да компилираме човешкия език до междинен код
- Който после, по време на изпълнение, се компилира до машинен
- Това, което прави вторичната компилация, се нарича runtime
- Типичните примери за това са .NET/Java
Плюсове и минуси
- По-бавен startup time, тъй като се налага междинния код да се транслира
- Евентуално по-бързо изпълнение от чист native код по две причини:
- При дълго вървящи процеси, runtime-ът може да оптимизира hot paths
- Когато upgrade-нем хардуера и runtime-а, всички "стари" програми ще вървят по-бързо
- Доста повече метаинформация, идваща от runtime-a
- Благинки като garbage-collection, reflection
Какво съдържа един native DLL/so
- Практически няма разлика между DLL и EXE
- Съдържа изпълним код, import таблица, export таблица
- Последната съдържа само имената на функциите за C DLL-и
- За C++ такива съдържа в името си и имената на типовете на аргументите си
- Не съдържат, обаче, дефиниции на типове
- Как тогава можем да компилираме код, който използва функции от такива DLL-и?
В чисто C и C++
- С header файлове. Те съдържат дефинициите на типове, декларации на функции и т.н.
В Java/.NET
- Java използва JAR файлове, а в .NET DLL-ите не са (просто) native такива
- В тях има много метаинформация за всички типове, функции, etc
Скриптовите езици (Python)
- Те обикновено не се компилират, а интерпретират, което означава, че ship-ват код
- Тогава нямаме проблем с липса на типова информация
Memory management
- Всяка програма работи с променливи, които се намират някъде в оперативната памет
- Има няколко подхода за това къде може да се намират
- В read-only паметта, например хардкоднати стойности в кода ни
- На стека, ако това са локални променливи във функции, например
- В heap-a, ако са променливи, които ще ни трябват по-продължително
- За последните някой трябва да се грижи ръчно кога да бъдат освободени
Manual memory management
- Езици като C/C++
- Това е най-бързото решение като скорост на изпълнение
- Освобождаваме ресурси максимално бързо, когато не ни трябват
- Стават МНОГО грешки
- Причината за 90% от crash-овете на приложения
Garbage collection
- По-модерни езици като Java/.NET/Ruby/Python
- По-бавно по време на изпълнение
- По-късно освобождаване на ресурси
- Но пък е programmer-proof