15
Для многопоточных приложений библиотека хранит код ошибки там, где функция errno(), которой известно, какой поток является текущим, может получить ее. Разные потоки могут содержать разные текущие коды возврата ошибок.
16
uid и gid обычно представляют собой положительные целые, но отрицательные целые тоже имеют определенное назначение. Применение -1 для идентификатора проблематично, однако многие системные вызовы, работающие с uid и gid, используют -1 в качестве признака, что модифицировать значение не нужно (см. пример этого в setregid() далее в главе).
17
Процессы Linux также имеют четвертый uid и gid, используемые для файлового доступа. Они обсуждаются в следующем разделе этой главы.
18
Большинство систем передают окружение в виде параметра main(), но такой метод не включен в стандарт POSIX. Переменная environ — это метод, утвержденный POSIX.
19
Детальную информацию о том, как родительские и дочерние открытые файлы соотносятся друг с другом, можно найти в главе 11.
20
Группы процессов рассматриваются далее в этой главе
21
В главе 15 описаны причины, по которым это может произойти.
22
Это тот же формат, который использует команда env для печати текущих значений переменных окружения, и аргумент envp имеет тот же тип, что и глобальная переменная environ.
23
Технически это указатель на завершающийся NULL массив указателей на массивы символов, каждый из которых завершается символом ' '. Более подробно об это рассказано в [15].
24
Появление vfork() было мотивировано старыми системами, которым необходимо было копировать всю память, используемую исходным процессом, как часть fork().Современные операционные системы используют копирование при записи, которое копирует области памяти только по необходимости, как это описано во многих источниках, посвященных операционным системам, в частности [40] и [2]. Это свойство делает fork() почти таким же быстрым, как vfork(), и намного более простым в использовании.
25
Это — существенное упрощение. В действительности kill() посылает сигнал, а сигналы сами по себе достаточно сложная тема. См. полное описание того, что такое сигналы и как их применять, в главе 12.
26
Это нужно для того, чтобы управляющая заданиями оболочка могла перезапускать процессы, у которых изменился эффективный идентификатор пользователя. Более подробно об управлении заданиями рассказывается в главе 15.
27
Одна из популярных ранее форм компьютерной памяти выглядела как набор маленьких железных колечек, расположенных на матрице, к каждому из которых подводились два проводка, служащих для установки и считывания магнитной полярности кольца. Эти кольца назывались ядрами (cores), а все вместе — ядерной памятью. Поэтому дамп ядра — это копия состояния системной памяти в определенный момент времени.
28
В процессе работы system() блокирует SIGCHILD, что заставляет передавать этот сигнал программе непосредственно перед тем, как system() вернет управление (но после того, как system() вызовет wait() для порожденного процесса), поэтому программы, которые используют обработчики сигналов, должны это учитывать и обрабатывать такие ложные сигналы осторожно. Функция system() также игнорирует SIGINT и SIGQUIT, а это означает, что быстрые циклические повторные вызовы system() может оказаться невозможно прервать ничем, кроме SIGSTOP и SIGKILL.
29
Хотя функция popen() это делает просто, с ней связаны некоторые побочные эффекты, которые не сразу становятся очевидны. Она создает дочерний процесс, который может быть прерван перед тем, как будет вызвана pclose(), что заставит функцию wait() вернуть состояние процесса. Когда этот процесс завершится, он также сгенерирует SIGCHLD, что может привести в замешательство упрощенно написанный обработчик сигналов.
30
Этот тип обработки часто приводит к взаимоблокировкам, при которых процесс А ожидает, пока процесс В выполнит какую-то работу, в то время как процесс В ожидает процесса А, в результате чего ничего не происходит.
31
Если вам понадобится делать это, запустите дочерний процесс с помощью fork() и exec(), а потом воспользуйтесь poll() для чтения и записи в дочерний процесс. Для этого предназначена программа под названием expect.
32
Информацию о чтении и записи в поток stdio можно найти в [15].
33
В главе 15 все это проясняется, поэтому, возможно, будет полезно сначала прочитать ее.
34
Но, может быть, не сразу. Может существовать группа процессов, содержащая процессы, чьи родители относятся к другой группе в том же сеансе. Поскольку отношения "родительский-дочерний" между процессами образуют дерево, иногда может оказаться, что существует группа процессов, которая содержит только те процессы, чьим родителем является оболочка, и когда такая группа процессов завершается, другая становится висячей.
35
Обсуждение станет более понятным после того, как вы прочитаете главы, посвященные сигналам (глава 12) и управлению заданиями (глава 15).
36
В среде Linux файловая система /proc включает информацию о каждом файле, открытом в системе в данный момент. Хотя это значит, что неименованные каналы могут быть найдены в файловой системе, все же они не имеют постоянных имен, потому что исчезают при завершении процесса, использующего их.
37
Не все блочные устройства представляют реальное оборудование. Более правильное описание блочных устройств — это нечто, на чем может располагаться файловая система; блочное устройство обратной связи (loopback block device) отображает на обычный файл логическое блочное устройство, что позволяет файлам содержать в себе целые файловые системы.
38
Точнее говоря, они кэшируются и доступ к ним упорядочен.
39
Это отличается от ряда систем, которые способны монтировать файловые системы как на символьных устройствах, так и на блочных.
40
В Linux всегда используется термин inode для обоих типов информационных узлов, в то время, как другие варианты Unix резервируют термин inode только для дисковых узлов, а узлы в памяти называют vnode. Хотя такая терминология менее запутана, мы будем использовать термин inode для обоих типов узлов, чтобы сохранять соответствие стандартам Linux.
41
Это режим, обычно используемый для каталога /tmp.
42
readv(), writev() и mmap() обсуждаются в главе 13; sendmsg() и recvmsg() упоминаются в главе 17.
43
Хотя такое разделение почти ясно, сокеты TCP поддерживают "внеполосные" данные, что несколько усложняет ситуацию. Такие данные выходят за пределы тем, рассматриваемых в этой книге. Их полное описание можно найти в [33].
44
Почти независимую; см. описание исключений из этого правила в дискуссии о dup() в конце этой главы.
45
Поскольку в большинстве систем SEEK_SET определена как 0, часто можно увидеть использование lseek(fd, offset, 0) вместо lseek(fd, offset, SEEK_SET). Это делает код непереносимым (или плохо читабельным), чем SEEK_SET, но подобное часто встречается в старом коде.
46
Все же не всегда. Если процессы разделяют файловые дескрипторы (имеются в виду дескрипторы, полученные от одного вызова open()),эти процессы разделяют одни и те же файловые структуры и одно и тоже текущее положение. Наиболее часто такое случается после вызова fork(),как обсуждается в конце этой главы. Другая ситуация, когда такое может случиться — это если файловый дескриптор передается другому процессу через доменный сокет Unix, описанный в главе 17.
47
Она так называется потому, что это протоколируемая версия Second Extended File System (второй расширенной файловой системы), которая была наследницей Linux Extended File System, которая, в свою очередь, была спроектирована как более сложная файловая система, чем файловая система Minix — единственная, которую изначально поддерживалась в Linux.