Марк Митчелл - Программирование для Linux. Профессиональный подход
- Название:Программирование для Linux. Профессиональный подход
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:2002
- Город:Москва
- ISBN:5-8459-0243-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Марк Митчелл - Программирование для Linux. Профессиональный подход краткое содержание
Данная книга в основном посвящена программированию в среде GNU/Linux. Авторы применяют обучающий подход, последовательно излагая самые важные концепции и методики использования расширенных возможностей системы GNU/Linux в прикладных программах. Читатели научатся писать программы, к интерфейсу которых привыкли пользователи Linux; освоят такие технологии, как многозадачность, многопотоковое программирование, межзадачное взаимодействие и взаимодействие с аппаратными устройствами; смогут улучшить свои программы, сделав их быстрее, надежнее и безопаснее; поймут особенности системы GNU/Linux, ее ограничения, дополнительные возможности и специфические соглашения.
Книга предназначена для программистов, уже знакомых с языком С и имеющих базовый опыт работы в GNU/Linux.
Программирование для Linux. Профессиональный подход - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Что же всё-таки случится, если родительский процесс не удалит своих потомков? Они останутся в системе в виде зомби. Программа, показанная в листинге 3.6, порождает дочерний процесс, который немедленно завершается, тогда как родительский процесс берет минутную паузу, после чего тоже заканчивает работу, так и не позаботившись об удалении потомка.
#include «stdlib.h>
#include
#include
int main() {
pid_t child_pid;
/* Создание дочернего процесса. */
child_pid = fork();
if (child_pid > 0) {
/* Это родительский процесс — делаем минутную паузу. */
sleep(60);
} else {
/* Это дочерний процесс — немедленно завершаем работу. */
exit(0);
}
return 0;
}
Скомпилируйте этот файл и запустите программу. Пока программа работает, перейдите в другое окно и просмотрите список процессов с помощью следующей команды:
% ps -е -o pid,ppid,stat,cmd
Эта команда отображает идентификатор самого процесса и его предка, а также статус процесса и его командную строку. Обратите внимание на присутствие двух процессов с именем zombie
. Один из них — предок, другой — потомок. У последнего идентификатор родительского процесса равен идентификатору основного процесса zombie
, при этом потомок обозначен как (несуществующий), а его код состояния равен Z (т.е. zombie — зомби).
Итак, мы хотим узнать, что будет, когда программа zombie
завершится, не вызвав функцию wait()
. Останется ли процесс-зомби? Нет — выполните команду ps
и убедитесь в этом: оба процесса zombie
исчезли. Дело в том, что после завершения программы управление ее дочерними процессами принимает на себя специальный процесс — демон init
, который всегда работает, имея идентификатор 1 (это первый процесс, запускаемый при загрузке Linux). Демон init
автоматически удаляет все унаследованные им дочерние процессы-зомби.
3.4.4. Асинхронное удаление дочерних процессов
Если дочерний процесс просто вызывает другую программу с помощью функции exec()
, то в родительском процессе можно сразу же вызвать функцию wait()
и пассивно дожидаться завершения потомка. Но очень часто нужно, чтобы родительский процесс продолжал выполняться одновременно с одним или несколькими своими потомками. Как в этом случае получать сигналы об их завершении?
Один подход заключается в периодическом вызове функции wait3()
или wait4()
. Функция wait()
в данной ситуации не подходит, так как в случае отсутствия завершившегося дочернего процесса она заблокирует основную программу. А вот упомянутые две функции принимают дополнительный флаг WNOHANG
, переводящий их в неблокируемый режим, в котором функция либо удаляет дочерний процесс, если он есть, либо просто завершается. В первом случае возвращается идентификатор процесса, во втором — 0.
Более элегантный подход состоит в асинхронном уведомлении родительского процесса о завершении потомка. Существуют разные способы сделать это, но проще всего воспользоваться сигналом SIGCHLD
, посылаемым как раз тогда, когда завершается дочерний процесс. По умолчанию программа никак не реагирует на этот сигнал, поэтому раньше вы могли и не знать о его существовании.
Таким образом, нужно организовать удаление дочерних процессов в обработчике сигнала SIGCHLD
. Естественно, код состояния удаляемого процесса следует сохранять в глобальной переменной, если эта информация необходима основной программе. В листинге 3.7 показана программа, в которой реализована данная методика.
SIGCHLD
#include
#include
#include
#include
sig_atomic_t child_exit_status;
void clean_up_child_process(int signal_number) {
/* Удаление дочернего процесса. */
int status;
wait(&status);
/* Сохраняем статус потомка в глобальной переменной. */
child_exit_status = status;
}
int main() {
/* Обрабатываем сигнал SIGCHLD, вызывая функцию
clean_up_child_process(). */
struct sigaction sigchld_action;
memset(&sigchld_action, 0, sizeof(sigchld_action));
sigchld_action.sa_handler = &clean_up_child_process;
sigaction(SIGCHLD, &sigchld_action, NULL);
/* Далее выполняются основные действия, включая порождение
дочернего процесса. */
/* ... */
return 0;
}
Глава 4
Потоки
Потоки, как и процессы, — это механизм, позволяющий программам выполнять несколько действий одновременно. Потоки работают параллельно. Ядро Linux планирует их работу асинхронно, прерывая время от времени каждый из них, чтобы дать шанс остальным.
С концептуальной точки зрения поток существует внутри процесса, являясь более мелкой единицей управления программой. При вызове программы Linux создает для нее новый процесс, а в нем — единственный поток, последовательно выполняющий программный код. Этот поток может создавать дополнительные потоки. Все они находятся в одном процессе, выполняя ту же самую программу, но, возможно, в разных ее местах.
Мы уже знаем, как программа порождает дочерний процесс. Первоначально он находится в родительской программе, получая копии ее виртуальной памяти, дескрипторов файлов и т.п. Модификация содержимого памяти, закрытие файлов и другие подобные действия в дочернем процессе не влияют на работу родительского процесса и наоборот. С другой стороны, когда программа создает поток, ничего не копируется. Оба потока — старый и новый — имеют доступ к общему виртуальному пространству, общим дескрипторам файлов и другим системным ресурсам. Если, к примеру, один поток меняет значение переменной, это изменение отражается на другом потоке. Точно так же, когда один поток закрывает файл, второй поток теряет возможность работать с этим файлом. В связи с тем что процесс и все его потоки могут выполнять лишь одну программу одновременно, как только одни из потоков вызывает функцию семейства exec()
, все остальные потоки завершаются (естественно, новая программа может создавать собственные потоки).
В Linux реализована библиотека API-функций работы с потоками, соответствующая стандарту POSIX (она называется Pthreads). Все функции и типы данных библиотеки объявлены в файле . Эти функции не входят в стандартную библиотеку языка С, поэтому при компоновке программы нужно указывать опцию -lpthread
в командной строке.
4.1. Создание потока
Каждому потоку в процессе назначается собственный идентификатор. При ссылке на идентификаторы потоков в программах, написанных на языке С или C++, нужно использовать тип данных pthread_t
.
Интервал:
Закладка: