Ах, процесс. Первая из многих абстракций, которые мы рассмотрим в этой серии, и самая фундаментальная часть любой операционной системы. Неформально процесс можно рассматривать как запущенную программу. Когда вы садитесь и набираете код, вы пишете программу. После запуска кода активное последовательное выполнение вашей программы называется процессом.

Для выполнения процессу нужно несколько вещей. А именно, ему нужен доступ к памяти и доступ к процессору. Память необходима, потому что, когда процесс выполняется, код загружается в память в течение жизни процесса. Доступ к ЦП необходим, потому что это часть компьютера, которая может «понимать» программу и выполнять каждую инструкцию.

Теперь я хочу провести вас через интерактивное упражнение. Если вы используете Mac или Linux, откройте свой терминал и запустите

ps aux

Если у вас Windows, я не знаю команду для просмотра всех активных процессов, может быть, в диспетчере задач? В любом случае количество активных запущенных процессов на вашем компьютере почти наверняка будет больше 20.

Теперь, опять же, если вы используете Mac или Linux, в типе вашего терминала

getconf _NPROCESSORS_ONLN

Это покажет, сколько процессоров в вашей системе. В Windows, я думаю, вы можете просмотреть это в диспетчере задач.

В любом случае очень вероятно, что у вас гораздо меньше процессоров, чем активных процессов, что открывает наш первый большой вопрос о Linux:

Как мы можем создать иллюзию наличия множества процессоров?

Linux создает эту иллюзию за счет виртуализации процессора. По сути, ОС имеет право остановить выполнение одного процесса и начать выполнение другого. Пока процесс не выполняется, он не знает, что процессор где-то используется. Однако, когда он использует ЦП, он предполагает, что это единственный активный процесс, получающий полный доступ к ЦП.

Ядром этой системы виртуализации является способность ОС переключаться между процессами. Для этого используются два инструмента:

  • Диспетчер: низкоуровневый метод, который выполняет переключение контекста (подробнее обсуждается позже).
  • Планировщик: логика более высокого уровня, которая решает, какой процесс должен выполняться, когда

Оба они будут рассмотрены более подробно позже. Однако стоит отметить, что обе системы должны знать состояние процесса, чтобы выполнять свою работу.

Состояния процесса

Обычно процессы могут находиться в одном из трех состояний:

  • Выполняется: процесс загружается в процессор и инструкции активно выполняются.
  • Готов: в этом состоянии процесс готов к выполнению, но в настоящее время не выполняется на процессоре.
  • Заблокирован: в этом состоянии процесс сделал что-то, что не позволяет ему быть готовым к запуску. Ярким примером этого является ввод-вывод. Если процесс считывает информацию с диска, он обычно не может продолжить выполнение до тех пор, пока этот ввод-вывод не будет завершен, поэтому он блокируется, чтобы какой-то другой процесс имел доступ к ЦП.

Состояние процесса можно найти в его блоке управления процессом (PCB). У каждого процесса есть печатная плата, которая содержит следующую информацию:

struct proc {
    char* mem;               // The start of process memory
    enum proc_state state;   // The state of the process
    int pid;                 // The Process ID
    struct context context;  // Switch here to run process
    ...
} 
// Side note: any code written in this series will be in C. 
// If you would like me to post a C tutorial series, leave
// a comment.

В печатной плате есть еще кое-что, но перечисленные мной четыре значения наиболее важны для целей этой серии. В частности, мы подробнее рассмотрим структуру контекста в следующей статье.

Помимо этой структуры proc, все процессы имеют адресное пространство . Адресное пространство - это, по сути, объем памяти, который предоставляется процессу. Адресные пространства - еще один пример абстракции, который мы обсудим далее в этой серии.

Взаимодействие с Linux: API процесса

API процесса - это, по сути, интерфейс, который Linux предоставляет при работе с процессами. Этот интерфейс содержит несколько системных вызовов:

вилка()

Этот системный вызов создает новый процесс. Этот новый процесс называется дочерним процессом, а процесс, вызвавший системный вызов fork (), называется родительским процессом. Если вы когда-нибудь слышали термин fork bomb, это связано с тем, что системный вызов fork () вызывается слишком много раз, переполняя систему активными процессами. Таким образом, как вы, вероятно, предположили, нам нужен способ остановки активных процессов.

убийство()

Системный вызов kill () отправляет сигнал процессу. Можно передать PID процесса, который они хотят завершить, в качестве параметра в системном вызове kill (), и этот процесс будет остановлен независимо от того, было ли завершено его выполнение. Системный вызов kill () может использоваться не только для уничтожения процессов, существуют и другие сигналы, которые могут быть полезны для отправки процессу. Но что, если мы хотим, чтобы процесс завершился сам по себе, не убивая его?

ждать()

Системный вызов wait () используется родительским процессом, чтобы, как вы уже догадались, дождаться завершения дочернего процесса. Это полезно, если вам нужно возвращаемое значение от дочернего процесса. Однако на данный момент дочерний процесс будет выполнять те же инструкции, что и родительский. Подумайте об этом, мы вызывали fork (), но никогда не указывали новый код для дочернего процесса. Вот почему нам нужен последний системный вызов.

exec ()

Последний системный вызов, о котором стоит упомянуть в этой статье, - это exec (). Вы должны вызвать exec () в дочернем процессе и указать имя некоторого исполняемого файла, и Linux будет знать, что нужно загрузить код из исполняемого файла и перезаписать текущий сегмент кода.

Ключевые выводы

  • Программа и процесс - две разные вещи
  • Чтобы одновременно работало несколько процессов, мы виртуализируем ЦП.
  • Процесс всегда либо выполняется, либо готов, либо заблокирован.
  • Есть API, позволяющий создавать процессы и взаимодействовать с ними.

Спасибо за чтение. Как всегда, если вы заметили, что я ошибся, не стесняйтесь оставлять комментарий. В следующей статье я расскажу о планировщике и познакомлю с некоторыми основами теории очередей.

Удачного взлома :)