Как указывалось ранее, параллельные программы зачастую используют процессы для выполнения отдельных ветвей. В эту категорию часто попадают программы сетевых служб, например сервер баз данных W:[PostgreSQL], служба удаленного доступа W:[SSH] и подобные.
Листинг ниже иллюстрирует программу postgres, выполняющуюся в шести параллельных процессах, один из которых — диспетчер, четыре служебных и еще один вызван подключением пользователя fitz к одноименной базе данных fitz.
При последующих подключениях пользователей к серверу будут порождены дополнительные дочерние процессы для обслуживания их запросов — по одному на каждое подключение.
Содержимое
Параллельные многопроцессные сервисы
fitz@ubuntu:~$ ps f -С postgres
PID TTY STAT TIME COMMAND
1427 ? S 0:01 /usr/lib/postgresql/9.1/bin/postgres -D …
1984 ? Ss 0:25 \_ postgres: writer process
| 1985 ? Ss 0:22 \_ postgres: wal writer process
| 1986 ? Ss 0:05 \_ postgres: autovacuum launcher process
| 1987 ? Ss 0:04 \_ postgres: stats collector process
21619 ? Ss 0:00 \_ postgres: fitz fitz [local] idle
fitz@ubuntu:~$ ssh ubuntu
fitz@ubuntu’s password:
Last login: Sat Nov 21 13:29:33 2015 from localhost
fitz@ubuntu:~$ ps f -C sshd
PID TTY STAT TIME COMMAND
655 ? Ss 0:00 /usr/sbin/sshd -D
21975 ? Ss 0:00 \_ sshd: fitz [priv]
22086 ? S 0:00 \_ sshd: fitz@pts/1
fitz@ubuntu:~$ ^Dвыход
Connection to ubuntu closed.
Аналогично, при удаленном доступе по протоколу SSH программа sshd, выполняясь в качестве диспетчера в одном процессе, на каждое подключение порождает один свой клон, который, выполнив аутентификацию и авторизацию пользователя в системе, порождает еще один свой клон, имперсонирующийся в пользователя и обслуживающий его запросы.
Параллельные многонитевые программы
Для управления нитями в Linux используют стандартный POSIX-интерфейс pthreads, реализующийся библиотекой W: [NPTL], которая является частью библиотеки libc (см. в примере ниже).
Интерфейс предоставляет «нитевой» вызов создания нити pthread_create, который является условным аналогом «процессных» fork и ехес, вызов завершения и уничтожения нити pthread_exit, условно аналогичный exit, и вызов получения статуса завершения нити pthread_join, условно аналогичный wait.
В качестве типичных примеров использования нитей можно привести сетевые сервисы, которые для параллельного обслуживания клиентских запросов используют нити вместо процессов.
Например, WEB-сервер apache, как показано в листинге ниже, использует два многонитевых процесса по 27 нитей в каждом, что позволяет экономить память (за счет работы всех нитей процесса с общей памятью) при обслуживании большого количества одновременных клиентских подключений.
Параллельные многонитевые сервисы
fitz@ubuntu:~$ ps f -С apache2
PID TTY STAT TIME COMMAND
21352 ? Ss 0:00 /usr/sbin/apache2 -k start
21355 ? S 0:00 \_ /usr/sbin/apache2 -k start
21357 ? Sl 0:00 \_ /usr/sbin/apache2 -k start
21358 ? Sl 0:00 \_ /usr/sbin/apache2 -k start
fitz@ubuntu:~$ ps fo pid,nlwp,cmd -C apache2
PID NLWP CMD
21352 1 /usr/sbin/apache2 -k start
21355 1 \_ /usr/sbin/apache2 -k start
21357 27 \_ /usr/sbin/apache2 -k start
21358 27 \_ /usr/sbin/apache2 -k start 1
fitz@ubuntu:~$ ps -fLC rsyslogd
UID PID PPID LWP C NLWP STIME TTY TIME CMD
syslog 936 1 936 0 4 Nov19 ? 00:00:01 rsyslogd -c5
syslog 936 1 961 0 4 Nov19 ? 00:00:14 rsyslogd -c5
syslog 936 1 962 0 4 Nov19 ? 00:00:00 rsyslogd -c5
syslog 936 1 963 0 4 Nov19 ? 00:00:01 rsyslogd -c5
Аналогично, сервис централизованной журнализации событий rsyslogd использует нити дли параллельного сбора событийной информации из разных источников, ее обработки и журнализации.
Одна нить считывает события ядра из /proc/kmsg, вторая принимает события других служб из файлового сокета /dev/log, третья фильтрует поток приятных событий и записывает в журнальные файлы каталога /var/log/* и т. д.
Параллельная обработка потоков поступающих событий при помощи нитей производится с минимально возможными накладными расходами, что позволяет достигать колоссальной производительности по количеству обрабатываемых сообщений в единицу времени.
Распараллеливание используется не только для псевдоодновременного выполнения ветвей параллельной программы, но и для их настоящего одновременного выполнения несколькими центральными процессорами. В примере из листинга ниже показано, как сокращается время сжатия ISO-образа файла при использовании параллельного упаковщика pbzip2 по сравнению с последовательным bzip2.
Для измерения времени упаковки применяется встроенная команда интерпретатора time, при этом сначала измеряется время упаковки и время распаковки последовательным упаковщиком, а затем — время упаковки и время распаковки параллельным упаковщиком.
Команды упаковки запускаются на «заднем фоне», оценивается наличие процессов и нитей паковщиков, после чего они переводятся на «передний фон» встроенной командой интерпретатора fg (foreground) и оцениваются затраты времени.
Параллельные многонитевые утилиты
iltz@ubuntu:~$ ls -lh plan9.iso
-rw-r—r— 1 fitz fitz 287M нояб. 28 15:47 plan9.1so
fltz@ubuntu:~$ time bzip2 plan9.iso &
[1] 5545
fitz@ubuntu:~$ ps f
PID TTY STAT TIME COMMAND
4637 pts/0 S 0:00 -bash
5545 pts/0 S 0:00 \_ -bash
5546 pts/0 R 0:12 | \_ bzip2 plan9.iso
5548 pts/0 R+ 0:00 \_ ps f
fttz@ubuntu:~$ ps -fLp 5546
UID PID PPID LWP C NLWP STIME TTY TIME CMD
fitz 5546 5545 5546 96 1 10:50 pts/0 00:00:22 bzip2 plan9.1so
fitz@ubuntu:~$ fg
time bzip2 plan9.iso
real 0m54.780s
user 0m51.772s
sys 0m0.428s
fitz@ubuntu:~$ ls -Ih plan9.iso.bz2
-rw-r—r— 1 fitz fitz 89M нояб. 28 15:47 plan9.iso.bz2
fitz@ubuntu:~$ time bzip2 -d plan9.iso.bz2
real 0m20.705s
user 0m19.044s
sys 0m1.168s
fitz@ubuntu:~$ time pbzip2 plan9.iso &
[1] 5571
fitz@ubuntu:~$ ps f
PID TTY STAT TIME COMMAND
4637 pts/0 S 0:00 -bash
5571 pts/0 S 0:00 \_ -bash
5572 pts/0 Sl 0:03 | \_ pbzip2 plan9.iso
5580 pts/0 R+ 0:00 \_ ps f
fitz@ubuntiu:~$ ps -fLp 5572
UID PID PPID LWP C NLWP STIME TTY TIME CMD
fitz 5572 5571 5578 92 8 10:52 pts/0 00:00:43 pbzip2 plan9.iso
fitz 5572 5571 5579 1 8 10:52 pts/0 00:00:00 pbzip2 plan9.iso
fitz@ubuntu:~$ fg
time pbzip2 plan9.iso
real 0m24.259s
user 1m22.940s
sys 0m1.888s
fitz@ubuntu:~$ ls -lh plan9.iso.bz2
-rw-r—г— 1 fitz fitz 89M нояб. 28 15:47 plan9.iso.bz2
fitz@ubuntu:~$ time pbzip2 -d plan9.iso.bz2
real 0m7.384s
user 0m25.972s
sys 0m1.396s
В результате оценки оказывается, что последовательный упаковщик bzip2 использует один однонитевой процесс и затрачивает ≈ 54,7 с реального времени на упаковку, из них ≈ 51,7 с проводит в пользовательском режиме user и лишь ≈ 0,4, с в режиме ядра sys (выполняя системные вызовы, например, read или write).
Соотношение между временем режимов говорит о вычислительном характере программы, т. е. о существенном превалировании времени вычислительных операций упаковки над временем операций ввода-вывода для чтения исходных данных и< записи результатов.
Это означает, что нагрузка последовательного упаковщика на центральный процессор близка к максимальной, и его параллельная реализация для псевдоодновременного выполнения ветвей (которые практически никогда не спят) лишена смысла.
Параллельный упаковщик pbzip2 использует один многонитевой процесс из восьми нитей и затрачивает » 24,4 с реального времени на упаковку, при этом ≈ 1 мин 22,9 с проводит в пользовательском режиме и ≈ 1,8 с в режиме ядра.
Прирост производительности упаковки и, как следствие, сокращение времени упаковки достигаются за счет настоящего параллельного выполнения нитей на разных процессорах (ядрах процессора).
Соотношение между реальным временем упаковки и суммарно затраченным временем режима пользователя, которое примерно в 3 раза больше, означает использование в среднем трех процессоров для параллельного выполнения вычислительных операций упаковки.