Red de conocimiento informático - Aprendizaje de código fuente - Los novatos piden consejo: ¿Cómo escribir operaciones atómicas en LINUX?

Los novatos piden consejo: ¿Cómo escribir operaciones atómicas en LINUX?

Acerca de las operaciones atómicas en Linux

2 de agosto de 2016

Operaciones atómicas: se refiere a realizar operaciones sin interrupción.

Los problemas de operación atómica en Linux se originan en interrupciones, apropiación de procesos y ejecución concurrente de programas en sistemas SMP multinúcleo.

Las operaciones atómicas se pueden garantizar agregando bloqueos para operaciones de secciones críticas, mientras que las operaciones de variables atómicas para operaciones de variables globales o estáticas deben depender de la plataforma de hardware.

Por tanto, existen dos tipos de operaciones atómicas: bloqueos para diversas áreas críticas y funciones que operan sobre variables atómicas.

En lo que respecta a arm, una sola instrucción de ensamblaje es una operación atómica, y smp multinúcleo también es una operación atómica. Debido a que hay arbitraje de bus, la CPU puede ocupar el bus sola hasta el final. de la instrucción. Las operaciones atómicas en sistemas multinúcleo a menudo se implementan utilizando barreras de memoria, lo que significa que cuando un núcleo de CPU realiza una operación atómica, otros núcleos de CPU deben dejar de realizar operaciones en la memoria para evitar problemas de carrera de datos. Pero para cargar y actualizar el almacenamiento, este proceso puede interrumpirse y adelantarse, por lo que el conjunto de instrucciones arm agrega instrucciones atómicas como ldrex/strex para cargar y actualizar el almacenamiento.

Sin embargo, Linux proporciona un conjunto de funciones para operar variables globales o variables estáticas para programas c/c++ (un c se compila en múltiples ensamblajes, por las razones anteriores, estas funciones no pueden garantizar la atomicidad). i. #include define operaciones atómicas enteras, que se dividen en Definir, Obtener, Sumar, Restar, Probar y Devolver.

void atomic_set(atomic_t *v,int i); //Establece el valor de la variable atómica v en iatomic_t v = ATOMIC_INIT(0); //Define la variable atómica v e inicializala en 0; Devuelve el valor de la variable atómica v; void atomic_add(int i, atomic_t* v); //agrega i a la variable atómica v; void atomic_sub(int i, atomic_t* v); aumento de variable en 1; void atomic_dec(atomic_t* v);?int atomic_inc_and_test(atomic_t* v); //primero incrementa en 1 y luego prueba si el valor es 0, si es 0, devuelve verdadero; de lo contrario, devuelve false; int atomic_dec_and_test(atomic_t* v); int atomic_sub_and_test(int i, atomic_t* v); // primero resta i y luego prueba si el valor es 0, si es 0, devuelve verdadero; de lo contrario, devuelve falso Nota: solo Operación de incremento automático, sin operación de suma 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); incrementar en 1 el nuevo valor después; int atomic_dec_return(atomic_t * v);ii. Las operaciones atómicas de bits definidas en #include se dividen en configuración, borrado, cambio y prueba void set_bit(int nr, volatile void* addr); // Establece el enésimo bit de la dirección, el llamado bit de configuración, es decir, escribe el bit en 1; void clear_bit( int nr, volatile void* addr ?//void change_bit( int nr, volatile void * addr); //invierte el enésimo bit de la dirección addr; int test_bit(int nr, volatile void* addr); //vuelve al nr bit de la dirección int test_and_set_bit(int nr, volatile void* addr); // prueba y configura el bit; si el bit nr de addr no es 0, devuelve verdadero; si el bit nr de addr es 0, devuelve false; el bit; int test_and_change_bit(int nr, volatile void* addr //Probar y cambiar);

Invertir el bit; la operación anterior equivale a ejecutar test_bit(nr,voidaddr) primero y luego ejecutar xxx _bit(nr,voidaddr)

Un ejemplo simple: para darse cuenta de que el dispositivo solo puede ser abierto por un proceso, por lo tanto Evite las condiciones de carrera static atomic_t scull_available = ATOMIC_INIT(1);?// Inicialice los átomos en la función scull_open y la función scull_close: int scull_open(struct inode *inode, struct file *filp){ struct scull_dev *dev; // Información del dispositivo dev = container_of(inode->i_cdev, struct scull_dev, cdev); filp->private_data = dev;?// para otros métodos if(!atomic_dec_and_test(&scull_available)){ atomic_inc(&scull_available return -EBUSY; } return 0; // Success?} int scull_release(struct inode *inode, struct file *filp){ atomic_inc(&scull_available return 0;}

Suponiendo que la implementación subyacente de las variables atómicas se implementa mediante instrucciones de montaje, entonces se debe garantizar esta atomicidad. Sin embargo, si la implementación de variables atómicas se implementa mediante una combinación de múltiples instrucciones, ¿habrá un impacto en SMP y en la intervención de interrupción? Cuando observé la implementación de ARM de operaciones de variables atómicas, descubrí que se implementa mediante múltiples instrucciones de ensamblaje (ldrex/strex). Después de consultar otros libros y fuentes, descubrí que la mayoría de los libros describen que estas dos instrucciones admiten el acceso mutuamente excluyente a la memoria multinúcleo disfrutada en sistemas SMP. Pero cuando se usan estas dos instrucciones en el sistema UP, si ocurre una interrupción entre ldrex/strex y la interrupción, y ldrex/strex también se usa para operar la misma variable atómica durante la interrupción, ¿habrá algún problema? Con respecto a este tema, el autor verificó cuidadosamente el código fuente de la variable atómica ARM en el kernel y la explicación oficial de ARM de la función ldrex/strex. El resumen es el siguiente:

1. arquitectura

Para la estructura de implementación de variables atómicas de la arquitectura ARM, su código fuente se encuentra en: