практикум по Linux Kernel

Вопросы программного кода и архитектуры Linux

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 16 мар 2015, 19:34

Olej писал(а): Задача: Вам предстоит выполнить некоторые ординарные действия в ядре, но параллельно, средствами потоков ядра. Создайте, как минимум, 4 разных способа формирования выполнения своих действий в отдельном потоке.
3. Используя очередь отложенных действий (struct workqueue_struct, struct work_struct).
Вам в учебнике написали, что очередь отложенных действий - это из области обработки прерываний IRQ?
Ничего подобного... Её область применения куда шире: запланировать на отложенное время выполнение любых действий в отдельном потоке.

Код: Выделить всё

#include <linux/module.h>
#include <linux/sched.h> 
#include <linux/delay.h>

#define LOG(...) printk( KERN_INFO "! "__VA_ARGS__ )

static void wq_function( struct work_struct *work ) { // рабочая функция
   LOG( "%d: start time %lX\n", current->pid, jiffies );
   msleep( 1000 );                                    // пауза 1с. 
   LOG( "%d: finish time %lX\n", current->pid, jiffies );
   return;
}

int test_thread( void ) {
   int ret;
   struct work_struct work;
   unsigned long j = jiffies;
   struct workqueue_struct *wq = create_workqueue( "queue" );
   if( !wq ) return -EBADRQC;
   INIT_WORK( &work, wq_function );
   ret = queue_work( wq, &work );
   flush_workqueue( wq );
   destroy_workqueue( wq );
   j = jiffies - j;
   LOG( "%d: evaluation time was %ld millisec.\n", current->pid, 1000 * j / HZ );
   if( !ret ) return -EPERM;
   return -1;
}

module_init( test_thread );

MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

Код: Выделить всё

[olej@fedora smp]$ time sudo insmod thr3.ko
Error: could not insert module thr3.ko: Operation not permitted
real    0m1.112s
user    0m0.035s
sys     0m0.057s

[olej@fedora smp]$ dmesg | tail -n3
[  737.579134] ! 1807: start time 6AD4B
[  738.580144] ! 1807: finish time 6B134
[  738.580409] ! 2521: evaluation time was 1004 millisec.
Как мне кажется (IMHO) это вообще самый цивилизованный, высокоуровневый механизм выполнения действий в отдельном потоке.
Вложения
thr3.c
(989 байт) 373 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 16 мар 2015, 19:44

Olej писал(а): Задача: Вам предстоит выполнить некоторые ординарные действия в ядре, но параллельно, средствами потоков ядра. Создайте, как минимум, 4 разных способа формирования выполнения своих действий в отдельном потоке.
4. Используя планирование тасклета tasklet_schedule().
По аналогии с очередью рабочих процедур действие можно выполнить и в тасклете ... совершенно не обязательно это в обработчике прерывания использовать:

Код: Выделить всё

#include <linux/module.h>
#include <linux/sched.h> 
#include <linux/delay.h>
#include <linux/interrupt.h>

#define LOG(...) printk( KERN_INFO "! "__VA_ARGS__ )

static void tasklet_fn( unsigned long data ) {
   unsigned long j;
   LOG( "%d: start time %lX\n", current->pid, jiffies );
   j = jiffies;
   while( jiffies - j < HZ );             // активная пауза 1с. 
   LOG( "%d: finish time %lX\n", current->pid, jiffies );
   return;
}

int test_thread( void ) {
   unsigned long j = jiffies;
   DECLARE_TASKLET( tasklet, tasklet_fn, (unsigned long)NULL );
   tasklet_schedule( &tasklet );          // запланировать на выполнение  
   tasklet_kill( &tasklet );              // остановить тасклет перед завершением
   j = jiffies - j;
   LOG( "%d: evaluation time was %ld millisec.\n", current->pid, 1000 * j / HZ );
   return -1;
}

module_init( test_thread );

MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

Код: Выделить всё

[olej@fedora smp]$ time sudo insmod thr4.ko
Error: could not insert module thr4.ko: Operation not permitted
real    0m1.110s
user    0m0.018s
sys     0m0.077s

[olej@fedora smp]$ dmesg | tail -n3
[ 4638.617181] ! 13: start time 4233B9
[ 4639.617039] ! 13: finish time 4237A1
[ 4639.617048] ! 3061: evaluation time was 1000 millisec.

[olej@fedora smp]$ ps -ef | grep 13 | grep softirq
root        13     2  0 15:06 ?        00:00:00 [ksoftirqd/1]
Обратите внимание: в контексте какого потока отрабатывает функция потока!

Код: Выделить всё

[Olej@modules ~]$ ps -ef | grep softirq | grep '\['
root         3     2  0 08:54 ?        00:00:01 [ksoftirqd/0]
root        15     2  0 08:54 ?        00:00:01 [ksoftirqd/1]
root        22     2  0 08:54 ?        00:00:01 [ksoftirqd/2]
root        29     2  0 08:54 ?        00:00:00 [ksoftirqd/3]
Подзадача: Почему в этом случае мы не используем паузу msleep()? Почему я в этом случае нахитрил, обманул вас и не сделал совсем единообразно с предыдущими 3-мя примерами?
(это тоже сама по себе хорошая задача для упражнений!)

А вы сделайте вместо активной паузы в потоке пассивную и увидите :lol: ... как ваше ядро сойдёт с ума! ;-)
Потому что тасклет выполняется в атомарном контексте (в контексте прерывания, если хотите ... хотя здесь и нет никаких прерываний), и код в этом контексте не имеет права переходить в блокированные состояния.

P.S. Здесь же архив этой и всех предыдущих задач (на потоки).
Вложения
thr4.c
(1016 байт) 361 скачивание
smp.tgz
(3.83 КБ) 352 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 17 мар 2015, 14:47

Olej писал(а):
Olej писал(а): Ещё один "необъятный" класс для "хороших" задач из Linux kernel - это всё, что связано с параллельными потоками в ядре, многопроцессорностью, SMP и всё в этом духе...
Задача: Вам предстоит выполнить некоторые ординарные действия в ядре, но параллельно, средствами потоков ядра. Создайте, как минимум, 4 разных способа формирования выполнения своих действий в отдельном потоке.
А как только речь зашла про параллельности, то здесь самое время и примитивам синхронизации.
Параллельность, сама по себе - дело простое, но она не стоит выеденного яйца без синхронизации, а вот здесь и начинаются сложности.

Синхронизации по завершению потока (ожидание завершения) уже были в предыдущих задачах...
А теперь наоборот:
- Делаем модуль, который запускает N потоков ядра (N сделать параметром загрузки).
- Потоки отрабатывают (делают инкремент своих счётчиков циклов) и прерываются (синхронно завершаются) из запускающего потока по истечению интервала работы T.

Вот такая вот задача...

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 17 мар 2015, 14:53

Olej писал(а):Вот такая вот задача...
Задача: Сделать это (классический вариант) завершая рабочие потоки поочерёдным kthread_stop() из вызывающего потока, и анализом kthread_should_stop() в циклах рабочих потоков.

Пожалуйста:

Код: Выделить всё

#include <linux/module.h> 
#include <linux/delay.h> 
#include <linux/kthread.h>
#include <linux/vmalloc.h>

#define LOG(...) printk( KERN_INFO "! "__VA_ARGS__ )

static int thr = 2;          // число рабочих потоков
module_param( thr, uint, 0 );
static int sec = 1;          // общее время работы
module_param( sec, uint, 0 );

typedef unsigned long parm_t;

static int thread_func( void *param ) { 
   unsigned long j1 = jiffies, j2; 
   while( !kthread_should_stop() ) {
      ++*(parm_t*)param;     // выполняемая работа потоковой функции 
      msleep( 7 );
   }
   j2 = jiffies;
   LOG( "PID=%d : [%lX...%lX => %ld]", 
        current->pid, j1, j2, ( j2 - j1 ) * 1000 / HZ );
   return 0;
}

static int pa_init( void ) { 
   int i, ret = -1;
   parm_t *data = (parm_t*)vmalloc( thr * sizeof( parm_t ) ); // массив блоков данных
   struct task_struct **pt = (struct task_struct**)vmalloc( thr * sizeof( void* ) );
   static char s[ 120 ] = "rezults: ";
   if( NULL == data || NULL == pt ) {
      ret = -ENOMEM;
      goto end;
   }
   memset( data, 0, thr * sizeof( parm_t ) );                 // инициализация данных
   for( i = 0; i < thr; i++ )
      *( pt + i ) = kthread_run( thread_func, (void*)( data + i ), "pa_%d", i );
   msleep( 1000 * sec );                                      // интервал работы
   for( i = 0; i < thr; i++ )
      kthread_stop( *( pt + i ) );
   for( i = 0; i < thr; i++ )
      sprintf( s + strlen( s ), "%ld%s",
               (long)*( data + i ), i != thr - 1 ? ", " : "\n" ); 
   LOG( "%s", s );
end:  
   if( data != NULL ) vfree( data ); 
   if( pt != NULL ) vfree( pt ); 
   return ret; 
} 

module_init( pa_init ); 
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

Код: Выделить всё

[Olej@modules synch]$ sudo insmod pamix.ko thr=7 sec=2
insmod: ERROR: could not insert module pamix.ko: Operation not permitted

[Olej@modules synch]$ dmesg | tail -n8
[ 1166.199881] ! PID=4573 : [1000D2C5B...1000D3433 => 2008]
[ 1166.207856] ! PID=4574 : [1000D2C5B...1000D343B => 2016]
[ 1166.215857] ! PID=4575 : [1000D2C5B...1000D3443 => 2024]
[ 1166.223868] ! PID=4576 : [1000D2C5B...1000D344B => 2032]
[ 1166.224872] ! PID=4577 : [1000D2C5B...1000D344C => 2033]
[ 1166.231888] ! PID=4578 : [1000D2C5B...1000D3453 => 2040]
[ 1166.232881] ! PID=4579 : [1000D2C5B...1000D3454 => 2041]
[ 1166.232935] ! rezults: 251, 252, 253, 254, 254, 255, 255
kthread_stop(), завершая очередной поток обязан ожидать завершения потока, только после этого приступать к следующему. Из-за этого продолжительность выполнения потоков будет незначительно, но отличаться (это хорошо видно по результатам).
Если такое поведение нежелательно ... то мы поборем это в следующей задаче. ;-)
Вложения
pamix.c
(1.81 КБ) 353 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 17 мар 2015, 14:57

Olej писал(а):Если такое поведение нежелательно ... то мы поборем это в следующей задаче. ;-)
Задача: Сделайте задачу подобной предыдущей, но синхронизацию завершения - на едином для всех потоков битовой атомарной переменной, флаге.

Код: Выделить всё

#include <linux/module.h> 
#include <linux/delay.h> 
#include <linux/kthread.h>
#include <linux/vmalloc.h>

#define LOG(...) printk( KERN_INFO "! "__VA_ARGS__ )

static int thr = 2;          // число рабочих потоков
module_param( thr, uint, 0 );
static int sec = 1;          // общее время работы
module_param( sec, uint, 0 );

typedef unsigned long parm_t;

volatile long unsigned int final = 0;

static int thread_func( void *param ) { 
   unsigned long j1 = jiffies, j2; 
   while( 0 == test_bit( 0, &final ) ) {
      ++*(parm_t*)param;     // выполняемая работа потоковой функции 
      msleep( 7 );
   }
   j2 = jiffies;
   LOG( "PID=%d : [%lX...%lX => %ld]", 
        current->pid, j1, j2, ( j2 - j1 ) * 1000 / HZ );
   return 0;
}

static int pa_init( void ) { 
   int i, ret = -1;
   parm_t *data = (parm_t*)vmalloc( thr * sizeof( parm_t ) ); // массив блоков данных
   struct task_struct **pt = (struct task_struct**)vmalloc( thr * sizeof( void* ) );
   static char s[ 120 ] = "rezults: ";
   if( NULL == data || NULL == pt ) {
      ret = -ENOMEM;
      goto end;
   }
   memset( data, 0, thr * sizeof( parm_t ) );                 // инициализация данных
   for( i = 0; i < thr; i++ )
      *( pt + i ) = kthread_run( thread_func, (void*)( data + i ), "pa_%d", i );
   msleep( 1000 * sec );                                      // интервал работы
   set_bit( 0, &final );                                      // установить атомарно
   msleep( 3 * thr );
   for( i = 0; i < thr; i++ )
      sprintf( s + strlen( s ), "%ld%s",
               (long)*( data + i ), i != thr - 1 ? ", " : "\n" ); 
   LOG( "%s", s );
end:  
   if( data != NULL ) vfree( data ); 
   if( pt != NULL ) vfree( pt ); 
   return ret; 
} 

