Библиотека сайта rus-linux.net
Цилюрик О.И. Модули ядра Linux | ||
Назад | Внутренние механизмы ядра | Вперед |
Потоки ядра
Для создания нового потока ядра используем вызов:
int kernel_thread( int (*fn)(void *), void * arg, unsigned long flags );
Параметры такого вызова понятны: функция потока, безтиповой указатель — параметр, передаваемый этой функции, и флаги, обычные для Linux вызова clone(). Возвращаемое функцией значение — это PID вновь созданного потока (если он больше нуля).
А вот он же среди экспортируемых символов ядра:
$ cat /proc/kallsyms | grep kernel_thread
c0407c44 T kernel_thread ...
Примечание: Позже, при рассмотрении обработчиков прерываний, мы увидим механизм рабочих очередей (workqueue), обслуживаемый потоками ядра. Должно быть понятно, что уже одного такого механизма высокого уровня достаточно для инициации параллельных действий в ядре (с неявным использованием потоков ядра). Здесь же мы пока рассмотрим только низкоуровневые механизмы, которые и лежат в базисе таких возможностей.
Первый простейший пример для прояснения того, как создаются потоки ядра (архив thread.tgz):
mod_thr1.c :
#include <linux/module.h> #include <linux/sched.h> #include <linux/delay.h> static int param = 3; module_param( param, int, 0 ); static int thread( void * data ) { printk( KERN_INFO "thread: child process [%d] is running\n", current->pid ); ssleep( param ); /* Пауза 3 с. или как параметр укажет... */ printk( KERN_INFO "thread: child process [%d] is completed\n", current->pid ); return 0; } int test_thread( void ) { pid_t pid; printk( KERN_INFO "thread: main process [%d] is running\n", current->pid ); pid = kernel_thread( thread, NULL, CLONE_FS ); /* Запускаем новый поток */ ssleep( 5 ); /* Пауза 5 с. */ printk( KERN_INFO "thread: main process [%d] is completed\n", current->pid ); return -1; } module_init( test_thread );
В принципе, этот модуль ядра ничего и не выполняет, за исключением того, что запускает новый поток ядра. При выполнении этого примера мы получим что-то подобное следующему:
$ uname -r
2.6.32.9-70.fc12.i686.PAE
$ time sudo insmod ./mod_thr1.ko
insmod: error inserting './mod_thr1.ko': -1 Operation not permitted real 0m5.025s user 0m0.004s sys 0m0.012s
$ sudo cat /var/log/messages | tail -n30 | grep thread:
Jul 24 18:43:57 notebook kernel: thread: main process [12526] is running Jul 24 18:43:57 notebook kernel: thread: child process [12527] is running Jul 24 18:44:00 notebook kernel: thread: child process [12527] is completed Jul 24 18:44:02 notebook kernel: thread: main process [12526] is completed
Примечание: Если мы станем выполнять пример с задержкой дочернего процесса больше родительского, то получим (после завершения запуска, при завершении созданного потока ядра!) сообщение Oops ошибки ядра:
$ sudo insmod ./mod_thr1.ko param=7
insmod: error inserting './mod_thr1.ko': -1 Operation not permitted $ Message from syslogd@notebook at Jul 24 18:51:00 ... kernel:Oops: 0002 [#1] SMP ...
$ sudo cat /var/log/messages | tail -n70 | grep thread:
Jul 24 18:50:53 notebook kernel: thread: main process [12658] is running Jul 24 18:50:53 notebook kernel: thread: child process [12659] is running Jul 24 18:50:58 notebook kernel: thread: main process [12658] is completed
Последний параметр flags вызова kernel_thread() определяет детальный, побитово устанавливаемый набор тех свойств, которыми будет обладать созданный потока ядра, так как это вообще делается в практике Linux вызовом clone() (в этом месте, создании потоков-процессов, наблюдается существенное отличие Linux от традиций UNIX/POSIX). Часто в коде модулей можно видеть создание потока с таким набором флагов:
kernel_thread( thread_function, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
Созданному потоку ядра (как и пользовательским процессам и потокам) присущ целый ряд параметров (<linux/sched.h>), часть которых будет иметь значения по умолчанию (такие, например, как параметры диспетчеризации), но которые могут быть и изменены. Для работы с параметрами потока используем следующие API:
1. Взаимно однозначное соответствие PID потока и соответствующей ему основной структуры данных, записи о задаче, которая уже обсуждалась (struct task_struct) — устанавливается в обоих направлениях вызовами:
static inline pid_t task_pid_nr( struct task_struct *tsk ) { return tsk->pid; } struct task_struct *find_task_by_vpid( pid_t nr );
Или, пользуясь описаниями из <linux/pid.h>:
// find_vpid() find the pid by its virtual id, i.e. in the current namespace extern struct pid *find_vpid( int nr ); enum pid_type { PIDTYPE_PID, PIDTYPE_PGID, PIDTYPE_SID, PIDTYPE_MAX }; struct task_struct *pid_task( struct pid *pid, enum pid_type ); struct task_struct *get_pid_task( struct pid *pid, enum pid_type ); struct pid *get_task_pid( struct task_struct *task, enum pid_type type );
В коде модуля это может выглядеть так:
struct task_struct *tsk; tsk = find_task_by_vpid( pid );
Или так:
tsk = pid_task( find_vpid( pid ), PIDTYPE_PID );
2. Дисциплина планирования и параметры диспетчеризации, предписанные потоку, могут быть установлены в новые состояния так:
struct sched_param { int sched_priority; }; int sched_setscheduler( struct task_struct *task, int policy, struct sched_param *parm ); // Scheduling policies #define SCHED_NORMAL 0 #define SCHED_FIFO 1 #define SCHED_RR 2 #define SCHED_BATCH 3 /* SCHED_ISO: reserved but not implemented yet */ #define SCHED_IDLE 5
3. Другие вызовы, имеющие отношение к приоритетам процесса:
void set_user_nice( struct task_struct *p, long nice ); int task_prio( const struct task_struct *p ); int task_nice( const struct task_struct *p );
4. Разрешения на использование выполнения на разных процессорах в SMP системах (афинити-маска процесса):
extern long sched_setaffinity( pid_t pid, const struct cpumask *new_mask ); extern long sched_getaffinity( pid_t pid, struct cpumask *mask );
где (<linux/cpumask.h>):
typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
Вообще, во всём связанном с созданием нового потока в ядре, прослеживаются прямые аналогии с созданием параллельных ветвей в пользовательском пространстве, что очень сильно облегчает работу с такими механизмами.
Предыдущий раздел: | Оглавление | Следующий раздел: |
Параллелизм и синхронизация | Синхронизации |