Системное программирование
Лекция 1:
Ядро. Устройство. Планировщики процессов.
Version: 2
The presentation is outdated and not maintained anymore. Please, refer to the English version for the most actual slides.
Ядро
Драйверы
Терминал
Загрузчик
Это операционная система
Плеер
Браузер
Компилятор
Почта
Это НЕ операционная система
Системные вызовы
read
write
open
close
fork
ОС - диспетчер физических ресурсов
Kernel space
User space
Пользователь
Кнопки
Электросигнал
Прерывание
Ядро
Контроллер устройства инициировал сигнал
Нажали кнопку
Процессор получил сигнал и прервал выполнение задачи
Вызван обработчик в ядре
Обработчик знает номер прерывания, планирует дальнейшую обработку
Прерывания
Ядро
Обработка
Кто запустил ядро?
Загрузчик
Кто запустил загрузчик?
Но откуда тогда BIOS?
Боже, ну а ROM то что такое?
Почему BIOS сам не запускает ядро?
Он не знает, что такое файловая система, и не может найти ядро
2 балла
Лампа на слайде означает вопрос за баллы
Процессы
Аппаратура
Время
Файловая система
IPC
Сеть
Пользователи
Структуры данных
Виртуализация
/**
* 30.09.2018
* #include/linux/sched.h
* 618 lines.
*/
struct task_struct {
struct thread_info thread_info;
long state;
void *stack;
atomic_t usage;
unsigned int cpu;
int prio;
struct mm_struct *mm;
int exit_state;
int exit_code;
int exit_signal;
pid_t pid;
struct task_struct *parent;
struct list_head children;
u64 start_time;
const struct cred *cred;
struct files_struct *files;
struct thread_struct thread;
};
long state;
atomic_t usage;
unsigned int cpu;
int prio;
struct mm_struct *mm;
int exit_state;
int exit_code;
int exit_signal;
pid_t pid;
struct task_struct *parent;
struct list_head children;
const struct cred *cred;
struct files_struct *files;
pid_t
getpid(void);
pid_t
getppid(void);
vladislav$> cat /proc/sys/kernel/pid_max
32768
fork()
Ready to run
Running
schedule()
Terminated
do_exit()
preemtion
Waiting an event
Wait an event
The event or signal
Периодические и разовые задачи
Аппаратный источник времени
Осциллятор
Осциллятор внутри
Обработанный кристалл
Дикий кристалл
Осциллятор внутри
Обработанный кристалл
HZ = #
1с
vladislav$> grep 'CONFIG_HZ=' /boot/config-$(uname -r)
CONFIG_HZ=250
Слишком высокий HZ
Real Time Clock
Detected 2400.131 MHz processor.
Calibrating delay loop... 4799.56 BogoMIPS
Pulse Per Second
Источник сигнала
Атомные часы
Точность - пикасекунды
/**
* 30.09.2018
* 33 virtual functions, 149 lines.
*/
struct file_operations {
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*flock) (struct file *, int, struct file_lock *);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
};
struct super_block {
struct file_system_type *s_type;
const struct super_operations *s_op;
int s_count;
struct list_head s_mounts;
};
read(fd)
syscall
struct inode
inode->read()
ext4_read()
Мьютексы, семафоры
Очереди сообщений
Разделяемая память
Pipe
Доменные сокеты
Приложений
Транспортный
Сетевой
Канальный
Уровень:
Модель IP + TCP/UDP
/**
* 30.09.2018.
* 39 lines.
*/
struct cred {
kuid_t uid; /* real UID of the task */
kgid_t gid; /* real GID of the task */
kuid_t suid; /* saved UID of the task */
kgid_t sgid; /* saved GID of the task */
kuid_t euid; /* effective UID of the task */
kgid_t egid; /* effective GID of the task */
struct user_struct *user; /* real user ID subscription */
};
struct list_head {
struct list_head *next, *prev;
};
Список
struct my_struct {
struct list_head base;
int a;
int b;
const char *c;
};
struct my_struct ms1, ms2;
INIT_LIST_HEAD(&ms1);
list_add(&ms1, &ms2);
struct rb_node {
unsigned long __rb_parent_color;
struct rb_node *rb_right;
struct rb_node *rb_left;
};
struct rb_root {
struct rb_node *rb_node;
};
Красно-черное дерево
struct rb_root tree = RB_ROOT;
struct my_struct {
struct rb_node node;
int a;
int b;
const char *c;
};
struct my_struct ms1, ms2;
rb_insert_color(&tree, &ms1);
rb_insert_color(&tree, &ms2);
struct my_struct {
struct my_struct *prev;
struct my_struct *next;
int a;
int b;
const char *c;
};
Кооперативная многозадачность -
voluntary yield
Вытесняющая многозадачность -
mandatory preemtion
dumb scheduler
O(1) scheduler
Completely Fair Scheduler
I/O Bound
CPU Bound
Time
Диск
Интерактивные приложения
Сеть
Обработка транзакций
Вычисления
Какие есть два типа многозадачности?
Кооперативная и вытесняющая
2 балла
nice -- execute a utility with an altered scheduling priority
renice -- alter priority of running processes
chrt -- manipulate the real-time attributes of a process
int
getpriority(int which, id_t who);
int
setpriority(int which, id_t who, int prio);
int
nice(int inc);
vladislav$> ps -el
UID PID PRI NI TTY CMD
0 1 37 0 ?? /sbin/launchd
Real-time приоритет
Nice
Timeslice / quantum
запуск
прерывание
Пример
Конвертация видео
Шахматы с ИИ
Быстрее закончится
Низкая интерактивность
Приоритет
Приоритет
Потери кешей, позже кончится
Быстрая реакция
Квант времени в Linux - относительная величина
10 мс
10 мс
50 %
50 %
Процесс может не использовать квант целиком
Complete Fair Scheduler
N - число процессов,
идеальное деление процессора - 1/N -> 0, бесконечно частое переключение
Идеальный планировщик
/**
* 30.09.2018.
* 36 lines.
*/
struct sched_entity {
struct load_weight load;
unsigned long runnable_weight;
struct rb_node run_node;
struct list_head group_node;
u64 exec_start;
u64 sum_exec_runtime;
u64 vruntime;
u64 nr_migrations;
struct sched_entity *parent;
};
struct load_weight load;
unsigned long runnable_weight;
u64 exec_start;
u64 sum_exec_runtime;
u64 vruntime;
/*
* Pick the next process, keeping these things in mind, in this order:
* 1) keep things fair between processes/task groups
* 2) pick the "next" process, since someone really wants that to run
* 3) pick the "last" process, for cache locality
* 4) do not run the "skip" process, if something else is available
*/
static struct sched_entity *
pick_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *curr)
{
struct sched_entity *left = __pick_first_entity(cfs_rq);
/* .... 47 lines of code. */
}
struct rb_node run_node;
Сортировка слиянием через С корутины
На диске лежат файлы, в них числа в строковом виде в произвольном порядке. Нужно отсортировать каждый файл и затем слить их в один большой. То есть выполнить сортировку слиянием.
Реализовать С корутины, планировщик и использовать их. Подробнее по ссылке:
Цена: 15 - 25 баллов.
Срок: 2 недели.
Положить на ваш гитхаб и сказать мне его. Сдавать как угодно - лично/удаленно.
Есть два варианта реализации корутин. Первый - через setjump/longjump, второй - через swapcontext. Longjump - это функция, которая позволяет делать goto в произвольное место стэка.
Стэк
... my_func() ...
... machine code ...
... return
coroutine1
coroutine2
coroutine3
coroutine4
coroutine5
Исполнение:
coroutine1, coroutine2, coroutine3, coroutine4, coroutine5, coroutine1,
...
Swapcontext - это функция, которая позволяет внутри одного потока одного процесса переключаться между разными стеками.
Стэк
... my_func() ...
... machine code ...
... return
coroutine1
Исполнение:
coroutine1, coroutine2, coroutine3, coroutine1, coroutine2,
...
Стэк
... my_func() ...
... machine code ...
... return
coroutine2
Стэк
... my_func() ...
... machine code ...
... return
coroutine3
Обратная связь: goo.gl/forms/TAeraYrqJcil7GDt1
и на портале Техносферы
В следующий раз:
Процесс. Режимы работы, память, ресурсы. Прерывания. Взаимодействие с ядром.
Системные вызовы.