#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().