Чтение онлайн

ЖАНРЫ

Программирование для Linux. Профессиональный подход

Самьюэл Алекс

Шрифт:
Листинг 4.15. (thread-pid.c) Вывод идентификаторов потоков

#include <pthread.h>

#include <stdio.h>

#include <unistd.h>

void* thread_function(void* arg) {

 fprintf(stderr, "child thread pid is %d\n", (int) getpid);

 /* Бесконечный цикл. */

 while (1);

 return NULL;

}

int main {

 pthread_t thread;

 fprintf(stderr, "main thread pid is %d\n", (int)getpid);

 pthread_create(&thread, NULL, &thread_function, NULL);

 /*
Бесконечный цикл. */

 while (1);

 return 0;

}

Запустите программу в фоновом режиме, а затем вызовите команду

ps x
, чтобы увидеть список выполняющихся процессов. Не забудьте затем уничтожить программу
thread-pid
, так как она потребляет ресурсы процессора. Вот что мы получим:

% cc thread-pid.c -о thread-pid -lpthread

% ./thread-pid &

[1] 14608

main thread pid is 14608

child thread pid is 14610

% ps x

PID TTY STAT TIME COMMAND

14042 pts/9 S 0:00 bash

14068 pts/9 R 0:01 ./thread-pid

14069 pts/9 S 0:00 ./thread-pid

14610 pts/9 R 0:01 ./thread-pid

14611 pts/9 R 0:00 ps x

% kill 14608

[1]+ Terminated ./thread-pid

Сообщения интерпретатора команд» касающиеся управления заданиями

Строки, начинающиеся с записи

[1]
, поступают от интерпретатора команд. Если программа запускается в фоновом режиме, интерпретатор назначает ей номер задания — в данном случае 1 — и сообщает ее идентификатор. Когда фоновое задание завершается, интерпретатор сообщает об этом при вызове первой же команды

Обратите внимание на то, что программе

thread-pid
соответствуют три процесса. Первый из них, с идентификатором 14608, — это основной поток программы. Третий, с идентификатором 14610, — это дочерний поток, выполняющий функцию
thread_function
. Что же такое тогда второй поток, с идентификатором 14609? Это "управляющий поток", являющийся частью внутреннего механизма реализации потоков в Linux. Он создается, когда программа вызывает функцию
pthread_create
.

4.5.1. Обработка сигналов

Предположим, что многопотоковая программа принимает сигнал. В каком потоке будет вызван обработчик сигнала? Это зависит от версии UNIX. В Linux поведение программы объясняется тем. что потоки на самом деле реализуются в виде процессов.

Каждый поток в Linux является отдельным процессом, а сигнал доставляется конкретному процессу, поэтому никакой неоднозначности на самом деле нет. Обычно сигнал, поступающий от внешней программы, посылается процессу, управляющему главным потоком программы. Например, если программа с помощью функции

fork
делится на два процесса и дочерний процесс запускает многопотоковую программу, в родительском процессе будет храниться идентификатор
главного потока дочернего процесса, и этот идентификатор будет включаться во все сигналы, посылаемые от предка потомку. Этим правилом следует руководствоваться при написании многопотоковых программ для Linux.

Тем не менее подобная особенность реализации библиотеки Pthreads в Linux не согласуется со стандартом POSIX. Нельзя полагаться на нее в программах, рассчитанных на то, чтобы быть переносимыми.

В многопотоковой программе один поток может послать сигнал другому. Для этого предназначена функция

pthread_kill
. Ее первым параметром является идентификатор потока, а второй параметр — это номер сигнала.

4.5.2. Системный вызов clone

Все потоки, создаваемые в одной программе, являются отдельными процессами, которые делят общее адресное пространство и другие ресурсы. Но дочерний процесс, создаваемый с помощью функции

fork
, получает в свое распоряжение копии ресурсов. Как же реализуются процессы первого типа?

В Linux имеется функция

clone
, являющаяся обобщением функций
fork
и
pthread_create
. Она позволяет вызывающему процессу указывать, какие ресурсы он согласен делить с дочерним процессом. Необходимо также задать область памяти, в которой будет расположен стек выполнения нового процесса. Вообще говоря, мы упоминаем функцию
clone
лишь для того, чтобы удовлетворить любопытство читателей. Использовать ее в программах не следует. Создавайте процессы с помощью функции
fork
, а потоки — с помощью функции
pthread_create
.

4.6. Сравнение процессов и потоков

В некоторых программах, связанных с параллельным выполнением операций, сделать выбор в пользу процессов или потоков может оказаться достаточно сложно. Приведем рад правил, которые помогут читателям выбрать наилучшую модель для своих программ.

■ Все потоки программы должны выполнять один и тот же код. В то же время дочерний процесс может запустить другой исполняемый файл с помощью функции

exec
.

■ Неправильно работающий поток способен помешать другим потокам того же процесса, поскольку все они используют одни и те же ресурсы. Например, неверное обращение к указателю может привести к искажению области памяти, используемой другим потоком. Процесс лишен возможности это делать, так как у него своя копия памяти,

■ Копирование памяти, требуемой для дочернего процесса, приводит к снижению производительности процессов в сравнении с потоками. Но на самом деле операция копирования выполняется только тогда, когда содержимое памяти изменяется, поэтому снижение производительности оказывается минимальным, если дочерний процесс обращается к памяти только для чтения данных.

■ Потоки требуются программам, в которых необходима тонкая настройка параллельной работы. Потоки, например, хорошо подходят в том случае, когда задание можно разбить на ряд почти идентичных задач. Процессы в основном работают не зависимо друг от друга.

■ Совместное использование данных несколькими потоками — тривиальная задача, ведь потоки имеют общий доступ к ресурсам (необходимо, правда, внимательно следить за тем, чтобы не возникало состояние гонки). В случае процессов требуется задействовать особый механизм взаимодействия, описанный в главе 5, "Взаимодействие процессов". Это делает программы более громоздкими, зато уменьшает вероятность ошибок, связанных с параллельной работой.

Глава 5

Взаимодействие процессов

Поделиться с друзьями: