Linux программирование в примерах
$ <b>cat data</b> /* Показать содержание демонстрационного файла */line 1line 2line 3line 4$ <b>ls -l test1 ; cat test1</b> /* Режим и содержание тестовой программы */-rwxr-xr-x 1 arnold devel 93 Oct 20 22:11 test1#! /bin/shread line ; echo p: $line /* Прочесть строку в родительской оболочке,вывести ее */( read line ; echo с: $line ) /* Прочесть строку в порожденной оболочке,вывести ее */read line ; echo p: $line /* Прочесть строку в родительской оболочке,вывести ее */$ <b>test1 < data</b> /* Запустить программу */p: line 1 /* Родитель начинает сначала */c: line 2 /* Порожденный продолжает оттуда, где остановился родитель */p: line 3 /* Родитель продолжает оттуда, где остановился порожденный */Первая исполняемая строка
читает из стандартного ввода строку, изменяя смещение файла. Следующая строкаtest1запускает команды, заключенные между скобками, в подоболочке (subshell). Это отдельный процесс оболочки, созданный — как вы догадались — с помощьюtest1. Порожденная подоболочка наследует от родителя стандартный ввод, включая текущее смещение. Этот процесс читает строку и обновляет разделяемое смещение в файле. Когда третья строка, снова в родительской оболочке, читает файл, она начинает там, где остановился порожденный.fork()Хотя команда
встроена в оболочку, все работает таким же образом и для внешних команд. В некоторых ранних Unix-системах была командаread, которая читала одну строку ввода (по одному символу за раз!) для использования в сценариях оболочки; если бы смещение файла не было разделяемым, было бы невозможно использовать такую команду в цикле.lineРазделение дескрипторов файлов и наследование играют центральную роль в перенаправлении ввода/вывода оболочки; системные вызовы и их семантика делают примитивы уровня оболочки простыми для реализации на С, как мы позже увидим в данной главе.
9.1.1.3. Разделение дескрипторов файлов и
close()Тот факт, что несколько дескрипторов файлов могут указывать на один и тот же открытый файл, имеет важное следствие: файл не закрывается до тех пор, пока не будут закрыты все дескрипторы файла.
Позже в главе мы увидим, что несколько дескрипторов для одного файла могут существовать не только для разных процессов, но даже и внутри одного и того же процесса; это правило особенно важно для работы с каналами (pipes).
Если вам нужно узнать, открыты ли два дескриптора для одного и того же файла, можете использовать
(см. раздел 5.4.2 «Получение сведений о файле») для двух дескрипторов с двумя различными структурамиfstat(). Если соответствующие поляstruct statиst_devравны, это один и тот же файл.st_inoПозже в главе мы завершим обсуждение манипуляций с дескрипторами файлов и таблицей дескрипторов файлов.
9.1.2. Идентификация процесса:
иgetpid()getppid()У каждого процесса есть уникальный ID номер процесса (PID). Два системных вызова предоставляют текущий PID и PID родительского процесса:
#include <sys/types.h> /* POSIX */#include <unistd.h>pid_t getpid(void);pid_t getppid(void);Функции так просты, как выглядят:
Возвращает PID текущего процессаpid_t getpid(void)Возвращает PID родителя.pid_t getppid(void)Значения PID уникальны; по определению, не может быть двух запущенных процессов с одним и тем же PID. PID обычно возрастают в значении, так что порожденный процесс имеет обычно больший PID, чем его родитель. Однако, на многих системах значения PID переполняются; когда достигается значение системного максимума для PID, следующий процесс создается с наименьшим не используемым номером PID. (Ничто в POSIX не требует такого поведения, и некоторые системы назначают неиспользуемые номера PID случайным образом.)
Если родительский процесс завершается, порожденный получает нового родителя,
. В этом случае PID родителя будет 1, что является PIDinit. Такой порожденный процесс называется висячим (orphan). Следующая программа,init, демонстрирует это. Это также первый примерch09-reparent.св действии:fork()1 /* ch09-reparent.c --- показывает, что getppid() может менять значения */23 #include <stdio.h>4 #include <errno.h>5 #include <sys/types.h>6 #include <unistd.h>78 /* main --- осуществляет работу */910 int main(int argc, char **argv)11 {12 pid_t pid, old_ppid, new_ppid;13 pid_t child, parent;1415 parent = getpid(); /* перед fork() */1617 if ((child = fork()) < 0) {18 fprintf(stderr, "%s: fork of child failed: %s\n",19 argv[0], strerror(errno));20 exit(1);21 } else if (child == 0) {22 old_ppid = getppid();23 sleep(2); /* см. главу 10 */24 new_ppid = getppid();25 } else {26 sleep(1);