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

В этой статье в основном представлены некоторые связанные варианты использования атомарных операций.


предисловие

        В этой статье в основном представлены некоторые связанные варианты использования атомарных операций.用于记录。  


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

     Атомарные операции гарантируют, что модификации целого числа являются эксклюзивными. Ядро Linux предоставляет ряд функций для реализации атомарных операций в ядре, которые делятся на две категории, выполняющие атомарные операции над битовыми и целочисленными переменными соответственно. Атомарные операции с битовыми и целочисленными переменными зависят от атомарных операций базового ЦП, поэтому все эти функции тесно связаны с архитектурой ЦП. Для процессоров ARM базовый уровень использует инструкции LDREX и STREX.Например, базовая реализация atomic_inc() вызовет atomic_add().Код выглядит следующим образом:

static inline void atomic_add(int i, atomic_t *v)
{
    unsigned long tmp;
    int result;
    prefetchw(&v->counter);
    __asm__ __volatile__("@ atomic_add\n"
    "1:
    ldrex
    %0, [%3]\n"
    "
    add
    %0, %0, %4\n"
    "
    strex
    %1, %0, [%3]\n"
    "
    teq
    %1, #0\n"
    "
    bne
    1b"
    : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
    : "r" (&v->counter), "Ir" (i)
    : "cc");
}

        Инструкция ldrex связана с strex, что позволяет шине отслеживать, есть ли другие объекты, обращающиеся к адресу между ldrex и strex.Если есть параллельный доступ, при выполнении инструкции strex значение первого регистра устанавливается в 1 (немонопольный доступ) и поведение при хранении также оказывается неудачным; если параллельного доступа нет, strex устанавливает 0 (монопольный доступ) в первом регистре, и поведение при хранении также оказывается успешным. В этом примере, если два параллельных объекта вызывают ldrex+strex одновременно, как показано на рис. 7.6, в момент времени T3 strex CPU0 не выполнится, а во время T4 strex CPU1 выполнится успешно. Таким образом, между CPU0 и CPU1 успешно выполняется только CPU1, и оператор суждения "teq%1, #0" для CPU0, которому не удалось выполнить strex, не будет установлен, поэтому CPU0, который не удалось выполнить, снова входит в ldrex через "bne 1b". Этот процесс ldrex и strex применим не только к параллелизму между несколькими ядрами, но и к параллелизму внутри одного ядра.

2. Целочисленные атомарные операции

1. Установите значение атомарной переменной

void atomic_set(atomic_t *v, int i);/*设置原子变量的值为 i*/
atomic_t v = ATOMIC_INIT(0);/*定义原子变量 v 并初始化为 0*/

2. Получить значение атомарной переменной

atomic_read(atomic_t *v);/*返回原子变量的值 */

3. Сложение и вычитание атомарных переменных

void atomic_add(int i, atomic_t *v);/*原子变量增加 i*/
void atomic_sub(int i, atomic_t *v);/*原子变量减少 i*/

 4. Инкремент и декремент атомарной переменной

void atomic_inc(atomic_t *v);/*原子变量增加1*/
void atomic_dec(atomic_t *v);/*原子变量减少1*/

5. Операция

int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
/*上述操作对原子变量执行自增、自减和减操作后(注意没有加),测试其是否为0,为0返回true,否
则返回false。*/
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
/*上述操作对原子变量进行加/减和自增/自减操作,并返回新的值。*/

3. Битовая атомарная операция

 

1.设置位
void set_bit(nr, void *addr);
上述操作设置addr地址的第nr位,所谓设置位即是将位写为1。
2.清除位
void clear_bit(nr, void *addr);
上述操作清除addr地址的第nr位,所谓清除位即是将位写为0。
3.改变位
void change_bit(nr, void *addr);
上述操作对addr地址的第nr位进行反置。
4.测试位
test_bit(nr, void *addr);
上述操作返回addr地址的第nr位。
5.测试并操作位
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
上述test_and_xxx_bit(nr,void*addr)操作等同于执行test_bit(nr,void*addr)后再执行
xxx_bit(nr,void*addr)。

пример Используйте атомарную переменную, чтобы открыть устройство только одним процессом

static atomic_t xxx_available = ATOMIC_INIT(1); /* 定义原子变量 */

static int xxx_open(struct inode *inode, struct file *filp)
{
   ...
 if (!atomic_dec_and_test(&xxx_available)) {
    atomic_inc(&xxx_available);
    return - EBUSY;
    /* 已经打开 */9 }
    ...
    return 0;
   /* 成功 */
}

static int xxx_release(struct inode *inode, struct file *filp)
{
  atomic_inc(&xxx_available);
  /* 释放设备 */
  return 0;
}

Supongo que te gusta

Origin blog.csdn.net/qq_48709036/article/details/124822656
Recomendado
Clasificación