Как (не) работят компютрите
2.12.2014
Операционни системи
- Парчета код, които управляват хардуера
- Позволяват изпълнението на потребителски програми
- Има ги разни (Windows/Linux/OS X)
Kernel
- Ядрото (duh) на операционната система
- Има пряк достъп до всичко в компютъра ви
- Грижи се за потребителските програми
Драйвъри
- Парчета код в ядрото, които управляват конкретен хардуер
- Могат да се слагат отделно от ОС-а
Userland
- "GNU" частта от "GNU/Linux"
- Полезните програмки, без които една ОС е безполезна
- Текстови редактори, web browser-и, терминали, графични среди, Angry Birds, etc
User space / Kernel space
- Userland частта върви в първото
- Kernel-ът върви във второто
- Това е грубо казано, демек е омешано, но така е в общия случай
- Няма общо с root (Linux)/Administrator (Windows)
- Защо е това разделение?
- Сигурност: между отделните процеси, както и между тях и ядрото
... а какво всъщност са процеси?
- Това са потребителските програми
- Всеки е разделен от всички останали
- Всеки има свое виждане за паметта
- Това става посредством виртуална памет
... а какво всъщност НЕ са процесите?
- Не са това, което се изпълнява на процесора
- Това всъщност са thread-овете
- Всеки процес съдържа един или повече thread-а
Threads
- Всеки си има отделен стек
- Споделят общ heap в процеса, в който се намират
Изпълнение на код
- Компютрите "говорят" на машинен език
- Ние програмираме на човешко-четим език
- Трябва някой по някакъв начин трябва да го обърне до машинен, за да може да бъде изпълнен от компютъра
- Има (грубо-казано) 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
Как получаваме изпълними файлове?
- Чрез компилация и линкване
- Компилацията превежда отделен файл с програмен код към машинни инструкции
- Линкването "свързва" отделни компилирани единици
- Какво правим с външни зависимости, които не са наш код?
- Като например printf от стандартната С библиотека?
Static linking
- Когато кодът на нужните библиотеки е в изпълнимия файл
- В Go става точно така, когато компилираме наше приложение, което import-ва "fmt"
Dynamic linking
- В изпълнимия файл има единствено имена на библиотеки и функции
- Пример: libc.so:printf или msvcrt.dll:printf
- ОС-а се грижи да "зареди" библиотеката и да навърже адресите
static VS dynamic linking
- По-големи изпълними файлове при статично линкване
- Малко по-бързо изпъленение при статично линкване
- Няма споделяне на код в обща библиотека при статично линкване