Читаем UNIX полностью

Если программа вызывается из shell, три дескриптора файла, 0, 1 и 2, ссылаются на соответствующие файлы, и все остальные дескрипторы доступны для использования. Когда эта программа вызывает другую, в соответствии с профессиональной этикой указанные условия должны быть соблюдены. Ни fork, ни exec не влияют никоим образом на открытые файлы; оба процесса, родитель и потомок, имеют одни и те же открытые файлы. Если процесс-родитель буферизует выходной поток, который необходимо вывести до процесса-потомка, родитель должен очистить свой буфер ранее execlp. И, наоборот, при буферизации родителем входного потока потомок потеряет информацию, которая читалась родителем. Выходной поток может быть выведен, но входной нельзя "положить назад". Обе ситуации являются следствием реализации входного или выходного потока стандартной библиотекой ввода-вывода, обсуждавшейся в гл. 6, поскольку при этом и ввод, и вывод буферизуются обычным образом.

Именно свойство наследования дескрипторов файлов через execlp используется в system: если у вызывающей программы стандартные входной и выходной потоки не связаны с терминалом, то этим же свойством обладает команда, вызванная из system. Возможно, такой вариант нам и нужен. В списке команд редактора ed, например, входной поток команды, начинающейся с символа !, вероятно, должен поступить из того же списка. Даже тогда ed должен считывать из своего входного потока по одному символу во избежание возникновения проблем буферизации ввода.

Для диалоговых программ, подобных p, system должна тем не менее вновь связать стандартный входной и выходной потоки с терминалом, в частности /dev/tty.

Системный вызов dup(fd) дублирует дескриптор файла fd на незанятый дескриптор файла с наименьшим номером и возвращает новый дескриптор, ссылающийся на тот же самый открытый файл. Следующая программа "присоединяет" стандартный входной поток программы к файлу:

int fd;

fd = open("file", 0);

close(0);

dup(fd);

close(fd);

Вызов close(fd) освобождает дескриптор файла 0 (стандартный входной поток), но, как правило, не влияет на процесс-родитель. Здесь приведена наша версия system для диалоговых программ, использующая progname для вывода сообщений об ошибках. Вам следует игнорировать те части функции, которые имеют дело с сигналами (мы вернемся к ним позднее).

/*

 * 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.

<p>7.5 Сигналы и прерывания</p>
Перейти на страницу:

Похожие книги