module_init( pa_init ); 
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

Код: Выделить всё

[Olej@modules synch]$ sudo insmod pamixab.ko thr=7 sec=2
insmod: ERROR: could not insert module pamixab.ko: Operation not permitted

[Olej@modules synch]$ dmesg | tail -n8
[ 1191.791981] ! PID=4595 : [1000D9043...1000D981B => 2008]
[ 1191.791983] ! PID=4593 : [1000D9043...1000D981B => 2008]
[ 1191.792956] ! PID=4594 : [1000D9043...1000D981B => 2008]
[ 1191.792959] ! PID=4599 : [1000D9044...1000D981C => 2008]
[ 1191.793034] ! PID=4596 : [1000D9043...1000D981C => 2009]
[ 1191.793332] ! PID=4597 : [1000D9043...1000D981C => 2009]
[ 1191.807971] ! PID=4598 : [1000D9044...1000D981C => 2008]
[ 1191.807971] ! rezults: 251, 251, 251, 251, 251, 251, 251
Вложения
pamixab.c
(1.91 КБ) 365 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 17 мар 2015, 15:01

Olej писал(а):Если такое поведение нежелательно ... то мы поборем это в следующей задаче. ;-)
Задача: Сделать то же, что и в предыдущем случае, но на арифметическом атомарном флаге. ... ну и пользуясь случаем ;-) , на этом флаге сделать ожидание запускающего потока пока завершаться все дочерние.

Код: Выделить всё

#include <linux/module.h> 
#include <linux/delay.h> 
#include <linux/kthread.h>
#include <linux/vmalloc.h>

#define LOG(...) printk( KERN_INFO "! "__VA_ARGS__ )

static int thr = 2;          // число рабочих потоков
module_param( thr, uint, 0 );
static int sec = 1;          // общее время работы
module_param( sec, uint, 0 );

typedef unsigned long parm_t;

static atomic_t final = ATOMIC_INIT( 0 );

static int thread_func( void *param ) { 
   unsigned long j1 = jiffies, j2; 
   while( 0 == atomic_read( &final ) ) {
      ++*(parm_t*)param;     // выполняемая работа потоковой функции 
      msleep( 7 );
   }
   atomic_inc( &final );     // инкремент атомарно
   j2 = jiffies;
   LOG( "PID=%d : [%lX...%lX => %ld]", 
        current->pid, j1, j2, ( j2 - j1 ) * 1000 / HZ );
   return 0;
}

static int pa_init( void ) { 
   int i, ret = -1;
   parm_t *data = (parm_t*)vmalloc( thr * sizeof( parm_t ) ); // массив блоков данных
   struct task_struct **pt = (struct task_struct**)vmalloc( thr * sizeof( void* ) );
   static char s[ 120 ] = "rezults: ";
   if( NULL == data || NULL == pt ) {
      ret = -ENOMEM;
      goto end;
   }
   memset( data, 0, thr * sizeof( parm_t ) );                 // инициализация данных
   for( i = 0; i < thr; i++ )
      *( pt + i ) = kthread_run( thread_func, (void*)( data + i ), "pa_%d", i );
   msleep( 1000 * sec );                                      // интервал работы
   atomic_inc( &final );                                      // инкремент атомарно
   while( atomic_read( &final ) < thr + 1 );                  // ожидание завершения
   for( i = 0; i < thr; i++ )
      sprintf( s + strlen( s ), "%ld%s",
               (long)*( data + i ), i != thr - 1 ? ", " : "\n" ); 
   LOG( "%s", s );
end:  
   if( data != NULL ) vfree( data ); 
   if( pt != NULL ) vfree( pt ); 
   return ret; 
} 

module_init( pa_init ); 
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

Код: Выделить всё

[Olej@modules synch]$ sudo insmod pamixad.ko thr=7 sec=2
insmod: ERROR: could not insert module pamixad.ko: Operation not permitted

