/*
* Safer version of system for interactive programs
*/
#include
#include
system(s) /* run command line s */
char *s;
{
int status, pid, w, tty;
int (*istat)(), (*qstat)();
extern char *progname;
fflush(stdout);
tty = open("/dev/tty", 2);
if (tty == -1) {
fprintf(stderr, "%s: can't open /dev/tty\n", progname);
return -1;
}
if ((pid = fork()) == 0) {
close(0);
dup(tty);
close(1);
dup(tty);
close(2);
dup(tty);
close(tty);
execlp("sh", "sh", "-c", s, (char*)0);
exit(127);
}
close(tty);
istat = signal(SIGINT, SIG_IGN);
qstat = signal(SIGQUIT, SIG_IGN);
while ((w = wait(&status)) != pid && w != -1)
;
if (w == -1)
status = -1;
signal(SIGINT, istat);
signal(SIGQUIT, qstat);
return status;
}
Отметим, что /dev/tty
открыта с режимом 2 — чтение и запись. С помощью dup
формируются стандартный входной и выходной потоки. Здесь можно провести аналогию со сборкой системой стандартных входного и выходного потоков и потока ошибок, когда вы в нее входите. Поэтому в ваш стандартный входной поток можно писать:
$ echo hello 1>&0
hello
$
Это означает, что вам следует применить dup
к дескриптору файла 2, чтобы вновь связать стандартные ввод и вывод, но открытие /dev/tty
является более естественным и безопасным. Даже system
имеет потенциальные проблемы: открытые файлы в вызывающей программе, такие, как tty
в подпрограмме ttin
программы p
, будут передаваться процессу-потомку.
Смысл изложенного выше состоит не в том, что вы должны использовать нашу версию system
для своих программ (она могла бы разрушить недиалоговый ed
, например), а в том, чтобы понять, как управляют процессами и корректно используют примитивы; значение слова "корректно" меняется в зависимости от приложения и может быть не согласовано со стандартной реализацией system
.
Теперь мы рассмотрим работу с сигналами извне (такими, как прерывания) и ошибками программы. Последние возникают главным образом из-за некорректных обращений к памяти, выполнения привилегированных команд или при выполнении операций с плавающей запятой. Наиболее распространенными внешними сигналами являются прерывание , посылаемый при печати символа del , выйти , генерируемый символом FS ( ctrl-\ ), отбой , вызываемый завершением телефонной связи, и закончить, генерируемый командой kill
. Когда происходит одно из этих событий, посылается сигнал всем процессам, запущенным с того же терминала, и если не были приняты другие меры, процесс завершается. Для большинства сигналов пишется файл образа памяти, который может потребоваться при поиске ошибок (см. справочное руководство по adb(1)
, sdb(1)
).
Системный вызов signal
изменяет действие, заданное по умолчанию. Он имеет два аргумента: номер, определяющий сигнал, и адрес функции или код, предписывающий игнорировать сигнал либо запустить процедуру, принятую по умолчанию. Файл содержит определения для различных аргументов. Так,
#include
signal(SIGINT, SIG_IGN);
Специфицирует игнорирование прерываний, тогда как
signal(SIGINT, SIG_DEL);
восстанавливает действие по умолчанию, означающее завершение процесса. В любом случае signal
возвращает предыдущее значение сигнала. Если второй аргумент signal
представляет собой имя функции, которая уже должна быть описана в том же самом исходном файле, то функция будет вызвана, когда возникнет сигнал. Это практикуется довольно часто, чтобы программа могла "подчищать" неоконченные работы перед своим завершением, например удалять временный файл:
#include
char *tempfile = "temp.xxxxxx";
main() {
extern onintr();
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, onintr);
mktemp(tempfile);
/* Process ... */
exit(0);
}
onintr() { /* почистить, если прервано */
unlink(tempfile);
exit(1);
}
Почему в main
имеют место проверки и двойной вызов signal
? Вспомните, что сигналы посылаются всем процессам, запущенным с данного терминала. Соответственно если программа должна быть запущена не в диалоговом режиме (с помощью &
), shell
делает так, что она будет игнорировать прерывания. Поэтому сигналы прерывания, посланные основным процессам, не остановят ее. Если бы эта программа началась с объявления о том, что все прерывания, которые должны быть посланы подпрограмме onintr
, не принимаются во внимание, были бы сведены на нет все усилия shell
защитить ее при запуске в фоновом режиме.
Читать дальше
Конец ознакомительного отрывка
Купить книгу