Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Модули ядра Linux
Назад Внутренние механизмы ядра Вперед

Атомарные переменные и операции

Атомарные переменные — это наименее ресурсоёмкие средства обеспечения атомарного выполнения операций (там, где их минимальных возможностей достаточно). Реализуются в платформенно зависимой части кода ядра. Важные качества атомарных переменных и операций: а). компилятор (по ошибке, пытаясь повысить эффективность кода) не будет оптимизировать операции обращения к атомарным переменным, б). атомарные операции скрывают различия между реализациями для различных аппаратных платформ.

Функции, реализующие атомарные операции можно разделить на 2 группы по способу выполнения: а).атомарные операции, устанавливающие новые значения и б). атомарные операции, которые обновляют значения, при этом возвращая предыдущее установленное значение (обычно это функции вида test_and_*()). С другой стороны, по представлению данных, с которыми они оперируют, атомарные операции также делятся на 2 группы по типу объекта: а).оперирующие с целочисленными значениями (арифметические) и б).оперирующие с последовательным набором бит. Атомарных операций, в итоге, великое множество, и далее обсуждаются только некоторые из них.

Битовые атомарные операции

Определены в <asm-generic/bitops.h> и целым каталогом описаний <asm-generic/bitops/*.h>. Битовые атомарные операции выполняют действия над обычными операндами типа unsigned long, первым операндом вызова является номер бита (0 — младший, ограничения на старший номер не вводится, для 32-бит процессоров это 31, для 64-бит процессоров 63):

void set_bit( int n, void *addr ); - установить n-й бит

void clear_bit( int n, void *addr ); - очистить n-й бит

void change_bit( int n, void *addr ); - инвертировать n-й бит

int test_and_set_bit( int n, void *addr ); - установить n-й бит и возвратить предыдущее значение этого бита

int test_and_clear_bit( int n, void *addr ); - очистить n-й бит и возвратить предыдущее значение этого бита

int test_and_change_bit( int n, void *addr ); - инвертировать n-й бит и возвратить предыдущее значение этого бита

int test_bit( int n, void *addr ); - возвратить значение n-го бита

Пример того, как могут использоваться битовые атомарные переменные:

	unsigned long word = 0; 
	set_bit( l, &word );        /* атомарно устанавливается бит 1 */ 
	clear_bit( 1, &word );      /* атомарно очищается бит 1 */ 
	change_bit( 1, &word );     /* атомарно инвертируется бит 1, теперь он опять установлен */ 
	if( test_and_clear_bit( 1, &word ) ) {  /* очищается бит 1, возвращается значение этого бита 1 */ 
	   /* в таком виде условие выполнится ... */ 
	} 
Арифметические атомарные операции

Реализуются в машинно-зависимом коде, описаны, например:

$ ls /lib/modules/`uname -r`/build/include/asm-generic/atomic*

	/lib/modules/2.6.32.9-70.fc12.i686.PAE/build/include/asm-generic/atomic64.h
	/lib/modules/2.6.32.9-70.fc12.i686.PAE/build/include/asm-generic/atomic.h
	/lib/modules/2.6.32.9-70.fc12.i686.PAE/build/include/asm-generic/atomic-long.h

Эта группа атомарных операций работает над операндами специального типа (в отличие от битовых операций). Вводятся специальные типы: atomic_t, atomic64_t, atomic_long_t, ...

ATOMIC_INIT( int i ) - объявление и инициализация в значение i переменной типа atomic_t

int atomic_read( atomic_t *v ); - считывание значения в целочисленную переменную

void atomic_set( atomic_t *v, int i ); - установить переменную v в значение i

void atomic_add ( int i, atomic_t *v ) ; - прибавить значение i к переменной v

void atomic_sub( int i, atomic_t *v ) ; - вычесть значение i из переменной v

void atomic_inc( atomic_t *v ) ; - инкремент v

void atomic_dec( atomic_t *v ) ; - декремент v

int atomic_sub_and_test( int i, atomic_t *v ); - вычесть i из переменной v , возвратить true, если результат равен нулю, и false в противном случае

int atomic_add_negative( int i, atomic_t *v ); - прибавить i к переменной v, возвратить true, если результат операции меньше нуля, иначе возвратить false

int atomic_dec_and_test( atomic_t *v ); - декремент v, возвратить true, если результат равен нулю, и false в противном случае

int atomic_inc_and_test( atomic_t *v ); - инкремент v, возвратить true, если результат равен нулю, и false в противном случае

Объявление атомарных переменных и запись атомарных операций не вызывает сложностей (аналогична работе с обычными переменными):

	atomic_t v = ATOMIC_INIT( 111 ); /* определение переменной и инициализация ее значения */ 
	atomic_add( 2, &v ) ;            / * v = v + 2  */ 
	atomic_inc( &v );                / * v++ */ 

В поздних версиях ядра набор атомарных переменных существенно расширен такими типами (64 бит), такими как:

	typedef struct {
	   long long counter;
	} atomic64_t;
	typedef atomic64_t atomic_long_t; 

И соответствующими для них операциями:

	ATOMIC64_INIT( long long ) ;
	long long atomic64_add_return( long long a, atomic64_t *v );
	long long atomic64_xchg( atomic64_t *v, long long new );
	...
	ATOMIC_LONG_INIT( long )
	void atomic_long_set( atomic_long_t *l, long i );
	long atomic_long_add_return( long i, atomic_long_t *l );
	int atomic_long_sub_and_test( long i, atomic_long_t *l );
	...

Предыдущий раздел: Оглавление Следующий раздел:
Синхронизации   Локальные переменные процессора