1. Введение в ядро Linux
1. Введение в линукс
1.1 Особенности Unix
- unix очень лаконичен, предоставляет всего несколько сотен системных вызовов и имеет очень четкую цель разработки
- Все в unix рассматривается как файл, и эта абстракция позволяет получать доступ как к данным, так и к устройствам через один и тот же набор интерфейсов системных вызовов.
- Ядро написано на C, переносимость сильная
- Быстрое создание процессов, уникальные форк-вызовы
- Предоставляет краткие, но стабильные примитивы межпроцессного взаимодействия
1.2 юникс и линукс
- Linux клонирует Unix, но не Unix
- Linux Unix рисует много дизайна и реализации API Unix
- Linux не использует напрямую исходный код Unix, но полностью отражает цели разработки Unix и обеспечивает согласованные программные интерфейсы.
2. Введение в операционную систему и ядро
- Ядро обычно включает в себя:
- Процедура обслуживания прерываний: отвечает за реагирование на прерывания.
- Планировщик: управляет несколькими процессами, распределяет процессорное время
- Диспетчер памяти: управление объемом памяти
- Программа системы обслуживания: включая сеть, межпроцессное сообщение
- Приложения запускаются через системные вызовы и взаимодействие с ядром
- Приложения обычно вызывают библиотечные функции, а библиотечные функции позволяют ядру использовать их для выполнения различных задач через системные вызовы.
- Управление ядром аппаратных устройств: когда оборудование хочет установить связь, оно отправляет асинхронный сигнал для прерывания ядра, и ядро находит обработчик по номеру прерывания.
- специфическая для разработки ядра Linux
- Стандартная библиотека c не может быть связана. Библиотека c слишком велика и влияет на размер и эффективность. Однако большинство часто используемых функций C реализованы в ядре.
- Нет механизма защиты памяти, обратите внимание на несанкционированный доступ к адресам памяти
- Не используйте числа с плавающей запятой легко, чтобы вручную сохранять и восстанавливать регистры с плавающей запятой.
- Пространство стека небольшое и фиксированное. 32 это 8кб для машины, 64 это 16кб
- Ядро подвержено гонкам, обратите внимание на синхронизацию и параллелизм.
- Обратите внимание на портативность
2. Управление процессами
1. Основные понятия
- Два абстрактных объекта системы unix: процесс и файл. Для получения подробной информации см. остальные триСтатьи Последовательность процессов и файлов на Unix
- Процесс — это программа в фазе выполнения, и Linux обычно также называет процесс задачей.
- К процессам относятся: сегменты кода, сегменты данных, открытые файлы, ожидающие сигналы, адресные пространства, потоки и т. д.
- Поток — это активный объект выполнения в процессе.
- Каждый поток имеет независимый счетчик программ, стек процесса и набор регистров процесса.
- Объектом планирования ядра является поток, а не процесс
- Реализация потоков Linux очень особенная и не различает нити и процессы.
- Процессы предоставляют два виртуальных механизма: виртуальные процессоры и виртуальную память.
- Потоки внутри одного процесса могут совместно использовать виртуальную память, но иметь собственные виртуальные процессоры.
2. Дескриптор процесса и очередь задач
2.1 Основные понятия
- Ядро хранит процессы в двусвязных списках, называемых очередями задач.
- Каждый элемент в связанном списке представляет собой тип task_struct, называемый дескриптором процесса, включая всю информацию о процессе. Путь: /include/linux/sched.h
2.2 Как назначить дескрипторы процесса
- Linux выделяет свою структуру task_struct через slab, что позволяет добиться повторного использования объектов и окраски кеша.
- За счет предварительного выделения и повторного использования task_struct можно избежать потери производительности, вызванной динамическим выделением и освобождением, поэтому процесс создания выполняется быстро.
- Структура task_struct помещается в конец стека ядра, чтобы позволить аппаратной системе с небольшим количеством регистров вычислять позицию только через указатель стека, избегая использования дополнительных регистров для хранения.
- Распределитель slab создает новую структуру thread_info в конце стека ядра, а внутренняя задача указывает на фактическую задачу task_struct. Расположение thread_info:
2.3 Где хранятся дескрипторы процессов?
- Текущий макрос может найти дескриптор текущего запущенного процесса.
- Конкретная реализация этого макроса варьируется в зависимости от соответствующей архитектуры аппаратного обеспечения
- Система X86, найдите дескриптор процесса через указатель задания структуры Thream_info в конце стека
- В некоторых системах (IBM RISC) для хранения адреса task_struct выделяется специальный регистр.
2.4 Состояние процесса
- Поле состояния дескриптора процесса описывает текущее состояние процесса, и каждый процесс находится в одном из пяти состояний.
- TASK_RUNNING: Выполняется. Процессы исполняемые.
- TASK_INTERRUPTIBLE: прерываемая. Процесс заблокирован, ожидая пробуждения
- TASK_UNINTERRUPTIBLE: бесперебойная работа. При получении сигнала ответа не происходит. В представлении команды ps отобразится буква D.
- Task_zombie: зомби. Процесс закончился, но родительский процесс не назвал системный вызов Wait4
- TASK_STOPPED: остановить. Процесс перестает выполняться
- диаграмма перехода состояний
- Установите текущий процесс: set_task_state(задача, состояние) или set_current_state
2.5 Контекст процесса
- Как правило, программа выполняется в пространстве пользователя, когда выполняется системный вызов или срабатывает исключение, она входит в пространство ядра, а ядро выполняется от имени процесса и находится в контексте процесса.
- Интерфейс системного вызова к ядру и обработке исключений является четко определен, все доступ к ядру только через эти интерфейсы
- Процесс Linux имеет четкие отношения наследования, все процессы являются потомками процесса PID 1 INIT.
- Каждый процесс в системе должен иметь родительский процесс, и каждый процесс может иметь один или несколько дочерних процессов.
- Отношения между процессами хранятся в дескрипторах процессов. Родительская переменная в task_struct указывает на task_struct и хранит адрес родительского процесса. Переменная Children представляет собой связанный список, указывающий на все дочерние процессы.
3. Создание процесса
3.1 Основные понятия
- Создание процесса Unix делится на два этапа: fork и exec.
- fork создает дочерние процессы, копируя текущий процесс. Дочерний процесс и родительский процесс имеют лишь несколько отличий: pid, ppid, определенные ресурсы и статистика
- exec отвечает за чтение исполняемого файла и загрузку адресного пространства для запуска
3.2 Копирование при записи (COW)
- Традиционный форк прямого копирования ресурсов неэффективен, Linux использует технологию копирования при записи для повышения эффективности.
- COW не копирует все адресное пространство, а позволяет родительским и дочерним процессам совместно использовать память только для чтения, а данные копируются только при записи.
3.3 функция вилки
- by linux clone) системный вызов fork() (, этот вызов с идентификацией параметра (много типов) указывает на необходимость совместного использования ресурсов
- clone вызывает do_fork внутри для завершения основной работы (kernel/fork.c)
- do_fork вызывает copy_process внутренне, затем позволяет процессу работать
- процесс вызова copy_process:
- Вызовите dup_task_struct, чтобы создать стек ядра, структуру thread_info и task_struct для нового процесса, эти значения такие же, как у текущего процесса, и дескрипторы точно такие же в это время
- Проверьте, не превышает ли количество процессов, принадлежащих системе, лимит
- сброс многих членов
- Установите статус task_uninterruptible, чтобы гарантировать, что он не будет запущен
- вызовите copy_flags, чтобы обновить элемент flags в task_struct
- Вызовите get_pid, чтобы получить новый pid
- В соответствии с идентификацией параметра копируйте или делитесь открытыми файлами, информацией о файловой системе, функциями обработки сигналов, адресным пространством процесса, пространством имен и т. д. Как правило, эти ресурсы совместно используются потоками.
- Родительский и дочерний процессы делят квант времени поровну
- Sweep работает и возвращает указатель на дочерний процесс
- Вновь созданный процесс пробуждается и введен в эксплуатацию. Как правило, приоритетный дочерний процесс выполняется в первую очередь.
3.4 функция вилки
- Та же функция, что и fork, за исключением того, что составляет запись таблицы страниц родительского процесса.
- путем передачи специального флага системному вызову clone
- Дизайн этой функции не очень хорош
4. Реализация потоков в linux
4.1 Обзор потоков liunx
- Набор адресного пространства памяти в наборе процессов совместного использования потоков, открытых файлов и других ресурсов.
- Механизм потока поддерживает параллельные технологии программирования и обеспечивает истинную параллельную обработку на нескольких процессорах
- Механизм реализации потоков в Linux очень уникален, с точки зрения ядра понятие потоков отсутствует.
- В Linux все потоки реализованы как процессы. В ядре нет специального алгоритма планирования или структуры данных для описания потоков. Оно рассматривается как процесс, использующий некоторые общие ресурсы.
- Каждый поток имеет свою собственную структуру task_struct, как и обычный процесс, этот процесс разделяет некоторые ресурсы с другими процессами.
- Реализация сильно отличается от других систем (Windows, Solaris), ядра этих систем специально обеспечивают поддержку потоков.
4.2 создание потока Linux
- Создание потоков и общие типы создания процессов, за исключением того, что при вызове клона вам необходимо передать некоторые флаги параметров, чтобы указать ресурсы, которые необходимо разделить.
- Логотип параметра Описание:
- CLONE_VM: родительский и дочерний процессы совместно используют адресное пространство
- CLONE_SIGHAND: родительский и дочерний процессы совместно используют обработчики сигналов.
- CLONE_THREAD: родительский и дочерний процессы помещаются в одну и ту же группу потоков.
- CLONE_FS: родительский и дочерний процессы совместно используют информацию о файловой системе.
- CLONE_FILES: общие открытые файлы ...
4.3 Тема ядра
- Поток ядра: стандартный процесс, который выполняется независимо в пространстве ядра.
- Отличие от обычных процессов: нет независимого адресного пространства, он может работать только в пространстве ядра
- Создание может быть создано только другими потоками ядра, функция kernel_thread
4.4 Завершение процесса
бесплатные ресурсы
- Когда процесс завершается, ядро должно освободить имеющиеся у него ресурсы и уведомить об этом родительский процесс.
- Окончание может быть нормальным, нештатным, также можно завершить функции очистки регистрации, как описано в другой статье:Цикл статей о процессах и файлах unix
- В конце будет вызван do_exit (kenel/exit.c), и выполненная работа включает в себя:
- Установите элемент flags из task_struct в PF_EXITING.
- Если включена функция учета процессов, для вывода статистики будет вызываться acct_process
- Вызовите функцию _exit_mm, чтобы отказаться от mm_struct, занятой процессом, и полностью освободить ее, если она не используется совместно.
- Позвоните Sem_exit. Если сигнал в очереди IPC, чтобы оставить очередь
- Вызов __exit_files: дескриптор файлов уменьшения; __exit_fs: данные о файловых системе; EXIT_NAMESSAPEASE: Количество ссылочного пространства имен; EXIT_SIGHAND: Количество ссылок функции обработки сигналов, если вы можете быть выпущены
- Код выхода из task_struct устанавливает код выхода.
- Вызовите exit_notify, чтобы отправить сигнал процессу, родительский процесс изменен на другой поток или процесс инициализации, а состояние процесса установлено на TASK_ZOMBLE (зомби, не запланировано).
- Наконец, расписание вызовов для переключения на другие процессы
- После вызова do_exit все ресурсы, относящиеся к процессу, освобождаются, и единственные ресурсы, которые он занимает, — это стек ядра, сообщающий об ошибке thread_info, и небольшой slab, сохраняющий task_struct.Единственная цель существования — предоставить информацию процессу. родительский процесс.
удалить дескриптор процесса
- После звонка Do_exit, Thread Dead, но сохраняет дескриптор файла
- После того, как родительский процесс получает информацию о дочернем процессе, структура Task_sturct дочернего процесса освобождается.
- Функция ожидания вызывает реализацию системной функции wait4, которая приостанавливает вызывающий ее процесс до тех пор, пока не завершится один из дочерних процессов, а функция возвращает pid дочернего процесса.
- Когда, наконец, потребуется освободить дескриптор процесса, будет вызвана функция release_task для выполнения следующей работы:
- Вызовите free_uid, чтобы уменьшить счетчик использования процесса владельцем процесса.
- Вызовите unhash_process, чтобы удалить процесс из pidhash и удалить процесс из task_list.
- Если процесс отслеживается ptrace, сбросьте родительский процесс трассировки.
- Наконец, вызовите put_task_struct, чтобы освободить страницы, занятые стеком ядра и структурой thread_info, и освободите slab-кеш, занятый task_struct.
- В это время освобождаются все ресурсы и дескрипторы.
Обработка потерянных процессов
- Если родительский процесс завершается раньше дочернего процесса, он должен найти нового родителя, иначе он умрет навсегда.
- Функция notify_present, вызываемая функцией поиска отца в do_exit, внутренне вызывает forgot_original_parent, эта функция реализует конкретный процесс поиска.
- Эта функция устанавливает родителем другие процессы в группе потоков, если нет, используйте процесс инициализации
3. Планирование процесса
1 Обзор
- Планировщик — это компонент ядра, отвечающий за выбор следующего запускаемого процесса.
- Планировщик отвечает за выделение ресурсов процессорного времени исполняемым процессам.
- Многозадачные системы можно разделить на: вытесняющие задачи (как современные операционные системы, такие как Linux) и невытесняющие задачи.
- Время выполнения, выделяемое каждому процессу, называется временным интервалом.
2. Стратегия планирования
2.1 Интенсивный процессор и интенсивный ввод-вывод
- Интенсивность ЦП: выполнять код большую часть времени
- Интенсивный ввод-вывод: отправляйте ввод-вывод и ждите ввода-вывода большую часть времени, часто работоспособный, но очень короткое время выполнения
- Учитывая скорость отклика системы, стратегия планирования Linux более склонна сначала планировать процессы с интенсивным вводом-выводом.
2.2 Приоритет процесса
- Самый простой тип алгоритма планирования: планирование на основе приоритетов, идея градации в соответствии со значением процесса и его потребностью в процессорном времени.
- Планировщик всегда выбирает процесс, запущенный временной средой и наивысший приоритет.
- Linux реализует алгоритм планирования, основанный на динамическом приоритете. Установите базовый приоритет в начале, а затем динамически увеличивайте или уменьшайте приоритет в соответствии с потребностями: если время ожидания процесса ввода-вывода превышает время выполнения, он является интенсивным вводом-выводом и будет увеличивать приоритет; наоборот, если время процесса фрагмент не заканчивается, он загружает процессор и снижает приоритет
- Linux предоставляет два отдельных набора диапазонов приоритетов:
- хорошее значение: -20~19, по умолчанию 0. Стандартный диапазон приоритетов. Чем выше значение, тем ниже приоритет и короче интервал времени. Поле static_prio в task_struct указывает
- Приоритет в реальном времени: 0~99
2.3 Временной интервал
- Указывает, как долго процесс может продолжать работать, прежде чем будет вытеснен
- Политика планирования должна указать нарезку времени по умолчанию. Если это слишком долго, интерактивный ответ не будет хорошо работать; если оно слишком короткое, оно значительно увеличит время процессора, вызванного переключением процессов.
- Многие системы имеют очень короткий временной интервал по умолчанию: 20 мс.
- Linux предоставляет механизм для динамической настройки приоритета и длины временного интервала, что делает производительность планирования стабильной и надежной.
- Процесс не должен использовать временной интервал за один раз, его можно использовать несколько раз, чтобы гарантировать, что он может работать как можно дольше.
2.4 Вытеснение процесса
- Когда процесс находится в состоянии TASK_RUNNING, ядро проверяет, выше ли его приоритет, чем запущенный процесс, и если он удовлетворен, планировщик пробуждается, и процесс выбирается для повторного запуска.
- Когда квант времени процесса равен 0, он будет вытеснен, и планировщик может выбрать новый процесс для выполнения.
3. Алгоритм планирования
3.1 Обзор
- определение планировщика linux с помощью kernel/sched.c
- Версия ядра 2.5 переписывает алгоритм планирования, который сильно отличается от предыдущей версии, и достигает следующих целей.
- Полностью реализовать планирование O (1), каждый алгоритм может выполняться за постоянное время независимо от количества процессов или входных данных.
- У каждого процессора своя блокировка и своя исполняемая очередь
- Попробуйте назначить тот же набор задач для одного и того же процессора для непрерывного выполнения для уменьшения движущихся процессов между процессорами
- Улучшенная интерактивная производительность для обеспечения быстрого отклика системы даже при ее нагрузке.
- Гарантия честности. Устраните голодные потоки и сократите количество процессов, требующих кванта времени.
3.2 Исполняемая очередь
- Исполняемая структура данных очереди находится в очереди выполнения в файле kernel/sched.c.
- Представляет связанный список исполняемых процессов на данном процессоре, по одному на каждый процессор.
- Исполняемая очередь — это структура данных в основе планировщика, и для получения этого указателя предусмотрено множество макросов.
- cpu_rq (процессор): возвращает указанный указатель исполняемой очереди процессора.
- this_rq: текущий указатель исполняемой очереди процессора
- task_rq: указатель на очередь, в которой находится данная задача
- При работе с очередью необходимо заблокировать очередь и заблокировать функцию
- task_rq_lock
- task_rq_unlock
- this_rq_lock
- this_rq_unlock
- double_rq_lock
- double_rq_unlock
3.3 Массив приоритетов
- Каждая исполняемая очередь имеет два массива приоритетов, один активный и один с истекшим сроком действия.
- Структура данных — это prio_array в файле kernel/sched.c.
- Структура данных, обеспечивающая алгоритмическую сложность уровня O(1).
- Массив приоритетов делает так, чтобы каждый приоритет исполняемого процессора содержал соответствующую очередь, содержащую связанный список исполняемых процессов с этим приоритетом.
- Массив приоритетов также имеет растровое изображение приоритета, помогающее эффективно запрашивать исполняемый процесс с наивысшим приоритетом.
- MAX_PRIO: количество приоритетов в системе, по умолчанию 140.
- BITMAP_SISE: размер массива растровых изображений приоритетов, unsigned long составляет 32 бита, для представления 140 приоритетов требуется 5 длинных целых чисел, всего 160 бит.
- Каждый массив приоритетов должен содержать элемент растрового изображения. В начале все битовые карты равны 0. Когда начинает выполняться процесс с приоритетом, соответствующая битовая карта становится равной 1, а поиск с наивысшим приоритетом становится первым значением битовой карты поиска, равным 1, а время поиска остается постоянным. Функция sched_find_first_bit
- Чтобы найти задачу с заданным приоритетом и опросом, просто перейдите к списку приоритетов
- nr_active указывает количество исполняемых процессов в массиве приоритетов
3.4 Пересчет кванта времени
- Когда все кванты времени потока израсходованы, старая версия Linux пересчитывает квант времени путем вычисления циклического обхода.
- Новая версия планировщика поддерживает два массива приоритетов: активный массив и массив с истекшим сроком действия.Поток, время истечения которого истекло, помещается в массив с истекшим сроком действия, а квант времени массива с истекшим сроком действия вычисляется асинхронно. Наконец, просто поменяйте местами два массива
3.5 Вычислительный приоритет и квант времени
Динамический расчет приоритета
- После того, как статический приоритет указан пользователем, его нельзя изменить, то есть введенное ранее значение nice
- Динамический приоритет рассчитывается на основе статического приоритета и функции интерактивности процесса.
- Функция Effective_prio возвращает динамический приоритет процесса: функция берет значение nice в качестве основы, а затем добавляет значение вознаграждения и наказания (от -5 до 5) в соответствии со степенью взаимодействия.
- Обмен оценкой степени: вычислить время выполнения и время ожидания процесса. Переменная sleep_avg в task_struct, по умолчанию 10 мс, увеличьте это значение (продолжительность сна) во время сна и уменьшите это значение при работе.
Динамический расчет временных интервалов
- При создании процесса родительский и дочерний процессы делят квант времени поровну. Предотвратите создание нескольких процессов, чтобы получить больше процессорного времени
- Когда квант времени задачи истекает, квант времени рассчитывается в соответствии с задачей с динамическим приоритетом. Функция task_timeslice. Значение приоритета значения времени масштабируется. До 200 мс, минимум 10 мс, по умолчанию 100 мс
- Если высокоинтерактивный процесс (макрос TASK_INTER_ACTIVE) после того, как квант времени будет израсходован, он будет снова помещен в активный массив вместо массива с истекшим сроком действия. Избегайте необходимости взаимодействовать и не выполнять, потому что он не ждет, пока два массива будут заменены местами. Код реализации: функция scheduler_tick
3.6 Сон и пробуждение
- Бездействующий поток входит в очередь ожидания, которая содержит простой связанный список всех процессов, ожидающих определенных событий.
- Структура данных очереди ожидания — wake_queue_head_t.
- Ожидание создания очереди: DECLEAR_WAITQUEUE или init_waitqueue_head
- Присоединяйтесь к очереди ожидания: add_wait_queue
- Когда очередь ожидания, связанная с инцидентом, процесс очереди будет пробужден, функция, использующая wake_up
3.7 Балансировщик нагрузки
- Балансировщик нагрузки предназначен для несбалансированной загрузки исполняемых программ в многопроцессорной системе.
- Это функция load_balance в файле kernel/sched.c.
- Время вызова: когда исполняемая очередь пуста, она будет вызываться регулярно (каждую 1 мс, когда система простаивает, и каждые 200 мс в других случаях). Эта функция не требуется для однопроцессорных систем.
- Когда вызывается балансировка нагрузки, текущая очередь должна быть заблокирована, а прерывания экранированы.
- шаги операции load_balance:
- Позвоните Find_busiest_Queue, чтобы найти самую загруженную очередь. Очередь имеет наибольшее количество процессов. Если нет более 25% текущей очереди, просто заканчивается и вернуться
- Выберите массив приоритетов из очереди занятости, чтобы использовать его для извлечения процессов, предпочтительно массив с истекшим сроком действия.
- Обращается к связанному списку с наивысшим приоритетом (наименьшее значение) и распределяет высокоприоритетные процессы.
- Найдите процесс в связанном списке, который не выполняется, может быть перемещен и не находится в кеше, и используйте pull_task для извлечения процесса в текущую очередь.
- Описанные выше шаги выполняются всякий раз, когда очередь несбалансирована. Наконец, отпустите замок.
4. Упреждение и переключение контекста
4.1 Обзор
- Переключение контекста переключается с одного процесса на другой.
- Переключение контекста определено в функции context_switch в файле kernel/sched.c, которая выполняет две основные задачи.
- Вызов switch_mm, определенный в include/asm/menu_context.h, отвечает за переключение виртуальной памяти из предыдущего сопоставления процесса в новый процесс.
- Вызов switch_to, определенный в include/asm/system.h, отвечает за переключение из состояния процессора предыдущего процесса в состояние процессора нового процесса. Включая сохранение, восстановление информации о стеке и информацию о регистрах.
- Ядро предоставляет флаг need_resched, чтобы указать, требуется ли повторное планирование. Schedule_tick устанавливает этот флаг, когда процесс исчерпал временной интервал; try_to_wake_up также устанавливает этот флаг, когда высокоприоритетный процесс переходит в исполняемое состояние.
- Каждый процесс включает флаг need_resched
4.2 Преимущество пользователя
- Пользовательское вытеснение: когда ядро возвращается в пространство пользователя, если установлено need_reshed, вызывающее вызов расписания, для выполнения будет выбран более подходящий процесс.
- Вытеснение пользователя происходит, когда:
- Возврат в пространство пользователя из системного вызова
- Возврат в пространство пользователя из обработчика прерывания
4.3 Преимущество ядра
- Большинство других вариантов Unix и большинство операционных систем не поддерживают вытеснение ядра, код ядра должен выполняться до завершения.
- 2.6 Версия ядра, добавлено упреждение ядра. Пока перераспределение безопасна, вы можете проверить ядро
- Пока блокировка не удерживается, перепланирование безопасно и может быть вытеснено ядром.
- Удержание блокировки представлено счетчиком preempt_count в thread_info. Значение увеличивается на единицу, когда блокировка используется, и значение уменьшается на единицу, когда блокировка снимается.
- Вытеснение ядра происходит, когда:
- При возврате в пространство ядра из обработчика прерывания
- Когда код ядра снова становится вытесняемым
- Задача в ядре показывает вызов по расписанию
- Блокировка задач в ядре
5. Системные вызовы, связанные с планированием
- sched_setscheduler: установить политику и значения rt_priority для task_struct
- sched_setaffinity: установить флаг битовой маски cpus_allowed для task_struct
- sched_yield: переместите процесс из активной очереди в очередь с истекшим сроком действия, чтобы сократить время выполнения.
4. Системный вызов
1 Обзор
- Системные вызовы обеспечивают интерфейс для взаимодействия между ядром и приложением.
- Внутри системного вызова asmlinkage должен быть добавлен к объявлению функции, чтобы сообщить время компиляции, чтобы извлекать только параметры функции из стека.
- Системные вызовы имеют префикс sys_ в ядре.
- Каждый системный вызов в Linux связан с уникальным номером системного вызова.
- Ядро записывает список всех зарегистрированных системных вызовов в таблицу системных вызовов, которая хранится в sys_call --table, связанной с архитектурой.
- Дизайн ядра Linux оптимизирован и лаконичен, время переключения контекста чрезвычайно быстрое, а эффективность выполнения операционной системы высока.
2. Обработчик системных вызовов
- Пользовательские программы не могут напрямую вызывать функции ядра, чтобы предотвратить выход из-под контроля безопасности пространства ядра. Вместо этого ядро уведомляется прерыванием, и ядро выполняется от имени программы.
- Перед запуском мягкого прерывания загрузите номер вызова в регистр eax.
- Передача параметров: В системах x86 ebx, ecx, edx, esi и edi сохраняют первые пять параметров по порядку. Возвращаемое значение возвращается через регистр eax
- Функции копирования пространства ядра и пространства пользователя: copy_to_user, copy_from_user
3. Контекст системного вызова
- Текущий указатель указывает на процесс, вызвавший текущий вызов
- В контексте процесса при выполнении системного вызова
- В контексте процесса ядро может находиться в спящем режиме (блокировка вызовов или расписание) и может быть вытеснено
4. Реализация системных вызовов
- Linux не поддерживает многоцелевые системные вызовы, каждый системный вызов должен иметь четкую цель.
- Интерфейс должен быть максимально лаконичным с небольшим количеством параметров. Стремитесь к стабильности, не внося изменений
- Старайтесь думать о будущем, старайтесь быть как можно более общим и не ограничивайте его. «Предлагайте механизмы, а не тактику»
- После написания его нужно зарегистрировать в ядре и стать реальным доступным системным вызовом
- Лучше всего добавить запись в системный вызов. Большинство из них находятся в файле entry.s. Все аппаратные системы, поддерживающие системные вызовы, должны быть
- Системный номер вызова определен для включения файла /asm/unist.h
- Функция помещается в папку под файлом ядра, поэтому она скомпилирована в образ ядра (не может быть скомпилирована как модуль)
- Как пользовательское пространство получает доступ к зарегистрированным системным вызовам
- Обычно пользователи могут использовать системные вызовы, включив стандартные заголовочные файлы и связав их с конкретной реализацией C базовых системных вызовов.
- Пользовательский системный вызов не существует в файле заголовка флага, и его можно вызвать с помощью макроса, предоставленного linux: _syscalln, n представляет параметры, которые необходимо передать. Макрос имеет 2+2n параметров, первый представляет тип возвращаемого значения, второй представляет имя функции, за которым следуют n типов параметров и имена параметров.
- Например: системный вызов функции open, номер системного вызова _NR_open, который определен в
и внутренне реализован макросом _syscall3.При вызове open макрос напрямую помещается в код приложения.
V. Прерывания и обработчики прерываний
1. Прерывание
- Прерывания используются для решения проблемы несоответствия скорости обработки процессора компьютера и аппаратного устройства.После того как аппаратное обеспечение обработало задачу, оно активно посылает сигнал процессору.
- Прерывание — это, по сути, электрический сигнал, который генерируется аппаратным устройством и отправляется на входной контакт обработчика прерывания. Затем контроллер прерываний посылает сигнал процессору. Процессор получает сигнал и прерывает работу, чтобы обработать прерывание
- Каждое прерывание уникально идентифицируется в цифровом виде и называется линией запроса на прерывание (IRQ). Например: IRQ0 — прерывание часов, IRQ1 — прерывание клавиатуры.
2. Обработчик прерываний
- При ответе на определенное прерывание функция, которая выполняется, является обработчиком прерывания или подпрограммой обслуживания прерывания.
- Желевые обработчики прерывания являются частью драйвера устройства, который является кодом ядра, используемого для управления устройством
- Отличие от функции ядра: обработчик прерывания вызывается ядром для ответа на прерывание, работая в контексте прерывания.
- Обработчики прерываний должны выполняться быстро, и в то же время мы полагаемся на них при выполнении большого количества другой работы. Эти две цели противоречат друг другу.
- Для того, чтобы разрешить вышеуказанное противоречие, обработчик прерывания разделен на две половины
- Верхняя половина: выполнить сразу после получения запроса, но выполнить небольшой объем работы. Подтверждение прерывания и аппаратный сброс
- Нижняя половина: выполнить, как только обработчик прерывания вернется
3. Зарегистрируйте обработчик прерывания
- IRQ: номер прерывания должен быть назначен
- обработчик: Фактический обработчик прерывания. Принимает три параметра и возвращает параметр типа irqreturn_t
- irqflags: может быть 0 или битовой маской следующих флагов
- SA_INTERRUPT: Указывает, что это программа быстрого прерывания. В основном используется для процедур прерывания часов
- SA_SAMPLE_RANDOM
- SA_SHIRQ: общая линия прерывания
- devname: текстовое имя устройства, связанного с прерыванием, в кодировке ASCII, например, «клавиатура» в середине клавиатуры.
- dev_id: используется для общих линий прерывания
- Эта функция может быть бездействующей, код не может быть вызван в контексте прерывания или не позволяет блокировать
4. Механизм обработки прерываний
- Устройство генерирует прерывание и отправляет электрический сигнал на контроллер прерываний.
- Контроллер прерываний посылает сигнал процессору
- Процессор прерывает программу и переходит в предопределенное место в памяти для выполнения. является точкой входа программы прерывания
- Ядро вызывает do_IRQ для ответа на прерывание.
- Соответствующие функции находятся в arch/i386/kernel/entry.s, arch/i386/kernel/irq.c.
5 Управление прерываниями
- Linux предоставляет набор интерфейсов для управления статусом прерывания на машине, предоставляя возможность отключить систему прерывания или экранировать линию прерывания.
- Соответствующий код находится в
, - Корень управления прерываниями заключается в обеспечении синхронизации.Отключение прерываний гарантирует, что программа прерывания не вытесняет текущий код, а ядру также можно запретить вытеснение
- Отключить и включить текущие прерывания процессора: local_irq_disable, local_irq_enable
- Отключить (защитить) назначенную линию прерывания: disable_irq, disable_irq_nosync, enable_irq, synchronize_irq
- Получает систему прерывания Статус: ASM / SYSTEM.h В IRQS_DISAL
- Определите, находится ли он в контексте прерывания: in_interrupt в asm/hardirq.h
6. Синхронизация ядра
1. Основные понятия
- Критическая секция: секция кода, которая обращается к общим данным и управляет ими.
- Состояние гонки: несколько потоков выполнения в одном и том же критическом разделе.
- Синхронизация: избегание параллелизма и предотвращение условий гонки
- Зачем нужна синхронизация: пользовательские программы вытесняются и перепланируются планировщиком
- Причины одновременности следующие:
- прерывать
- вытеснение ядра
- Синхронизация сна и пользовательского пространства
- мультипроцессор
- Решение проблем с синхронизацией: блокировка
- Какие данные нужно заблокировать
- глобальные переменные ядра
- общие данные
- Тупик; все потоки ждут друг друга, чтобы снять блокировку, и ни один поток не может продолжить работу.
2. Метод синхронизации ядра
2.1 Атомарные операции
- Атомарные операции гарантируют, что инструкции выполняются атомарно.
- Атомарные операции обычно являются встроенными функциями, которые выполняются с помощью встроенных ассемблерных инструкций.
- Атомарные операции дают системе меньше накладных расходов, чем другие методы синхронизации.
- Ядро Linux обеспечивает атомарные операции над целыми числами и отдельными битами.
- Функции, связанные с целочисленными атомарными операциями, находятся в файле asm/atomic.h.
- Функции, связанные с битовыми атомарными операциями: asm/bitops.h
2.2 Замок самоворота
- Spin Lock может удерживаться только одним исполняемым потоком. Если замок не занят, нить можно получить сразу. Если он занят, он всегда будет зациклен на доступном замке.
- Спин-блокировки можно использовать для предотвращения одновременного входа нескольких потоков в критическую секцию.
- Вращение особенно расточительно для процессора, поэтому его не следует удерживать в течение длительного времени.
- Интерфейс определен в
, а конкретная реализация, связанная с архитектурой, находится в . - Рекурсия недоступна для спин-блокировок в Linux.
- Использование спин-блокировок
spinlock_t mr_lock = SPIN_LOCK_UNLOCKED; //普通请求锁 spin_lock(&mr_lock); //禁止中断请求锁 spin_lock_irqsave(&mr_lock); //确保中断是激活的情况可用的方法 spin_lock_irq(&mr_lock) /**临界区**/ spin_unlock_irq(&mr_lock) spin_unlock_irqrestore(&mr_lock); spin_unlock(&mr_lock)
- Спин-блокировки можно использовать в обработчиках прерываний (семафор не годится, он вызовет сон), перед использованием блокировок локальные прерывания должны быть отключены, иначе это вызовет взаимоблокировку
2.3 Чтение и запись спин-блокировок
- Использование блокировки можно четко разделить на блокировку чтения и блокировку записи.
- Одна или несколько задач чтения могут одновременно удерживать блокировки чтения.
- Блокировка, используемая для записи, может удерживаться только одной задачей записи, и одновременное чтение в настоящее время невозможно.
- Использование блокировки чтения-записи
rwlock_t mr_rwlock = RW_LOCK_UNLOCKED; read_lock(&mr_rwlock); /**只读临界区**/ read_unlock(&mr_rwlock) write_lock(&mr_rwlock) /**读写临界区**/ write_unlock(&mr_rwlock)
- Обновление не может читать замки для записи блокировков, приведет к тупику
2.4 Семафор
- Семафор — это блокировка сна
- Разрешить любое количество держателей замков одновременно
- Когда количество семафоров равно 1, он называется бинарным семафором или семафором взаимного исключения.
- Если задача пытается получить доступ к занятому семафору, семафор помещает его в очередь ожидания, переводя его в спящий режим. Когда процесс, удерживающий семафор, освобождается, ожидающая задача пробуждается и семафор получается.
- Характеристики семафоров
- Подходящий чехол блокировки будет проходить в течение длительного времени
- Блокировка удерживается в течение короткого времени, и время сна может быть больше, чем полное время
- будет спать и не может быть вызван в прерывании
- Спин-блокировка не может удерживаться, когда удерживается семафор. Спинлоки не позволяют спать
- Семафор поддерживает две атомарные операции P и V, голландский зонд и приращение.
- Файлы, связанные с семафором:
- использовать семафор
//声明信号量 static DECLARE_SEMAPHORE_GENERIC(name, count); //声明互斥量 static DECLARE_MUTET(name); //以指针方式初始化信号量 sema_init(sem, count); //以指针方式初始化互斥量 init_MUTET(sem); //试图获得信号量 down_interruptible(&name) //释放信号量 up(&name)
2.5 Чтение и запись семафоров
- то же, что блокировка чтения-записи
- Связанные файлы:
2.6 Полные переменные
- Предоставляет простой обходной путь вместо семафора
- Связанные файлы:
2.7 SEQ LOCK
- Он обеспечивает простой механизм для чтения и записи общих данных
- Внутренне реализует в основном счетчик последовательности
- Инициализация блокировки 0. При записи данных блокировка будет получена, а значение последовательности увеличится. До и после чтения данных считывается порядковый номер, если он одинаковый то не прерывается, если четный то операция записи не происходит
2.8 Барьеры
- Барьеры обеспечивают выполнение инструкций по порядку и запрещают изменение порядка инструкций.
- Изменение порядка происходит потому, что современные процессоры нарушают порядок, в котором инструкции отправляются и фиксируются, чтобы оптимизировать их конвейер передачи.
- Метод rmb обеспечивает барьер «чтения» памяти, загрузки перед rmb не ставятся в очередь после rmb, и наоборот.
- Метод wmb обеспечивает барьер «записи» в память, и операции сохранения перед wmb не ставятся в очередь после wmb.
- Метод mb обеспечивает барьер «чтение-запись».
7. Управление памятью
1. Ядро управления памятью
1,1 страницы
- Ядро рассматривает физические страницы как основную единицу управления памятью.
- Блок управления памятью: MMU, аппаратное обеспечение, которое управляет памятью и преобразует виртуальные адреса в физические адреса.
- Размер страницы зависит от архитектуры.Большинство 32-разрядных архитектур поддерживают страницы размером 4 КБ, а 64-разрядные архитектуры поддерживают страницы размером 8 КБ.
- Структура данных физической страницы находится в struct page в
. страница связана с физическими страницами, а не с виртуальными страницами sturct page{ unsigned long flags; // 页的状态,是否脏,是否被锁定。可表示32种状态。定义与<linux/page-flags.h> atomic_t count; // 页的引用计数,被使用了多少次。为0时,新的分配就可以使用它 struct list_head list; struct address_space *mapping; //指向与该页有关的address_space对象 unsigned long index; struct list_head lru; union{ struct pte_chain *chain; pte_addr_t direct; }pte; unsigned long private; void *virtual; //页虚拟地址,虚拟内存地址 }
1.2 Зона
- Из-за аппаратных ограничений некоторые страницы располагаются по определенным физическим адресам в памяти и не могут использоваться для конкретных задач, поэтому они также разделены на разные зоны.
- Страницы группы зон со схожими характеристиками. Разделение области не имеет физического смысла, оно используется только для управления логической группировкой страниц.
- Linux использует три типа областей:
- ZONE_DMA: зона, которая может выполнять DMA (прямой доступ к памяти).
- Zone_Normal: зона, которая может быть отображена нормально
- ZONE_HIGHEM: «более высокая память», которую нельзя постоянно отображать в адресное пространство ядра.
- Структура данных зоны находится в struct zone файла
.
2. Интерфейс, связанный со страницей
- Ядро предоставляет низкоуровневый механизм запроса памяти и предоставляет интерфейсы доступа. Интерфейсы выделяют память со страницами как Праздник лодок-драконов. Определение такое же, как в
// 分配2^order个连续的物理页,并返回指针 struct page* alloc_pages(unsigned int gfp_mask, unsigned int order) // 将页转换为逻辑地址,页是连续的,其他页紧随其后 unsigned long __get_free_pages(unsigned int gfp_mask, unsigned int order) // 只获取一页,高端地址分配需要使用此函数 struct page* alloc_page(unsigned int gfp_mask) unsigned long get_free_page(unsigned int gfp_mask) //获取填充内容为0的页 unsigned long get_zeroed_page(unsigned int gfp_mask) //释放页 void __free_pages(struct page *page, unsigned int order) void free_pages(unsigned long addr, unsigned int order) void free_page(unsigned int order) // 与用户空间的malloc函数类似,最通用的接口。提供用于获得以字节为单位的一块内核内存 // 定义与<linux/slab.h>中 void *kmalloc(size_t siez, int flags) // kmalloc相反函数,释放空间 void kfree(const void *ptr) // 确保分配的页在物理地址上是连续的 // 定义与<linux/vmalloc.h> void* vmalloc(unsigned long size) //释放空间 void vfree(void *addr)
- Флаг gfp_mask в
разделен на три категории: модификаторы поведения, модификаторы области и типы. - Модификаторы поведения: как ядро выделяет требуемую память
- Модификаторы области: откуда выделить память
- Тип: комбинированный модификатор поведения и модификатор области
3. slab
- Slab обеспечивает роль уровня кэша общей структуры данных, slab будет поддерживать объект для каждого процессора, чтобы сообщить кэшу (бесплатный список)
- Подходит для больших структур данных, которые необходимо создавать и уничтожать
- Слой slab делит разные объекты на так называемые группы кеша, в каждой из которых хранятся разные типы объектов (каждый тип объекта соответствует кешу)
- Каждый блок находится в одном из трех состояний: медленный, частичный или пустой.
- Несколько SALB составляют кэш, различные структуры данных:
- Плита: структурная плита
- Кэш: kmem_cache_s
- Полный связанный список: slabs_full
- Частично связанный список: slabs_partial
- Пустой связанный список: slabs_empty
4. Отображение высокопроизводительной памяти
- Страница памяти верхнего уровня не может быть постоянно отображена в адресное пространство ядра, поэтому страница, полученная с помощью некоторого флага, не является логическим адресом.
- В системах x86 страницы в верхней памяти сопоставляются с логическими адресами 3–4 ГБ.
- Отображение связанных интерфейсов:
//映射一个给定的page结构到内核地址空间: void kmap(sturct page *page) //解除映射关系 void kunmap(struct page* page) //临时映射 void *kmap_atomic(sturct page *page, enum km_type type)
8. Виртуальная файловая система
1. Основные понятия
- Виртуальная файловая система: VFS, которая предоставляет интерфейс файловой системы. Через VFS вы можете использовать стандартную файловую систему unix для вызова разных файлов на разных носителях для работы.
- Linux поддерживает довольно много файловых систем (более 50):
- Локальная файловая система: ext2, ext3
- Сетевые файловые системы: NFS, Coda
- В VFS есть четыре основных типа объектов.
- сверхбыстрый объект, представляющий смонтированную файловую систему
- Объект Inode, представляющий файл
- Объект ввода каталога, представляющий запись каталога
- Файловый объект, представляющий файл, открытый процессом
2. Суперблочный объект
- Различные файловые системы должны реализовывать суперблоки, которые используются для хранения информации о конкретной файловой системе, обычно соответствующие блокам управления файловой системой, хранящимся в определенных секторах диска.
- Структура данных суперблока определяется с помощью super_block в
. - s_op в суперблоке указывает на таблицу функций сверхбыстрых операций, которая представлена структурой super_operation. Когда файловая система работает с суперблоком, она находит соответствующий метод операции.
- То есть информация о разных файловых системах предоставляется VFS путем регистрации собственного метода для операций с файловой системой в super_operation.
- Связанный со сверхбыстрым кодом код находится в
3. Инод объект
- Объект inode содержит всю информацию, необходимую ядру для работы с файлом или каталогом.
- Структура данных объекта inode находится в struct inode в
. - Представляет файл в файловой системе (включая обычные файлы, каналы и т. д.)
- Элемент inode_operations в айноде также очень важен, определяя все методы работы с объектом айнода.
4. Объект элемента каталога
- Записи каталога включают выполнение операций, связанных с каталогом, таких как поиск пути и т. д.
- Структура данных записи каталога находится в struct dentry в
. - Статус элемента каталога включает в себя: используется, не используется и отрицательный статус
- Элементы каталога также включают кэши элементов каталога, в том числе:
- Список ожерелий из каталога б/у
- Недавно используется вдвойне связанный список
- Hash Tables и соответствующие хеш-функции используются для быстрого разрешения заданного пути в связанные объекты ввода каталогов
- Операции входа в каталог определены в структуре dentry_operation, расположенной в
.
5. Файловый объект
- Определение файлового объекта и структурная файловая структура в
- Файловые операции представлены структурой file_operations.
- Конкретные файловые системы определяют разные реализации
6. Другие структуры данных
- Структуры данных, связанные с файловой системой: struct file_system_type, описывающая конкретный тип файловой системы, такой как ext3 или XFS.
- Пример смонтированной файловой системы: vfsmount,
- Данные, на которые указывают файлы дескриптора процесса, включая такую информацию, как открытые файлы и дескрипторы файлов, связанные с процессом: struct files_struct,
- Данные, на которые указывает fs дескриптора процесса, включая файловую систему и информацию о процессе: struct fs_struct,
- Данные, на которые указывает пространство имен дескриптора процесса, включая информацию о пространстве имен: struct namespace,
9. Уровень блока ввода-вывода
1. Основные понятия
- К основным типам устройств относятся: блочное устройство, символьное устройство. Разница в том, можно ли получить к нему доступ случайным образом
- Наименьшая адресуемая единица в блочном устройстве — это сектор, а размер сектора обычно кратен 2. Наиболее распространенный размер — 512 байт.
- Физические диски адресуются в секторах, доступ к файловым системам осуществляется в блоках, а блоки представляют собой более высокий уровень абстракции.
- Блок содержит один или несколько секторов, но не больше, чем размер страницы
- Когда блок вызывается в память, его необходимо загрузить в буфер сначала, и каждый буфер соответствует блоку
- Каждый буфер имеет дескриптор, представленный структурой buffer_head, называемой заголовком буфера. в файле
. Включая состояние буфера, количество использований, номер логического блока, физическую страницу, размер блока и т. д.
2. bio
- В настоящее время основным контейнером блочных операций ввода-вывода в ядре является структура bio, расположенная в
3. Планировщик ввода-вывода
10. Адресное пространство процесса
1. Основные понятия
- Каждый процесс имеет уникальное адресное пространство, взаимные помехи друг другу
- Процесс может обращаться только к адресам памяти в допустимом диапазоне.
- Область памяти содержит различные объекты памяти, в том числе:
- Фрагмент кода: карта памяти исполняемого кода
- сегмент данных: карта памяти инициализированных глобальных переменных
- Сегмент BSS: глобальная переменная не инициализирована
- разное
2. Дескриптор памяти
- Ядро использует дескриптор памяти представляет собой адресное пространство процесса.
- Структура данных дескриптора памяти находится в mm_struct в
. - Внутренние структуры данных mmap и mm_rb представляют одно и то же содержимое, которое здесь избыточно. Первый представляет собой связанный список, по которому легко и эффективно перемещаться, а второй представляет собой красно-черное дерево, удобное для эффективного поиска.
- Структура mm_struct, имеющая блокировку, связана в двусвязном списке через собственный список mmlist, процесс инициализации первого элемента, дескриптор init_mm. При работе со связанным списком используйте mmlist_lock для блокировки, блокировка находится в
- Поле mm в дескрипторе процесса хранит дескриптор памяти
- Назначить дескриптор памяти: COPY_MM, внутренний вызов allocate_mm распределения макросов из slab-кэша mm_cachep
- Поток ядра не имеет адресного пространства процесса, а поле MM пусто.
3. Область памяти
- Область памяти представлена структурой vm_area_struct, определенной в
, также называемой областью виртуальной памяти. - vm_area_struct описывает независимый диапазон памяти в непрерывном пространстве в указанном адресном пространстве.
- vma включает в себя множество флагов, которые отмечают поведение и информацию содержащихся страниц.
- VM_READ: разрешение на чтение страницы
- VM_WRITE: разрешение на запись страницы
- VM_EXEC: Страница исполняемых разрешений
- vm_ops структуры vm_area_struct указывает на таблицу функций операций, относящуюся к указанной области памяти, которая представлена vm_operations_struct.
11. Кэш страницы и обратная запись страницы
1. Основные понятия
- Кэш страницы — это своего рода дисковый кеш, реализованный Linux, который в основном используется для уменьшения операций ввода-вывода на диске.
- При кэшировании данных на диске в физической памяти доступ к диску заменяется доступом к физической памяти.
- Значение дискового кеша:
- Более быстрый доступ, память быстрее, чем диск
- Временный локальный принцип: после доступа к данным, скорее всего, к ним снова будут обращаться в краткосрочной перспективе.
- Перед выполнением операции io ядро проверит, есть ли данные уже в страничном кеше, и если да, то может сразу вернуться
2. Структура данных кэша страниц
- Кэш страниц использует структуру address_space для описания страниц в кеше страниц, как определено в
. - Внутренний a_ops указывает на таблицу функций операций в объекте адресного пространства, который представлен структурой address_space_operations.
- Проверка того, находится ли страница в кеше, должна быть эффективной, система счисления, предоставляемая каждой структурой address_, двоичное дерево дерева страниц, записывает смещение файла. с целью быстрого поиска. Код radix-tree находится в
3. Страница запишите обратно
- Фоновая процедура pdflush отвечает за запись грязных страниц памяти обратно на диск.
- Свободная память падает ниже определенного значения, чтобы освободить память. Порог настраивается
- Время пребывания грязной страницы превышает определенное значение
- Периодическая обратная запись для предотвращения ненормальной потери данных в системе
- Код pdflush находится в
. Код механизма обратной записи находится в , .
12. Другие
Таймеры и управление временем
- Периодическое время управляется системным таймером, который представляет собой программируемую аппаратную микросхему, которая может генерировать прерывания с фиксированной частотой (прерывания таймера).
- Фактическое время: определено в
. struct timespec xtime, структура данных timespec определена в файле - Таймер: определен в
структурой time_list.