Продолжаем публикацию книги о программирование на языке ассемблера (GAS) в операционной системе Linux (x86-64). Сегодня говорим о создании дочернего процесса и функции fork.
Параграф 7.2
Создание процесса с помощью функции fork на ассемблере
Как уже было сказано в предыдущем параграфе при запуске приложения создается объект под названием процесс. Автоматически создается объект для хранения данных об исполнении кода, который называется потоком. Для программного запуска другого процесса можно использовать системную функцию execve. При этом создаваемый процесс полностью замещает старый. Однако существует возможность создавать параллельные процессы. В данном параграфе мы рассмотрим этот механизм.
Для того, чтобы создать параллельный процесс, назовем его дочерним, поскольку его порождает другой процесс, существует системная функция fork, номер 57. Функция не имеет параметров, но не проста для понимания. Она сразу возвращает управление. При этом возникает и дочерний процесс. Дочерний процесс наследует: 1. Определенные до этого переменные. 2. Код, начиная с вызова функции fork. При этом в дочернем процессе функция возвращает 0, а в родительском процессе идентификатор дочернего процесса. Надо иметь в виду, что у дочернего процесса своя память. Но она работает с образом, полученным от родительского процесса.
Чтобы несколько сократить код программы, для вспомогательных функций вывода на консоль мы используем язык C (см. листинг 63)
Программа в листинге 64 запускает дочерний процесс и заканчивает свою работу. При этом дочерний процесс продолжает работать с точки вызова функции fork. По сути тот факт, что родительский процесс заканчивает свою работу никак не влияет на работу дочернего процесса. Просто если родительский процесс заканчивает работу раньше, то для дочернего процесса родительским становится их прародитель в операционной системе Linux — systemd. Конечно родительский процесс может влиять на дочерний, отслеживать его работу, но эти вопросы будут рассмотрены в следующем параграфе.
Может возникнуть следующий вопрос: если дочерний процесс унаследует код родительского, то в коде можно различить, выполняется родительский или дочерний. Но вспомним, что в дочернем процесс fork возвращает 0, а в родительском — идентификатор дочернего. Именно это мы и учитываем в программа из листинга 64.
Пояснения к программе из листинга 64.
Программа в листинге 64 является своего рода экспериментальной. По ее работе можно понять, как происходит разделение двух процессов: родительского и дочернего.
1. Обратим внимание на то место в программе, где вызывается системная функция fork. Именно с этого момента начинают параллельно работать два идентичного кода. Обратим внимание на переменную a. Ее значение задается в родительском коде. Вывод строки
До 89
Происходит только один раз, поскольку этот вывод осуществляется только в родительском процессе. С другой стороны вывод строки
После 89
происходит дважды в родительском и дочернем процессе. При этом значение переменной в обоих процессах одинаково.
2. Обращаем внимание на последовательность строк
cmpq $0, res
jnz l3
Именно в этой точке программы происходит «раздвоение кода» на код, который выполняется в родительском процессе (переход на метку l3) и на код, который выполняется в дочернем процессе (после указанных строк). Обратим также внимание на две системные функции getpid(номер 39) — получить идентификатор текущего процесса иgetppid(номер 110) — получить идентификатор родительского процесса. При этом в дочернем процессе получаем оба идентификатора с помощью функций getpid и getppid, а в родительском процессе с помощью функции getpid и переменно res, где хранится значение, возвращенное функцией fork — идентификатор дочернего процесса.
Для трансляции программы используем последовательность команд
as --64l73.s -ol73.o
gcc -cl72.c
gcc -no-piel73.o l72.o -ol73
Пример результата работы данной программы
До 89
После 89
Родительский 23696
Дочерний 23697
После 89
Дочерний 23697
Родительский 23696
Как видим, идентификаторы полученные из дочернего и родительского процесса совпадают. Однако при нескольких запусках с некоторой вероятностью оказывается, что у родительских процессов идентификаторы разные. И это очень важный результат. Происходит следующее. Родительский процесс заканчивает свою работу, а дочерний процесс еще работает. При этом родительским процессом для него становится системный процесс systemd. Вот именно идентификатор этого процесса выводится дочерним процессом на консоль.
На сегодня все. Подписываемся на мой канал Old Programmer и ставьте "лайки". А я продолжаю заниматься книгой Ассемблер для Linux 64.
<--Глава 6. Параграф 7.1
#программирование #программисты #ассемблер #assembler #языки программирования