Лаборатория Tarantool

Системное программирование

Лекция 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

Прерывания [1]

Пользователь

Кнопки

Электросигнал

Прерывание

Ядро

Контроллер устройства инициировал сигнал

Нажали кнопку

Процессор получил сигнал и прервал выполнение задачи

Вызван обработчик в ядре

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

Прерывания [2]

Прерывания

Ядро

Обработка

Загрузчик [1]

Кто запустил ядро?

Загрузчик

Кто запустил загрузчик?

Но откуда тогда BIOS?

Боже, ну а ROM то что такое?

Загрузчик [2]

Почему BIOS сам не запускает ядро?

Он не знает, что такое файловая система, и не может найти ядро

Загрузчик [3]

2 балла

Лампа на слайде означает вопрос за баллы

Ядро Linux

Процессы

Аппаратура

Время

Файловая система

IPC

Сеть

Пользователи

Структуры данных

Виртуализация

Ядро Linux. Процессы [1]

/**
 * 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

Ядро Linux. Процессы [2]

fork()

Ready to run

Running

schedule()

Terminated

do_exit()

preemtion

Waiting an event

Wait an event

The event or signal

Ядро Linux. Процессы [3]

Ядро Linux. Аппаратура

Ядро Linux. Время [1]

Периодические и разовые задачи

Аппаратный источник времени

Осциллятор

Осциллятор внутри

Обработанный кристалл

Дикий кристалл

Осциллятор внутри

Обработанный кристалл

HZ = #

vladislav$> grep 'CONFIG_HZ=' /boot/config-$(uname -r)
CONFIG_HZ=250

Слишком высокий HZ

Ядро Linux. Время [2]

Real Time Clock

Detected 2400.131 MHz processor.
Calibrating delay loop... 4799.56 BogoMIPS

Ядро Linux. Время [3]

Pulse Per Second

Источник сигнала

Атомные часы

Точность - пикасекунды

Ядро Linux. ФС

/**
 * 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()

Ядро Linux. IPC

Мьютексы, семафоры

Очереди сообщений

Разделяемая память

Pipe

Доменные сокеты

Ядро Linux. Сеть

Приложений

Транспортный

Сетевой

Канальный

Уровень:

Модель IP + TCP/UDP

Ядро Linux. Пользователи

/**
 * 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 */
};

Ядро Linux. Структуры данных

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;
};

Ядро Linux. Планировщик [1]

Кооперативная многозадачность -

voluntary yield

Вытесняющая многозадачность -

mandatory preemtion

Ядро Linux. Планировщик [2]

dumb scheduler

O(1) scheduler

Completely Fair Scheduler

I/O Bound

CPU Bound

Time

Диск

Интерактивные приложения

Сеть

Обработка транзакций

Вычисления

Какие есть два типа многозадачности?

Кооперативная и вытесняющая

Планировщик [3]

2 балла

Ядро Linux. Планировщик [4]

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. Планировщик [5]

Пример

Конвертация видео

Шахматы с ИИ

Быстрее закончится

Низкая интерактивность

Приоритет

Приоритет

Потери кешей, позже кончится

Быстрая реакция

Ядро Linux. Планировщик [6]

Квант времени в Linux - относительная величина

10 мс

10 мс

50 %

50 %

Процесс может не использовать квант целиком

Ядро Linux. Планировщик [7]

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;
W_i = \frac{1024}{1.25^{nice}}; P_j = \frac{W_j}{\sum\limits_{i=1}^n W_i}\\ Пример\colon\\ nice1 = 1, nice2 = 0\\ weight1 = 820, weight2 = 1024\\ P_1 = 55%, P2 = 45%
/*
 * 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;

Ядро Linux. Планировщик [8]

Практикум [1]

Сортировка слиянием через С корутины

На диске лежат файлы, в них числа в строковом виде в произвольном порядке. Нужно отсортировать каждый файл и затем слить их в один большой. То есть выполнить сортировку слиянием.

 

Реализовать С корутины, планировщик и использовать их. Подробнее по ссылке:

Цена: 15 - 25 баллов.

Срок: 2 недели.

Положить на ваш гитхаб и сказать мне его. Сдавать как угодно - лично/удаленно.

Практикум [2]. Setjump/longjump

Есть два варианта реализации корутин. Первый - через setjump/longjump, второй - через swapcontext. Longjump - это функция, которая позволяет делать goto в произвольное место стэка.

Стэк

...
my_func()
...

...
machine code
...
...
return

coroutine1

coroutine2

coroutine3

coroutine4

coroutine5

Исполнение:

coroutine1, coroutine2, coroutine3, coroutine4, coroutine5, coroutine1,
...

Практикум [3]. Swapcontext

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

и на портале Техносферы

В следующий раз:

Процесс. Режимы работы, память, ресурсы. Прерывания. Взаимодействие с ядром.
Системные вызовы.