Если вам нужно узнать, открыты ли два дескриптора для одного и того же файла, можете использовать
fstat
(см. раздел 5.4.2 «Получение сведений о файле») для двух дескрипторов с двумя различными структурами
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_t getpid(void)
Возвращает PID текущего процесса
pid_t getppid(void)
Возвращает PID родителя.
Значения PID уникальны; по определению, не может быть двух запущенных процессов с одним и тем же PID. PID обычно возрастают в значении, так что порожденный процесс имеет обычно больший PID, чем его родитель. Однако, на многих системах значения PID переполняются; когда достигается значение системного максимума для PID, следующий процесс создается с наименьшим не используемым номером PID. (Ничто в POSIX не требует такого поведения, и некоторые системы назначают неиспользуемые номера PID случайным образом.)
Если родительский процесс завершается, порожденный получает нового родителя,
init
. В этом случае PID родителя будет 1, что является PID
init
. Такой порожденный процесс называется висячим (orphan). Следующая программа,
ch09-reparent.с
, демонстрирует это. Это также первый пример
fork
в действии:
1 /* ch09-reparent.c --- показывает, что getppid может менять значения */
2
3 #include <stdio.h>
4 #include <errno.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7
8 /* main --- осуществляет работу */
9
10 int main(int argc, char **argv)
11 {
12 pid_t pid, old_ppid, new_ppid;
13 pid_t child, parent;
14
15 parent = getpid; /* перед fork */
16
17 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);
27 exit(0); /* родитель завершается после fork */
28 }
29
30 /*
это выполняет только порожденный процесс */
31 printf("Original parent: %d\n", parent);
32 printf("Child: %d\n", getpid);
33 printf("Child's old ppid: %d\n", old_ppid);
34 printf("Child's new ppid: %d\n", new_ppid);
35
36 exit(0);
37 }
Строка 15 получает PID начального процесса, используя
getpid
. Строки 17–20 создают порожденный процесс, проверяя по возвращении ошибки.
Строки 21–24 выполняются порожденным процессом: строка 22 получает PPID. Строка 23 приостанавливает процесс на две секунды (сведения о
sleep
см в разделе 10.8.1 «Аварийные часы:
sleep
,
alarm
и
SIGALRM
»), а строка 24 снова получает PPID.
Строки 25–27 исполняются в родительском процессе. Строка 26 задерживает родителя на одну секунду, давая порожденному процессу достаточно времени для осуществления первого вызова
getppid
. Строка 27 завершает родителя.
Строки 31–34 выводят значения. Обратите внимание, что переменная
parent
, которая была установлена до разветвления, сохраняет свое значение в порожденном процессе. После порождения у двух процессов идентичные, но независимые копии адресного пространства. Вот что происходит при запуске программы:
$ ch09-reparent /* Запуск программы */
$ Original parent: 6582 /* Программа завершается: приглашение оболочки
и вывод порожденного процесса */
Child: 6583
Child's old ppid: 6582
Child's new ppid: 1
Помните, что обе программы выполняются параллельно. Графически это изображено на рис. 9.2.
Рис. 9.2. Два параллельно исполняющихся процесса после разветвления
ЗАМЕЧАНИЕ. Использование
sleep
, чтобы заставить один процесс пережить другой, работает в большинстве случаев. Однако, иногда случаются ошибки, которые трудно воспроизвести и трудно обнаружить. Единственным способом гарантировать правильное поведение является явная синхронизация с помощью
wait
или
waitpid
, которые описываются далее в главе (см. раздел 9.1.6.1 «Использование функций POSIX:
wait
и
waitpid
»).
9.1.3. Установка приоритетов процесса:
nice
Когда процессы запущены, ядро динамически меняет приоритет каждого процесса. Как и в жизни, элементы с большим приоритетом получают внимание до элементов с меньшим приоритетом. Короче говоря, каждому процессу выделяется небольшая порция времени для исполнения, которая называется квантом времени (time slice). Когда квант истекает, если текущий процесс все еще является процессом с наивысшим приоритетом, ему разрешается продолжать.