Библиотека сайта rus-linux.net
Цилюрик О.И. Модули ядра Linux | ||
Назад | Внутренние механизмы ядра | Вперед |
Параллелизм и синхронизация
«Две передние, старшие, ноги вели животное в одну сторону – за большой головой, а две задние, младшие, ноги – в противоположную, за снабжённым головой женским хвостом.»
Милорад Павич «Смерть святого Савы, или невидимая сторона Луны»
Механизм потоков ядра (kernel thread - появляющийся с ядра 2.5) предоставляет средство параллельного выполнения задач в ядре. Общей особенностью и механизмов потоков ядра, и примитивов для их синхронизации, является то, что они в принципиальной основе своей единообразны, что для пользовательского пространства, что для ядра — различаются тонкие нюансы и функции доступного API их использования. Поэтому, рассмотрение (и тестирование на примерах) работы механизмов синхронизации можно с равной степенью общности (или параллельно) проводить как в пространстве ядра, там и в пространстве пользователя, например, так как это сделано в [9].
Нужно отчётливо разделить два класса параллелизма (а особенно требуемых для их обеспечения синхронизаций), природа которых совершенно различного происхождения:
- Логический параллелизм (или квази-параллелизм), обусловленный удобством разделения разнородных сервисов ядра, но реализующие потоки которых вытесняют друг друга, создавая только иллюзию параллельности. При этом синхронизация осуществляется исключительно классическими блокирующими механизмами, когда поток ожидает недоступных ему ресурсов переводясь в блокированное состояние.
- Физический параллелизм (или реальный параллелизм), возникший только с широким распространением SMP (в виде многоядерности или/и гипертриэдинга), когда разные задачи ядра выполняются одновременно на различных процессорах. В этом случае широко начинают использоваться (наряду с классическими) активные примитивы синхронизации (спин-блокировки), когда один из процессоров просто ожидает требуемых ресурсов выполняя пустые циклы ожидания. Этот второй класс (активно развиваемый с 2003-2005 г.г.) много крат усложняет картину происходящего (существуя одновременно с предыдущим классом), и доставляет большую головную боль разработчику. Но с ним придётся считаться, прогнозируя достаточно динамичное развитие тех направлений, что уже сегодня называется массивно-параллельными системами (примером чего может быть модель программирования CUDA компании NVIDIA), когда от систем с 2-4-8 процессоров SMP происходит переход к сотням и тысячам процессоров.
Механизм потоков ядра начал всё шире и шире использоваться от версии к версии ядер 2.6.х, на него даже было перенесено (переписано) ряд традиционных и давно существующих демонов Linux пользовательского уровня (в протоколе команд далее специально сохранены компоненты, относящиеся к сетевой файловой подсистеме nfsd — одной из самых давних подсистем UNIX). В формате вывода команды ps потоки ядра выделяются квадратными скобками:
$ uname -r
2.6.32.9-70.fc12.i686.PAE $ ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 09:52 ? 00:00:01 /sbin/init root 2 0 0 09:52 ? 00:00:00 [kthreadd] root 3 2 0 09:52 ? 00:00:00 [migration/0] root 4 2 0 09:52 ? 00:00:00 [ksoftirqd/0] root 5 2 0 09:52 ? 00:00:00 [watchdog/0] root 6 2 0 09:52 ? 00:00:00 [migration/1] root 7 2 0 09:52 ? 00:00:00 [ksoftirqd/1] root 8 2 0 09:52 ? 00:00:00 [watchdog/1] root 9 2 0 09:52 ? 00:00:00 [events/0] root 10 2 0 09:52 ? 00:00:00 [events/1] ... root 438 2 0 09:52 ? 00:00:00 [kjournald] root 458 2 0 09:52 ? 00:00:00 [kauditd] ... root 518 1 0 09:52 ? 00:00:00 /sbin/udevd -d root 858 2 0 09:53 ? 00:00:00 [tifm] root 870 2 0 09:53 ? 00:00:00 [kmmcd] ... root 1224 1 0 09:53 ? 00:00:00 /sbin/rsyslogd -c 4 root 1245 2 0 09:53 ? 00:00:00 [kondemand/0] root 1246 2 0 09:53 ? 00:00:00 [kondemand/1] rpc 1268 1 0 09:53 ? 00:00:00 rpcbind ... rpcuser 1323 1 0 09:53 ? 00:00:00 rpc.statd ... root 1353 2 0 09:53 ? 00:00:00 [rpciod/0] root 1354 2 0 09:53 ? 00:00:00 [rpciod/1] root 1361 1 0 09:53 ? 00:00:00 rpc.idmapd ... root 1720 1 0 09:53 ? 00:00:00 rpc.rquotad root 1723 2 0 09:53 ? 00:00:00 [lockd] root 1724 2 0 09:53 ? 00:00:00 [nfsd4] root 1725 2 0 09:53 ? 00:00:00 [nfsd] root 1726 2 0 09:53 ? 00:00:00 [nfsd] root 1727 2 0 09:53 ? 00:00:00 [nfsd] root 1728 2 0 09:53 ? 00:00:00 [nfsd] root 1729 2 0 09:53 ? 00:00:00 [nfsd] root 1730 2 0 09:53 ? 00:00:00 [nfsd] root 1731 2 0 09:53 ? 00:00:00 [nfsd] root 1732 2 0 09:53 ? 00:00:00 [nfsd] root 1735 1 0 09:53 ? 00:00:00 rpc.mountd ...
Для всех показанных в выводе потоков ядра родителем (PPID) является демон kthreadd (PID=2), который, как и процесс init не имеет родителя (PPID=0), и который запускается непосредственно при старте ядра. Число потоков ядра может быть весьма значительным:
$ ps -ef | grep -F '[' | wc -l
78
Функции организации работы с потоками и механизмы синхронизации для них доступны после включения заголовочного файла <linux/sched.h>. Макрос current возвращает указатель текущую исполняющуюся задачу в циклическом списке задач, на соответствующую ей запись struct task_struct :
struct task_struct { volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ void *stack; ... int prio, static_prio, normal_prio; ... pid_t pid; ... cputime_t utime, stime, utimescaled, stimescaled; ... }
Это основная структура, один экземпляр которой соответствует любой выполняющейся задаче: будь то поток (созданный вызовом kernel_thread()) ядра, пользовательский процесс (главный поток этого процесса), или один из пользовательских потоков, созданных вызовом pthread_create(...) в рамках единого процесса - Linux не знает разницы (исполнительной) между потоками и процессами, все они порождаются одним системным вызовом clone(). В единственном случае текущему исполняющемуся коду нет соответствия в виде записи struct task_struct() — это контекст прерывания (обработчик аппаратного прерывания, или, как частный случай, таймерная функция, которые мы уже рассматривали). Но и в этом случае указатель current указывает на определённую запись задачи, только это — последняя (до прерывания) выполнявшаяся задача, не имеющая никакого касательства к текущему выполняющемуся коду (текущему контексту). И на это обстоятельство нужно обращать особое внимание — оно может стать предметом очень серьёзных шибок!
Предыдущий раздел: | Оглавление | Следующий раздел: |
Время и диспетчирование в ядре | Потоки ядра |