$ ls -l test1 ; cat test1 /* Режим и содержание тестовой программы */
-rwxr-xr-x 1 arnold devel 93 Oct 20 22:11 test1
#! /bin/sh
read line ; echo p: $line /* Прочесть строку в родительской оболочке,
вывести ее */
( read line ; echo с: $line ) /* Прочесть строку в порожденной оболочке,
вывести ее */
read line ; echo p: $line /* Прочесть строку в родительской оболочке,
вывести ее */
$ test1 < data /* Запустить программу */
p: line 1 /* Родитель начинает сначала */
c: line 2 /* Порожденный продолжает оттуда, где остановился родитель */
p: line 3 /* Родитель продолжает оттуда, где остановился порожденный */
Первая исполняемая строка test1
читает из стандартного ввода строку, изменяя смещение файла. Следующая строка test1
запускает команды, заключенные между скобками, в подоболочке (subshell). Это отдельный процесс оболочки, созданный — как вы догадались — с помощью fork()
. Порожденная подоболочка наследует от родителя стандартный ввод, включая текущее смещение. Этот процесс читает строку и обновляет разделяемое смещение в файле. Когда третья строка, снова в родительской оболочке, читает файл, она начинает там, где остановился порожденный.
Хотя команда read
встроена в оболочку, все работает таким же образом и для внешних команд. В некоторых ранних Unix-системах была команда line
, которая читала одну строку ввода (по одному символу за раз!) для использования в сценариях оболочки; если бы смещение файла не было разделяемым, было бы невозможно использовать такую команду в цикле.
Разделение дескрипторов файлов и наследование играют центральную роль в перенаправлении ввода/вывода оболочки; системные вызовы и их семантика делают примитивы уровня оболочки простыми для реализации на С, как мы позже увидим в данной главе.
9.1.1.3. Разделение дескрипторов файлов и close()
Тот факт, что несколько дескрипторов файлов могут указывать на один и тот же открытый файл, имеет важное следствие:
Позже в главе мы увидим, что несколько дескрипторов для одного файла могут существовать не только для разных процессов, но даже и внутри одного и того же процесса; это правило особенно важно для работы с каналами (pipes).
Если вам нужно узнать, открыты ли два дескриптора для одного и того же файла, можете использовать fstat()
(см. раздел 5.4.2 «Получение сведений о файле») для двух дескрипторов с двумя различными структурами struct stat
. Если соответствующие поля st_dev
и st_ino
равны, это один и тот же файл.
Позже в главе мы завершим обсуждение манипуляций с дескрипторами файлов и таблицей дескрипторов файлов.
9.1.2. Идентификация процесса: getpid()
и getppid()
У каждого процесса есть уникальный ID номер процесса (PID). Два системных вызова предоставляют текущий PID и PID родительского процесса:
#include
#include
pid_t getpid(void);
pid_t getppid(void);
Функции так просты, как выглядят:
pid_t getpid(void)
Возвращает PID текущего процесса
pid_t getppid(void)
Возвращает PID родителя.
Значения PID уникальны; по определению, не может быть двух запущенных процессов с одним и тем же PID. PID обычно возрастают в значении, так что порожденный процесс имеет обычно больший PID, чем его родитель. Однако, на многих системах значения PID
Если родительский процесс завершается, порожденный получает нового родителя, init
. В этом случае PID родителя будет 1, что является PID init
. Такой порожденный процесс называется ch09-reparent.с
, демонстрирует это. Это также первый пример fork()
в действии:
1 /* ch09-reparent.c --- показывает, что getppid() может менять значения */
2
3 #include
4 #include
5 #include
6 #include
7