¿Es realmente necesario un modificador volátil si las variables globales son modificadas por una interrupción?

Hubo mucho dicho y escrito acerca de las variables volátiles y su uso. En esos artículos, se pueden encontrar dos ideas ligeramente diferentes:

1 – La volatilidad debe usarse cuando la variable se cambia fuera del progtwig comstackdo.
2 – Se debe usar volátil cuando la variable se cambia fuera del flujo normal de la función.

La primera statement limita el uso volátil a los registros asignados en memoria, etc. y cosas de subprocesos múltiples, pero la segunda en realidad agrega interrupciones en el scope.

Este artículo ( http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword ), por ejemplo, establece explícitamente que el modificador volátil debe usarse para cambios globales durante la interrupción, y proporciona este ejemplo:

int etx_rcvd = FALSE; void main() { ... while (!ext_rcvd) { // Wait } ... } interrupt void rx_isr(void) { ... if (ETX == rx_char) { etx_rcvd = TRUE; } ... } 

Observe cómo la configuración de rx_isr () como callback se omite aquí. Por eso escribí mi propio ejemplo:

 #include  #include  #include  void f(); int n = 0; void main() { signal(2,f); time_t tLastCalled = 0; printf("Entering the loop\n"); while (n == 0) { if (time(NULL) - tLastCalled > 1) { printf("Still here...\n"); tLastCalled = time(NULL); } } printf ("Done\n"); } void f() { n = 1; } 

Comstackdo con gcc en linux con varios niveles de optimizaciones, cada vez que salía un bucle y vi “Hecho” cuando presioné ctrl + c, lo que significa que el comstackdor realmente es lo suficientemente inteligente como para no optimizar la variable n aquí.

Dicho esto, mi pregunta es:
Si el comstackdor realmente puede optimizar las variables globales modificadas por una rutina de servicio de interrupción, entonces:
1. ¿Por qué tiene el derecho de optimizar una variable global en primer lugar cuando posiblemente pueda llamarse desde otro archivo?
2. ¿Por qué el artículo de ejemplo y muchos otros en Internet afirman que el comstackdor no “notará” la función de callback de interrupción?
3. ¿Cómo modifico mi código para lograr esto?

Si el comstackdor realmente puede optimizar las variables globales modificadas por una rutina de servicio de interrupción, entonces:

  1. ¿Por qué tiene el derecho de optimizar una variable global en primer lugar cuando posiblemente se puede llamar desde otro archivo?

La clave aquí es que, en un progtwig “normal” de un solo hilo sin interrupciones, la variable global no puede modificarse en ningún momento. Todos los accesos a la variable se secuencian de manera predecible, sin importar qué archivo haga el acceso.

Y las optimizaciones pueden ser sutiles. No es tan simple como “ah, parece que este global no se usa, lo eliminamos por completo”. Más bien, para algún código como

 while(global) { do_stuff(global); } 

El optimizador podría crear algo que se comporte como:

 register tmp = global; loop: do_stuff(tmp); goto loop; 

Lo que cambia completamente el significado del progtwig. Cómo estos errores causados ​​por la falta de volatilidad se manifiestan en sí mismos es siempre diferente de un caso a otro. Son muy difíciles de encontrar.


  1. ¿Por qué el artículo de ejemplo y muchos otros en Internet afirman que el comstackdor no “notará” la función de callback de interrupción?

Porque los comstackdores embebidos son tradicionalmente estúpidos cuando se trata de este aspecto. Tradicionalmente, cuando un comstackdor detecta una palabra clave de interrupción no estándar, solo hará 2 cosas:

  • Genere el código de retorno específico de esa función, ya que las interrupciones usualmente tienen convenciones de llamada diferentes en comparación con las llamadas de función normales.
  • Asegúrese de que la función se vincule aunque nunca se llame desde el progtwig. Posiblemente asignado en un segmento de memoria separado. Esto es hecho por el enlazador y no por el comstackdor.

Puede que hoy en día haya comstackdores más inteligentes. Los comstackdores de PC / escritorio enfrentan el mismo problema al tratar con las funciones de callback / subprocesos, pero generalmente son lo suficientemente inteligentes como para darse cuenta de que no deben asumir cosas sobre variables globales compartidas con una callback.

Los comstackdores integrados son tradicionalmente mucho más tontos que los comstackdores de PC / escritorio cuando se trata de optimizaciones. En general, son de menor calidad y peor en el cumplimiento estándar. Si usted es uno de los pocos proveedores de comstackdores que admiten un objective específico, o tal vez el único proveedor, entonces la falta de competencia significa que no tiene que preocuparse mucho por la calidad. Puedes vender basura y cobrar mucho por ello.

Pero incluso los buenos comstackdores pueden enfrentarse a tales escenarios, especialmente los multiplataforma que no saben nada sobre cómo funcionan las interrupciones, etc., específicamente en “target x”.

Así que tienes el caso donde el comstackdor bueno y multiplataforma es demasiado genérico para manejar este error. Mientras que al mismo tiempo, el comstackdor estrecho y malo para “target x” está demasiado mal escrito para manejarlo, a pesar de que supuestamente sabe todo sobre cómo funcionan las interrupciones en “target x”.


  1. ¿Cómo modifico mi código para lograr esto?

Hacer volatile tales globos.

Debido a que tiene una llamada de función a una función externa, el bucle while comprueba n cada vez. Sin embargo, si elimina esas llamadas de función, el optimizador puede registrarse o cancelar cualquier comprobación de n .

Ex (gcc x86_64 -O3):

 volatile int n; int main() { while(n==0) {} return 0; } 

se convierte en:

 .L3: movl n(%rip), %eax testl %eax, %eax je .L3 xorl %eax, %eax ret 

Pero

 int n; int main() { while(n==0) {} return 0; } 

se convierte en:

  movl n(%rip), %eax testl %eax, %eax jne .L2 .L3: jmp .L3 

En este caso, n nunca se mira en el bucle infinito.

Si hay un controlador de señal que modifica un global, realmente debería etiquetar ese volátil global. Puede que no se meta en problemas al saltarse esto, pero está teniendo suerte o está contando con que el optimizador no pueda verificar si se está tocando un global.

Hay cierto movimiento en la optimización de módulos cruzados en el tiempo de enlace ( llvm ), por lo que algún día un optimizador podrá decir que las llamadas a time o printf no están tocando globales en su archivo. Cuando eso sucede, perder la palabra clave volátil puede causar problemas incluso si tiene llamadas a funciones externas.