Жизненный цикл процессов

- fork() - создание процесса
- Готов к выполнению (чем дольше процесс в очереди, тем выше приоритет его выполнения)
- Выполнение в режиме ядра/пользователя (чем дольше процесс находится в состоянии выполнения, тем ниже его приоритет)
_exit()wait()- только после вызова wait процесс переходит из состояния зомби в завершённый.
Начальная нагрузка (запуск компьютера)
Начальная загрузка - загрузка ядра системы в оперативную память, запуск ядра.
- Подаётся питание на схемы компьютера
- Запуск аппаратного загрузчика. Загрузчик в готовых к работе устройствах выбирает наиболее приоритетное.
- В BIOS хранится информация об устройствах.
- Внешнее запоминающее устройство - устройство, на котором может находиться система. Эти устройства ранжированы в приоритетном списке.
- Чтение нулевого блока системного устройства аппаратным загрузчиком.
- Запуск программного загрузчика конкретной ОС. (например, grub)
- Этот загрузчик “знает” структуру системного диска
- В общем случае, знает устройство файловой системы
- Программный загрузчик ищет в корне файловой системы файл ядра
/unix()
- Запуск
/unix- Инициализация компонентов компьютера (установка часов, инициализация контроллера памяти и т.п.)
- Инициализация системных структур данных (берётся из системного файла конфигурации с параметрами)
- Инициализация нулевого процесса (ядра):
- Не имеет кодового сегмента
- Существует в течение всего времени работы системы
Инициализация системы
- Создание ядром первого процесса
- Копируется процесс
0(запись таблицы процессов)
- Копируется процесс
- Создание области кода процесса
1- Копирование в область кода процесса
1программы, реализующей системный вызовexec(), который необходим для выполнения/etc/init
- Копирование в область кода процесса
- Замена команды процесса
1кодом из файла/etc/init(exec()) - Подключение интерпретатора команд к системной консоли
- Создание многопользовательской среды (чтение
/etc/passwd)
getty- команда, устанавливающая терминальные соединения и порты и управляющая ими.
- Схема дальнейшей работы

Легковесные процессы (threads/нити) Unix
- Реализация аппарата управления нитями - использование библиотеки функций в рамках полновесного процесса.
- Нити, созданные в разных полновесных процессах не пересекаются.
- Нити, созданные” внутри” процесса разделяют глобальные переменные и системные ресурсы, выделенные процессу. стек и регистры (включая счётчик команд) локализованы в рамках каждой нити.
- Идентификация нити TID (Thread ID) - значение предопределённого типа pthread_t. в различных системах TID может представляться по разному:
- Long int/unsigned int/pthread
Pthread_create() - Создание нити
#include <pthread.h>
int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void* (*start)(void*), void *arg)tid- указатель на область памяти, в которой будет размещён идентификатор созданной нити.attr- используется для задания свойств (атрибутов) нити.start- имя функции (указатель на функцию) фиксированного формата, реализующей нить. Параметр типаvoid*и возвращаемое значение типаvoid*.arg- аргумент для функции, указанной вstart
Аппарат
errnoздесь не используется.0- ошибка,0- успех.
Операции с идентификатором нити tid
Сравнение идентификаторов нитей
int pthread_equal(pthread_t tid1, pthread_t tid2)- returns
0iftid1tid2
Получение tid
pthread_t pthread_self(void)- Возвращает tid вызвавшей нити
Pthread_exit() - Завершение нити
Завершение всего процесса
- В случае выполнения из одной из нитей
_exit()(или библиотечнойexit())
Завершение текущей нити
void pthread_exit(void *val_ptr)val_ptr- параметр, через который можно передать другим нитям, ожидающим завершения данной, информацию о завершении. (можно передать структуру и т.п., поэтому используется указатель).- Процесс завершается по завершении последней нити.
Завершение заданной нити
int pthread_cancel(pthread_t tid)tid- thread id принудительно завершаемой нити.- Возвращает
0в случае успеха.
Ожидание завершения нити
int pthread_join(pthread_t tid, void **val_ptr)tid- идентификатор ожидаемой нитиval_ptr- указатель на переменную, в которой будет размещён код ответа (изpthread_exit(), например)- Возвращает
0в случае успеха.
Блокирует вызвавшую нить до завершения указанной, аналогично wait() в процессах.
Только одна нить может дождаться завершения заданной нити.
Пример программирования нитей
Создаём несколько нитей, перемешиваем порядок их выполнения. Перед завершением каждой нити выводим глобальную и локальную переменные и смотрим разницу.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define NUM_THREADS 3
// общий счётчик
int shared_counter = 0;
void* th_fn(void *arg) {
int local = 0;
// перемешиваем работу нитей
sleep(rand() % 4);
// выводим переменные
printf("Thread #%d with ID %#x: shared value is %d, local value is %d\n", (int)arg, pthread_self(), shared_counter++, local++);
return NULL;
}
int main() {
pthread_t th_id;
int i, err;
// создаём нити
for (i = 0; i < NUM_THREADS; i++) {
err = pthread_create(&th_id, NULL, th_fn, (void*)i);
printf("Created thread with ID %#x\n", th_id);
}
sleep(10);
return 0;
}Второй пример программирования нитей
Создаём нить, засекаем время. Нить длится случайное время и завершается. Пишем время работы нити.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
void* th_fn(void *arg) {
sleep(time(0) % 10);
pthread_exit(NULL);
}
int main() {
pthread_t th_id;
void *pret;
pthread_create(&th_id, NULL, th_fn, NULL);
time_t tm_start = time(0);
pthread_join(th_id, &pret);
printf("Thread %#x execution time = %u\n", th_id, time(0) - tm_start);
return 0;
}