Команды и командный интерпретатор
Оболочкой (shell) в системе UNIX называют механизм взаимодействия между пользователями и системой. По сути дела, это интерпретатор команд, который считывает набираемые пользователем строки и запускает выполнение запрошенных системных функций. Полный командный язык, интерпретируемый оболочкой, богат по возможностям и достаточно сложен, однако большинство команд просты в использовании и запомнить их не составляет труда.
Командная строка состоит из имени команды (то есть имени выполняемого файла), за которым следует список аргументов, разделённых пробелами. Оболочка разбивает командную строку на компоненты. Указанный в команде файл загружается, и ему обеспечивается доступ к заданным в команде аргументам.
Любой командный язык семейства shell фактически состоит из трёх частей:
¨ служебных конструкций, позволяющих манипулировать с текстовыми строками и строить сложные команды на основе простых команд;
¨ встроенных команд, выполняемых непосредственно интерпретатором командного языка;
¨ команд, представляемых отдельными выполняемыми файлами.
В свою очередь, набор команд последнего вида включает стандартные команды (системные утилиты, такие как vi, cc и т. д.) и команды, созданные пользователями системы. Для того чтобы выполняемый файл, разработанный пользователем ОС UNIX, можно было запускать как команду shell, достаточно определить в одном из исходных файлов функцию с именем main (имя main должно быть глобальным, то есть перед ним не должно указываться ключевое слово static). Если употребить в качестве имени команды имя такого выполняемого файла, командный интерпретатор создаст новый процесс и запустит в нём указанную выполняемую программу, начиная с вызова функции main.
Тело функции main, вообще говоря, может быть произвольным (для интерпретатора существенно только наличие входной точки в программу с именем main), но для того, чтобы создать команду, которой можно задавать параметры, нужно придерживаться некоторых стандартных правил. В этом случае каждая функция main должна определяться с двумя параметрами – argc и argv. После вызова команды параметру argc будет соответствовать число символьных строк, указанных в качестве аргументов вызова команды, а argv – массив указателей на переменные, содержащие эти строки. При этом имя самой команды составляет первую строку аргументов (то есть после вызова значение argc всегда больше или равно 1). Код функции main должен проанализировать допустимость заданного значения argc и соответствующим образом обработать заданные текстовые строки.
Например, следующий текст на языке С может быть использован для создания команды, которая выводит на экран текстовую строку, заданную в качестве её аргумента:
#include <stdio.h>
main(argc, argv)
{
int argc;
char* argv[ ];
if(argc!=2)
{
printf(“usage: %s your-text\n”, argv[0]);
exit;
}
printf(“%s\n”, argv[1]);
}
Процессы
Процесс в ОС UNIX понимается в классическом смысле этого термина, то есть как программа, выполняемая в собственном виртуальном адресном пространстве. Когда пользователь входит в систему, автоматически создается процесс, в котором выполняется программа командного интерпретатора. Если командному интерпретатору встречается команда, соответствующая выполняемому файлу, то он создает новый процесс и запускает в нём соответствующую программу, начиная с функции main. Эта запущенная программа, в свою очередь, может создать процесс и запустить в нём другую программу (она тоже должна содержать функцию main) и т. д.
Для образования нового процесса и запуска в нём программы используются два системных вызова API – fork( ) и ехес(имя_выполняемого_файла). Системный вызов fork приводит к созданию нового адресного пространства, состояние которого абсолютно идентично состоянию адресного пространства основного процесса (то есть в нём содержатся те же программы и данные). Для дочернего процесса заводятся копии всех сегментов данных.
Другими словами, сразу после выполнения системного вызова fork основной (родительский) и порожденный процессы являются абсолютными близнецами;
управление и в том и в другом находится в точке, непосредственно следующей за вызовом fork. Чтобы программа могла разобраться, в каком процессе она теперь работает – в основном или порождённом, функция fork возвращает разные значения: 0 в порождённом процессе и целое положительное число (идентификатор порождённого процесса – так называемый PID) в основном процессе.
Теперь, если мы хотим запустить новую программу в порождённом процессе, нужно обратиться к системному вызову ехес, указав в качестве аргументов вызова имя файла, содержащего новую выполняемую программу, и, возможно, одну или несколько текстовых строк, которые будут переданы в качестве аргументов функции main новой программы. Выполнение системного вызова ехес приводит к тому, что в адресное пространство порожденного процесса загружается новая выполняемая программа и запускается с адреса, соответствующего входу в функцию main. Другими словами, это приводит к замене текущего программного сегмента и текущего сегмента данных, которые были унаследованы при выполнении вызова fork, на новые соответствующие сегменты, заданные в файле. Прежние сегменты теряются. Это эффективный метод смены выполняемой процессом программы, но не самого процесса. Файлы, уже открытые до выполнения примитива ехес, остаются открытыми после его выполнения.
В следующем примере пользовательская программа, вызываемая как команда shell, выполняет в отдельном процессе стандартную команду shell ls, которая выдаёт на экран содержимое текущего каталога файлов.
main( )
{
if (fork ( )==(0) wait(0); /* родительский процесс */
else execl('ls", "Is", 0); /* порождённый процесс */
}
Таким образом, с практической точки зрения процесс в UNIX является объектом, создаваемым в результате выполнения функции fork( ). Каждый процесс, за исключением начального (нулевого), порождается в результате запуска другим процессом операции fork( ). Каждый процесс имеет одного родителя, но может породить много процессов. Начальный (нулевой) процесс является особенным процессом, который создается в результате загрузки системы. После порождения нового процесса с идентификатором 1 нулевой процесс становится процессом подкачки и реализует механизм виртуальной памяти. Процесс с идентификатором 1, известный под именем init, является предком любого другого процесса в системе и связан с каждым процессом особым образом.
Дата добавления: 2022-02-05; просмотров: 287;