
- •Рекомендации по написанию командных интерпретаторов для ос unix
- •Void c_handl(int num) //обработчик sigchld
- •4. Терминальный ввод/вывод
- •Int cterm, //управляющий терминал
- •5. Запуск внешних программ
- •6. Ожидание завершения фоновых программ
- •7. Анализ статуса (кода) завершения программ
- •8. Управление заданиями
- •Приложения:
- •Задания на курсовую работу по дисциплине "спо"
6. Ожидание завершения фоновых программ
При завершении процесса система сохраняет код завершения в специальном поле
его контекста и посылает его родителю сигнал SIGCHLD. Код завершения
процесса, как правило, должен быть считан его родителем или процессом init
в случае 'осиротевших' процессов. Считывание кода завершения производится
функциями семейства wait(). Если родительский процесс жив, но не ожидает
кода завершения потомка, то завершившийся потомок становится процессом-зомби.
Зомби не потребляет системных ресурсов, так как это всего-лишь оставшийся
от процесса описатель в таблице процессов. Для того, чтобы интерпретатор
мог получить код завершения каждого фонового процесса, можно использовать
вызов waitpid() в обработчике сигнала SIGCHLD. Учтите, что SIGCHLD может
также быть сгенерирован при остановке потомка.
7. Анализ статуса (кода) завершения программ
Статус завершения, возвращаемый функциями wait(), содержит код, передаваемый
вызовом _exit(), флаги, уточняющие причину завершения и номер полученного
сигнала, если он был причиной завершения процесса.
Обычно статус завершения анализируется с помощью макросов, описанных в
файле <sys/wait.h>, например:
WIFEXITED(stat) - возвращает истину (не ноль), если процесс завершился по
своей инициативе (достигнут конец функции main(), вызван
exit() или return() в main())
WEXITSTATUS(stat) - возвращает младший байт кода завершения, возвращенного
пользовательской программой, если процесс завершился
WIFSIGNALED(stat) - возвращает истину, если причиной завершения был сигнал
WTERMSIG(stat) - если WIFSIGNALED возвратил истину, то возвращает номер
сигнала, приведшего к завершению
WCOREDUMP(stat) - возвращает истину, если был создан посмертный дамп памяти
процесса
WIFSTOPPED(stat) - возвращает истину, если процесс был остановлен
8. Управление заданиями
Для того, чтобы интерпретатор мог контролировать все порожденные им процессы
в нем должна использоваться внутренняя таблица процессов. Управление процессами
осуществляется с помощью посылки сигналов и изменения принадлежности управляющего
терминала. Для безусловной остановки процесса ему нужно послать сигнал SIGSTOP,
а для возобновления его выполнения - сигнал SIGCONT. Сигналы SIGTSTP, SIGTTIN и
SIGTTOU останавливают процесс, если они не замаскированы или не перехватываются.
Последовательность перевода процесса из фонового режима в интерактивный могла бы
выглядеть так:
Пример: Изменение режима выполнения (протестирован под Linux 6.2).
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
int cterm; //управляющий терминал
pid_t shellpid;
...
//переводим фоновый или остановленный процесс на передний план
int bg2fg(pid_t chpid, int stopped)
{
int stat;
//здесь можно сохранить настройки терминала
...
tcsetpgrp(cterm,chpid); //отдаем фоновому процессу терминал
if(stopped) //если процесс был остановлен
kill(pid_t chpid, SIGCONT); //возобновляем его выполнение
waitpid(chpid,&stat,WUNTRACED); //ожидаем завершения или
//остановки процесса
//здесь можно восстановить принадлежность и параметры
//управляющего терминала
...
return(stat);
}
9. Перенаправление ввода/вывода
Интерпретатор может закрыть или перенаправить стандартные потоки ввода,
вывода и вывода сообщений об ошибках для запускаемой программы.
Например, для перенаправления стандартного потока вывода в порожденном
процессе до вызова exec() необходимо закрыть стандартный вывод и затем сразу
открыть на запись или дополнение указанный в командной строке файл. В
результате открытый файл получит младший из доступных дескрипторов, то есть
номер 1, ассоциированный со стандартным потоком вывода. Если необходимо
реализовать конвейер команд, то используется тот же прием, но вместо файла
открывается программный канал. В конвейере процессы связаны отношением
писатель-читатель. Интерпретатор создает для каждой пары писатель-читатель
неименованный программный канал, затем, перед вызовом exec() в писателе
закрывается стандартный поток вывода и дублируется дескриптор канала для
записи, а в читателе - закрывается поток ввода и дублируется дескриптор канала
для чтения. В описанных выше операциях используются системные вызовы open(),
close(), pipe(), dup() и dup2().