[Olej@modules synch]$ dmesg | tail -n8
[ 1206.995511] ! PID=4608 : [1000DCBA4...1000DD375 => 2001]
[ 1207.001479] ! PID=4605 : [1000DCBA3...1000DD37B => 2008]
[ 1207.001497] ! PID=4606 : [1000DCBA3...1000DD37B => 2008]
[ 1207.001520] ! PID=4607 : [1000DCBA3...1000DD37B => 2008]
[ 1207.002495] ! PID=4611 : [1000DCBA4...1000DD37C => 2008]
[ 1207.002500] ! PID=4610 : [1000DCBA4...1000DD37C => 2008]
[ 1207.002535] ! PID=4609 : [1000DCBA4...1000DD37C => 2008]
[ 1207.002538] ! rezults: 251, 251, 251, 250, 251, 251, 251
Вложения
pamixad.c
(2.06 КБ) 354 скачивания
synch.tgz
(5.27 КБ) 345 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 17 мар 2015, 19:45

Olej писал(а): А как только речь зашла про параллельности, то здесь самое время и примитивам синхронизации.
Параллельность, сама по себе - дело простое, но она не стоит выеденного яйца без синхронизации, а вот здесь и начинаются сложности.
Здесь (и чуть раньше) мы проходимся по материалу раздела "Внутренние API ядра": управление памятью, параллельные потоки ядра и синхронизация, служба времени в ядре...

Это тот раздел, которому почти полностью посвящена знаменитая книжка LDD3 ... почему я и говорю, что не люблю эту книжку ;-) : это самый непотребный, в моём понимании, раздел для обсуждений. Эти вещи нужно изучать и знать в совершенстве по POSIX, пространству пользовательских процессов, а потом только переносить аналогии на API ядра. В API ядра нет ничего круто принципиального, отличающего от POSIX ... кроме названий и прототипов вызовов. ;-)

А вот что действительно для модулей ядра архи-актуально - это соглашения интерфейсов к ядру в Linux: построение символьных и блочных устройств, сетевых уровней и протоколов, обработки прерываний и т.д.

Но если мы "крутимся" вокруг механизмов синхронизации, то там есть один вопрос а). на который обязательно нужно бы сделать задачу-пример, б). который часто очень плохо понимается (я сам в нём время от времени попадаю впросак ;-) ) и в) который может быть очень опасным.
Это - активные блокировки: спин-блокировки.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 17 мар 2015, 20:06

Olej писал(а):Но если мы "крутимся" вокруг механизмов синхронизации, то там есть один вопрос а). на который обязательно нужно бы сделать задачу-пример, б). который часто очень плохо понимается (я сам в нём время от времени попадаю впросак ;-) ) и в) который может быть очень опасным.
Это - активные блокировки: спин-блокировки.
Над задачей (-чами) я ещё подумаю...
А пока краткие выжимки о спин-блокировках (которые не всегда все известны - повторить не лишнее):

1. Спин-блокировка имеет смысл исключительно и только в многопроцессорных системах. Спин-блокировка предназначена для защиты критических секций кода от одновременного выполнения разными процессорами.

2. В Linux ядре всё, что связано со спин-блокировками компилируется (в ядро) только если ядро генерируется для поддержки SMP ... что-то типа такого:

Код: Выделить всё

[Olej@modules boot]$ cat /boot/config-3.18.9-100.fc20.x86_64 | grep SMP
CONFIG_X86_64_SMP=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_SMP=y
# CONFIG_X86_VSMP is not set
# CONFIG_MAXSMP is not set
CONFIG_PM_SLEEP_SMP=y
CONFIG_SCSI_SAS_HOST_SMP=y
CONFIG_VIDEO_VP27SMPX=m

Если система собирается на однопроцессорную конфигурацию (CONFIG_SMP=n), то абсолютно всё, что связано со спин-блокировками в ядре исчезает, т.к. оно обложено соответствующими препроцессорными #ifdef ... Спин-блокировки в этом случае исчезают из ядра как класс!

3. Если код (пользовательского приложения или ядра) делает что-то такое:

Код: Выделить всё

DEFINE_SPINLOCK( lock );
...
spin_lock( &lock );
...
spin_lock( &lock ); // от на этом месте - deadlock! 
...
spin_unlock( &lock );
...
spin_unlock( &lock );
То в таком коде на 2-м spin_lock( &lock ) процессор остаётся крутиться навечно!
Навечно здесь нужно понимать: в приложении - пока вы убьёте своё приложение, а в ядре - действительно пока система не перезагрузится.
Т.е. такой "проприетарный" код приводит к деградации системы: если до этого места у вас было 4 процессора, то после этого их остаётся 3.

4. Если у вас N процессоров и такой код записать N+1 раз (в самых разных модулях), то после этого ядро системы Linux глухо умрёт, хотя все N процессоров будут активно крутиться и греть мировое пространство, выполняя бесконечные пустые циклы.
После этого - только перезагрузка!

5. Причём, если у вас удалённое (по SSH, например) подключение к хосту, то после такого "активного зависания" системы перешрузить вы её можете только физическим доступом: вырубать питание или жать кнопку RESET.

P.S. Я достаточно часто в примерах кода вижу залихватское ("шашки наголо!" :lol: ) использование спин-блокировок ... там где легко и с успехом использовался бы семафор или мютекс. Поэтому решил для себя специально выписать правила безопасности использования спин-блокировки (то что выше). Возможно ещё что-то вспомнится + допишется...

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 18 мар 2015, 01:30

Olej писал(а): Над задачей (-чами) я ещё подумаю...
Вот показательная задача:

Задача (спин-блокировки): Возьмите любой вариант из предыдущих 3-х задач (N параллельных потоков), и добавьте внутрь цикла потоковой функции захват и освобождение (в начале и конце тела цикла) активной спин-блокировки, общей для всех рабочих потоков, примерно как-то так:

Код: Выделить всё

DEFINE_SPINLOCK( lock );     // активная спин-блокировка
...
   while( !kthread_should_stop() ) {
      if( act ) spin_lock( &lock );
      msleep( 7 );
      if( act ) spin_unlock( &lock );
   }

Наблюдайте и покажите эффекты:
- Радикальное падение итоговой производительности от столь незначительного дополнения (такое будет происходить всегда при смешении пассивных и активных блокировок);
- Полную остановку (крах!) операционной системы при N+1 рабочих потоках, где N — число процессоров в вашей системе;

Изменения в коде там небольшие, я не буду всё рисовать (оно есть в прикреплённом файле), а вот результаты:

Код: Выделить всё

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/synch$ sudo insmod pamixx.ko thr=3 act=0
insmod: error inserting 'pamixx.ko': -1 Operation not permitted

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/synch$ dmesg | tail -n4
[ 2429.209532] ! PID=14380 : [81E52...81F4E => 1008]
[ 2429.220026] ! PID=14381 : [81E52...81F51 => 1020]
[ 2429.220071] ! PID=14382 : [81E52...81F51 => 1020]
[ 2429.220101] ! rezults: 84, 85, 85 => 254

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/synch$ sudo insmod pamixx.ko thr=3 act=1
insmod: error inserting 'pamixx.ko': -1 Operation not permitted

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/synch$ dmesg | tail -n4
[ 2445.300029] ! PID=14390 : [82E06...82F05 => 1020]
[ 2445.312036] ! PID=14391 : [82E06...82F08 => 1032]
[ 2445.324031] ! PID=14392 : [82E06...82F0B => 1044]
[ 2445.324059] ! rezults: 29, 29, 29 => 87

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/synch$ sudo insmod pamixx.ko thr=4 act=0
insmod: error inserting 'pamixx.ko': -1 Operation not permitted

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/synch$ dmesg | tail -n5
[ 2510.112045] ! PID=15301 : [86D54...86E50 => 1008]
[ 2510.112081] ! PID=15302 : [86D54...86E50 => 1008]
[ 2510.128537] ! PID=15303 : [86D54...86E53 => 1020]
[ 2510.137524] ! PID=15304 : [86D54...86E56 => 1032]
[ 2510.137553] ! rezults: 84, 84, 85, 86 => 339

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/synch$ sudo insmod pamixx.ko thr=4 act=1
insmod: error inserting 'pamixx.ko': -1 Operation not permitted

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/synch$ dmesg | tail -n5
[ 2520.620029] ! PID=15312 : [87790...87892 => 1032]
[ 2520.628032] ! PID=15313 : [87790...87895 => 1044]
[ 2520.640032] ! PID=15314 : [87790...87898 => 1056]
[ 2520.652030] ! PID=15315 : [87790...8789B => 1068]
[ 2520.652053] ! rezults: 16, 16, 16, 16 => 64
Здесь (при введении активной блокировки) мало того, что суммарная производительность падает в 3-6 раз, но здесь любопытнейший эффект: при увеличении числа рабочих потоков (и задействованных процессоров!) суммарная производительность не растёт, а падает.

А вот такая команда - это полный крах операционной системы:
olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/synch$ sudo insmod pamixx.ko thr=5 act=1
Это задача, конечно, не о том как надо делать, а о том как не надо делать.
Но если не ожидать (не понимать) подобных эффектов, и напороться на такие проявления в реальных проектах, то разгрести ситуацию будет ... ой как не просто! :-o
Вложения
pamixx.c
(2.16 КБ) 364 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 18 мар 2015, 13:41

Olej писал(а): Задача (спин-блокировки): Возьмите любой вариант из предыдущих 3-х задач (N параллельных потоков), и добавьте внутрь цикла потоковой функции захват и освобождение (в начале и конце тела цикла) активной спин-блокировки, общей для всех рабочих потоков, примерно как-то так:
Родственная задача:

Задача: Смените в предыдущей задаче спин-блокировку на, например, мютекс реального времени (заодно и практика будет с этим новым мютексом ;-) ) и убедитесь, что многопоточное приложение и в этом случае продолжает выполняться как однопоточное, последовательно. Но при этом не могут возникнуть эффекты деградации системы, даже при большом числе параллельных потоков.

Сначала нужно проверить, что ваш экземпляр ядра Linux вообще собирался с поддержкой мютексов реального времени (скорее всего это так и есть):

Код: Выделить всё

[Olej@modules ~]$ cat /boot/config-`uname -r` | grep RT_MUTEX
CONFIG_RT_MUTEXES=y
# CONFIG_DEBUG_RT_MUTEXES is not set
А дальше потоковую функцию меняем на вот так:

Код: Выделить всё

DEFINE_RT_MUTEX( lock );     // мютекс реального времени
...
static int thread_func( void *param ) {
   unsigned long j1 = jiffies, j2;
   while( !kthread_should_stop() ) {
      if( act ) rt_mutex_lock( &lock );
      ++*(parm_t*)param;     // выполняемая работа потоковой функции 
      msleep( 7 );
      if( act ) rt_mutex_unlock( &lock );
   }
...
}
Всё! :

Код: Выделить всё

[Olej@modules synch]$ sudo insmod pamixm.ko thr=15 act=1
[sudo] пароль для Olej:
insmod: ERROR: could not insert module pamixm.ko: Operation not permitted

[Olej@modules synch]$ dmesg | tail -n16
[ 2993.373034] ! PID=5295 : [100290CAB...1002910EB => 1088]
[ 2993.381033] ! PID=5296 : [100290CAB...1002910F3 => 1096]
[ 2993.397065] ! PID=5297 : [100290CAB...100291103 => 1112]
[ 2993.493102] ! PID=5298 : [100290CAB...100291163 => 1208]
[ 2993.509128] ! PID=5299 : [100290CAB...100291173 => 1224]
[ 2993.589188] ! PID=5300 : [100290CAB...1002911C3 => 1304]
[ 2993.605195] ! PID=5301 : [100290CAB...1002911D3 => 1320]
[ 2993.669207] ! PID=5302 : [100290CAB...100291213 => 1384]
[ 2993.677238] ! PID=5303 : [100290CAB...10029121B => 1392]
[ 2993.685232] ! PID=5304 : [100290CAB...100291223 => 1400]
[ 2993.693230] ! PID=5305 : [100290CAB...10029122B => 1408]
[ 2993.701242] ! PID=5306 : [100290CAB...100291233 => 1416]
[ 2993.709239] ! PID=5307 : [100290CAB...10029123B => 1424]
[ 2993.717251] ! PID=5308 : [100290CAB...100291243 => 1432]
[ 2993.725245] ! PID=5309 : [100290CAB...10029124B => 1440]
[ 2993.725302] ! rezults: 10, 10, 10, 11, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 => 180
Вложения
pamixm.c
(2.17 КБ) 354 скачивания

Ответить

Вернуться в «Linux изнутри»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 4 гостя