null

Некоторые особенности разработки ядра Linux на SMP-системах

Начиная с версии 2.0 ядро Linux поддерживает многопроцессорные системы. В этой статье я расскажу о некоторых интерфейсах ядра, связанных с SMP-системами. Речь пойдет о процессорных масках (cpu mask) и переменных, индивидуальных для процессора (per-cpu variables). Сконфигурировано ли ядро с поддержкой SMP можно определить с помощью опции CONFIG_SMP:

#ifdef CONFIG_SMP
// Специфичный для SMP код
#endif

Работаем с номерами процессоров и масками

Определить, на каком процессоре в данный момент исполняется код можно с помощью функции smp_processor_id(). Также есть функция on_each_cpu. Она вызывает IPI (Inter Processor Interrupt) и вызывает переданную ему функцию в виде аргумента на каждом из процессоров:

static void cpu_hello(void* info) {
  pr_notice("Hello from cpu%d\n", smp_processor_id());
}

...
on_each_cpu(cpu_hello, NULL, 0);


Вывод на консоль будет следующим:

[ 2542.592429] Hello from cpu1
[ 2542.592942] Hello from cpu0
[ 2542.592969] Hello from cpu3
[ 2542.593060] Hello from cpu2



В ядре также есть макросы get_cpu (возвращает номер процессора) и put_cpu - они гарантируют, что код, выполняемый между их вызовами не сменит процессора (в текущей реализации запрещают и разрешают преемптивность).

Максимально допустимое количество процессоров в системе определяется константой NR_CPUS (но не текущее). А вот сами процессоры определяются четырьмя битовыми масками, определенными в файле include/linux/cpumask.h

  • cpu_possible_mask - биты установлены для всех возможных процессоров в системе, то ест те, которые могут быть размечены. Это узначение устанавливается при загрузке системы.
  • cpu_present_mask - биты установлены для всех присутствующих в системе процессоров, и если у вас с конфигурировано CONFIG_HOTPLUG_CPU, может динамически меняться в процессе работы системы
  • cpu_online_mask - биты установлены для всех процессоров, на которые можно ставить исполняться процессы (т.е. доступных для планировщика).
  • cpu_active_mask - биты для всех процессоров, которые доступны для миграции. В случае если процессор отключается в hotplug-операции, соответствующий бит в маске снимается.



Соответственно, имходя из маски можно определить количество присутствующих в системе процессоров так: num_present_cpus() или проверить, соответствует ли номер процессору в онлайне (эти макросы доступны для всех четырех масок):

if(cpu_online(i)) {
    //...
}



Поддерживаются различные битовые операции с масками, например их пересечение с помощью cpumask_and. Особый интерес представляет обход всех процессоров в системе. Простейший вариант - это использование макросов for_each_cpu(index, mask) и их вариантов, например:

int i = 0;
   
for_each_online_cpu(i) {
    /*Do something*/
}

Переменные, локальные для процессора (Per-CPU variables)

Также некоторые переменные в Linux выделяются по одной на процессор, например указатель на текущую задачу current работает по этому принципу (т.к. понятно, что общий для всех процессоров указатель current не имеет смысла). Они используют отдельные механизмы выделения памяти (что улучшает локальность обращений на NUMA-системах), и даже специфичный способ адресации, например на x86 архитектуре они адресуются через сегментный регистр GS. Объявляется такая переменная конструкцией DECLARE_PER_CPU:
DECLARE_PER_CPU(type, cpu_var)

Обращаться к ним можно как с помощью функций get_cpu_var/put_cpu_var (аналогичны get_cpu/put_cpu), например так:

x = get_cpu_var(cpu_var);
put_cpu_var(cpu_var);

...

get_cpu_var(cpu_var) = 10;
put_cpu_var(cpu_var)


Так и с помощью специальных операций percpu_* Использование таких операций предпочтительнее, т.к. они имеют оптимизированые для архитектуры варианты.

x = percpu_read(cpu_var);

...

percpu_write(cpu_var, 10);
percpu_add(cpu_var, 5);

К списку статей

 

Интересуюсь по большей части системным анализом программного обеспечения: поиском багов и анализом неисправностей, а также системным программированием (и не оставляю надежд запилить свою операционку, хотя нехватка времени сказывается :) ). Программированием увлекаюсь с 12 лет, но так уж получилось, что стал я инженером.

Основная сфера моей деятельности связана с поддержкой Solaris и оборудования Sun/Oracle, хотя в последнее время к ним прибавились технологии виртуализации (линейка Citrix Xen) и всякое разное от IBM - от xSeries до Power. Учусь на кафедре Вычислительной Техники НИУ ИТМО.

See you...out there!

http://www.facebook.com/profile.php?id=100001947776045
https://twitter.com/AnnoyingBugs