Вход/Регистрация
Разработка приложений в среде Linux. Второе издание
вернуться

Троан Эрик В.

Шрифт:

Большая часть

struct jobSet
состоит из динамически распределенной памяти, которая должна быть освобождена по завершении задания. Первая функция в
ladsh1.с
,
freeJob
, освобождает память, использованную заданием.

Следующая функция,

getCommand
, получает команду, введенную пользователем, и возвращает строку. Если команды читаются из файла, то никакого приглашения не выводится (вот почему код сравнивает входной файловый поток со
stdin
).

parseCommand
разбивает строку команды в структуру
struct job
для использования в
ladsh
. Первый аргумент — это указатель на указатель на команду. Если в строке множество команд, он переставляется на начало следующей команды. Он устанавливается в
NULL
, когда завершается разбор последней команды в строке. Это позволяет
parseCommand
разбирать только одну команду при каждом вызове и дает возможность вызывающей функции просто разбирать строку за несколько вызовов. Следует отметить, что несколько программ, объединенных каналами, не рассматриваются как отдельные команды — независимыми друг от друга считаются только команды, разделенные символами
;
или
&
. Поскольку
parseCommand
— это просто пример разбора строк, мы не будем углубляться в детали ее работы.

Функция

runCommand
отвечает за запуск отдельного задания. Она принимает структуру
struct job
, описывающую запускаемое задание, список заданий, выполняющихся в данный момент, а также флаг, указывающий, должно ли задание выполняться в фоновом режиме или же на переднем плане.

Пока

ladsh
не поддерживает каналов, поэтому каждое задание может состоять только из одной программы (хотя большая часть инфраструктуры, поддерживающей каналы, уже присутствует в
ladsh1.с
). Если пользователь запускает
exit
, происходит немедленный выход из программы. Это пример встроенной команды, которую выполняет сама оболочка для обеспечения правильного поведения. Другая встроенная команда —
jobs
— также здесь реализована.

Если же команда не является встроенной, необходимо выполнить дочернюю команду. Поскольку каждое задание состоит только из одной программы (до тех пор, пока не будут реализованы каналы), это сделать достаточно просто.

219: if (!(newJob.progs[0].pid = fork)) {

220: execvp(newJob.progs[0].argv[0], newJob.progs[0].argv);

221: fprintf(stderr, "exec для %s потерпела неудачу: %s\n",

222: newJob.progs[0].argv[0],

223: strerror(errno));

224: exit(1);

225: }

Во-первых, с помощью

fork
порождается дочерний процесс. Родитель сохраняет идентификатор pid дочернего процесса в
newJob.progs[0].pid
, тогда как дочерний процесс сохраняет там
0
(помните, что родитель и потомок имеют разные образы памяти, хотя изначально они и содержат одинаковую информацию). В результате управление в дочернем процессе входит в тело оператора
if
, в то время как родитель пропускает его. Дочерний немедленно запускает новую программу с помощью вызова
execvp
. Если ему этот вызов не удается, печатается сообщение об ошибке и работа завершается. Это все необходимо, чтобы породить простой дочерний процесс.

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

Следующая функция,

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

1. Ищет задание, частью которого является процесс.

2. Помечает программу как завершенную (устанавливая сохраненный pid равным 0) и уменьшает количество работающих программ в задании на единицу.

Если задание, содержавшее завершенный процесс, не имеет других работающих процессов, что всегда верно для данной версии

ladsh
, оболочка печатает сообщение пользователю о том, что процесс завершен, и удаляет задание из списка фоновых процессов.

Процедура

main
из
ladsh1.с
контролирует поток управления оболочки. Если при ее запуске ей передан аргумент, он трактуется как имя файла, из которого нужно читать последовательность команд. В противном случае в качестве источника команд используется
stdin
. Затем программа игнорирует сигнал
SIGTTOU
. Это элемент "магии" управления заданиями, который обеспечивает, что все происходит гладко. Смысл этого будет пояснен в главе 15. Пока что это только скелет.

Остаток функции

main
составляет главный цикл программы. Условие выхода из цикла не предусмотрено. Программа завершается вызовом
exit
внутри функции
runCommand
.

Переменная

nextCommand
указывает на исходное (не разобранное) строковое представление следующей команды, которая должна быть выполнена, либо
NULL
, если команда должна быть прочитана из входного файла, коим обычно является
stdin
. Когда никакое задание не выполняется на переднем плане,
ladsh
вызывает
checkJobs
для проверки выполняющихся фоновых заданий, читает следующую команду из входного файла, если
nextCommand
равно
NULL
, затем разбирает и выполняет следующую команду.

  • Читать дальше
  • 1
  • ...
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: