¿Por qué se volatile
necesita en C? ¿Para qué se usa esto? Que va a hacer
c
declaration
volatile
Jonathan Leffler
fuente
fuente
Respuestas:
Volátil le dice al compilador que no optimice nada que tenga que ver con la variable volátil.
Hay al menos tres razones comunes para usarlo, todas involucrando situaciones en las que el valor de la variable puede cambiar sin acción del código visible: cuando interactúa con hardware que cambia el valor en sí mismo; cuando hay otro hilo en ejecución que también usa la variable; o cuando hay un controlador de señal que puede cambiar el valor de la variable.
Digamos que tiene una pequeña pieza de hardware que está asignada a la RAM en algún lugar y que tiene dos direcciones: un puerto de comando y un puerto de datos:
Ahora quieres enviar algún comando:
Parece fácil, pero puede fallar porque el compilador es libre de cambiar el orden en que se escriben los datos y los comandos. Esto haría que nuestro pequeño gadget emita comandos con el valor de datos anterior. También eche un vistazo al ciclo de espera mientras está ocupado. Ese será optimizado. El compilador intentará ser inteligente, leerá el valor de isbusy solo una vez y luego entrará en un bucle infinito. Eso no es lo que quieres.
La forma de evitar esto es declarar el dispositivo puntero como volátil. De esta manera, el compilador se ve obligado a hacer lo que escribió. No puede eliminar las asignaciones de memoria, no puede almacenar en caché las variables en los registros y tampoco puede cambiar el orden de las asignaciones:
Esta es la versión correcta:
fuente
volatile
en C realmente surgió con el propósito de no almacenar en caché los valores de la variable automáticamente. Le indicará al compilador que no almacene en caché el valor de esta variable. Por lo tanto, generará código para tomar el valor de lavolatile
variable dada de la memoria principal cada vez que la encuentre. Este mecanismo se utiliza porque el sistema operativo o cualquier interrupción pueden modificar el valor en cualquier momento. Por lo tanto, usarvolatile
nos ayudará a acceder al valor nuevamente cada vez.fuente
volatile
era hacer posible que los compiladores optimizaran el código mientras permitían a los programadores lograr la semántica que se lograría sin tales optimizaciones. Los autores del Estándar esperaban que las implementaciones de calidad admitirían cualquier semántica que fuera útil, dadas sus plataformas de destino y campos de aplicación, y no esperaban que los escritores de compiladores buscaran ofrecer la semántica de menor calidad que se ajustara al Estándar y no fueran 100% estúpido (tenga en cuenta que los autores de la Norma reconocen explícitamente en la justificación ...Otro uso para
volatile
es manejadores de señal. Si tienes un código como este:El compilador puede notar que el cuerpo del bucle no toca la
quit
variable y convierte el bucle en unwhile (true)
bucle. Incluso si laquit
variable se establece en el controlador de señal paraSIGINT
ySIGTERM
; el compilador no tiene forma de saber eso.Sin embargo, si
quit
se declara la variablevolatile
, el compilador se ve obligado a cargarla cada vez, ya que puede modificarse en otro lugar. Esto es exactamente lo que quieres en esta situación.fuente
quit
, el compilador puede optimizarlo en un bucle constante, suponiendo que no hay forma dequit
cambiar entre iteraciones. NB: Esto no es necesariamente un buen sustituto para la programación real segura para subprocesos.volatile
otros marcadores, se supondrá que nada fuera del ciclo modifica esa variable una vez que ingresa al ciclo, incluso si es una variable global.extern int global; void fn(void) { while (global != 0) { } }
congcc -O3 -S
y mirar el archivo de conjunto resultante, en mi máquina lo hacemovl global(%rip), %eax
;testl %eax, %eax
;je .L1
;.L4: jmp .L4
, es decir, un bucle infinito si el global no es cero. Luego intenta agregarvolatile
y ver la diferencia.volatile
le dice al compilador que su variable puede cambiarse por otros medios, además del código que está accediendo a ella. por ejemplo, puede ser una ubicación de memoria asignada de E / S. Si esto no se especifica en tales casos, se pueden optimizar algunos accesos variables, por ejemplo, su contenido se puede mantener en un registro y la ubicación de la memoria no se puede volver a leer.fuente
Vea este artículo de Andrei Alexandrescu, " volátil: el mejor amigo del programador multiproceso "
El artículo se aplica a ambos
C
yC++
.También vea el artículo " C ++ y los peligros del bloqueo doblemente revisado " por Scott Meyers y Andrei Alexandrescu:
fuente
volatile
No garantiza la atomicidad.Mi explicación simple es:
En algunos escenarios, basados en la lógica o el código, el compilador hará la optimización de las variables que cree que no cambian. La
volatile
palabra clave evita que se optimice una variable.Por ejemplo:
Del código anterior, el compilador puede pensar que
usb_interface_flag
se define como 0, y que en el ciclo while será cero para siempre. Después de la optimización, el compilador lo trataráwhile(true)
todo el tiempo, dando como resultado un bucle infinito.Para evitar este tipo de escenarios, declaramos que el indicador es volátil, le estamos diciendo al compilador que este valor puede ser cambiado por una interfaz externa u otro módulo de programa, es decir, no lo optimice. Ese es el caso de uso para volátiles.
fuente
Un uso marginal para volátil es el siguiente. Supongamos que desea calcular la derivada numérica de una función
f
:El problema es que
x+h-x
generalmente no es igual ah
debido a errores de redondeo. Piénselo: cuando resta números muy cercanos, pierde muchos dígitos significativos que pueden arruinar el cálculo de la derivada (piense 1.00001 - 1). Una posible solución podría serpero dependiendo de la plataforma y los conmutadores del compilador, la segunda línea de esa función puede ser eliminada por un compilador de optimización agresiva. Entonces escribes en su lugar
para forzar al compilador a leer la ubicación de la memoria que contiene hh, perdiendo una eventual oportunidad de optimización.
fuente
h
ohh
en una fórmula derivada? Cuandohh
se calcula, la última fórmula la usa como la primera, sin diferencia. Tal vez debería ser(f(x+h) - f(x))/hh
?h
yhh
es quehh
la operación trunca a una potencia negativa de dosx + h - x
. En este caso,x + hh
yx
difieren exactamente porhh
. También puede tomar su fórmula, se le dará el mismo resultado, ya quex + h
yx + hh
son iguales (es el denominador que es importante en este caso).x1=x+h; d = (f(x1)-f(x))/(x1-x)
? sin usar el volátil.-ffast-math
o equivalente.Hay dos usos. Estos se usan especialmente con mayor frecuencia en el desarrollo integrado.
El compilador no optimizará las funciones que usan variables definidas con palabras clave volátiles
Volátil se usa para acceder a ubicaciones de memoria exactas en RAM, ROM, etc. Esto se usa con más frecuencia para controlar dispositivos mapeados en memoria, acceder a registros de CPU y localizar ubicaciones de memoria específicas.
Ver ejemplos con listado de ensamblaje. Re: Uso de la palabra clave "volátil" C en desarrollo integrado
fuente
Volatile también es útil cuando desea forzar al compilador a no optimizar una secuencia de código específica (por ejemplo, para escribir un micro-benchmark).
fuente
Mencionaré otro escenario donde los volátiles son importantes.
Suponga que asigna un archivo de memoria a un archivo para una E / S más rápida y ese archivo puede cambiar detrás de escena (por ejemplo, el archivo no está en su disco duro local, sino que es servido a través de la red por otra computadora).
Si accede a los datos del archivo mapeado en memoria a través de punteros a objetos no volátiles (en el nivel del código fuente), entonces el código generado por el compilador puede obtener los mismos datos varias veces sin que usted lo sepa.
Si esos datos cambian, su programa puede usar dos o más versiones diferentes de los datos y entrar en un estado inconsistente. Esto puede conducir no solo a un comportamiento lógicamente incorrecto del programa, sino también a agujeros de seguridad explotables en él si procesa archivos no confiables o archivos desde ubicaciones no confiables.
Si le importa la seguridad, y debería hacerlo, este es un escenario importante a considerar.
fuente
volátil significa que es probable que el almacenamiento cambie en cualquier momento y se cambie pero algo fuera del control del programa de usuario. Esto significa que si hace referencia a la variable, el programa siempre debe verificar la dirección física (es decir, una entrada asignada Fifo), y no utilizarla en una memoria caché.
fuente
La Wiki dice todo sobre
volatile
:Y el documento del kernel de Linux también hace una excelente notación sobre
volatile
:fuente
En mi opinión, no debes esperar demasiado de
volatile
. Para ilustrar, mire el ejemplo en la respuesta altamente votada de Nils Pipenbrinck .Yo diría que su ejemplo no es adecuado para
volatile
.volatile
solo se usa para: evitar que el compilador realice optimizaciones útiles y deseables . No tiene nada que ver con el hilo seguro, el acceso atómico o incluso el orden de la memoria.En ese ejemplo:
el
gadget->data = data
antesgadget->command = command
solo solo está garantizado en el código compilado por el compilador. En tiempo de ejecución, el procesador aún puede reordenar los datos y la asignación de comandos, en relación con la arquitectura del procesador. El hardware podría obtener los datos incorrectos (supongamos que el gadget está asignado a E / S de hardware). La barrera de memoria es necesaria entre los datos y la asignación de comandos.fuente
volatile
está escrito, parece que está degradando el rendimiento sin ninguna razón. En cuanto a si es suficiente, eso dependerá de otros aspectos del sistema que el programador pueda conocer más que el compilador. Por otro lado, si un procesador garantiza que una instrucción para escribir en una determinada dirección vaciará el caché de la CPU pero un compilador no proporcionó ninguna forma de vaciar las variables almacenadas en caché de registro de las que la CPU no sabe nada, vaciar el caché sería inútil.En el lenguaje diseñado por Dennis Ritchie, cada acceso a cualquier objeto, que no sean objetos automáticos cuya dirección no se haya tomado, se comportaría como si calculara la dirección del objeto y luego leyera o escribiera el almacenamiento en esa dirección. Esto hizo que el lenguaje fuera muy poderoso, pero limitó severamente las oportunidades de optimización.
Si bien podría haber sido posible agregar un calificador que invitaría a un compilador a suponer que un objeto en particular no se cambiaría de manera extraña, tal suposición sería apropiada para la gran mayoría de los objetos en los programas en C, y habría tenido No ha sido práctico agregar un calificador a todos los objetos para los cuales tal suposición sería apropiada. Por otro lado, algunos programas necesitan usar algunos objetos para los cuales tal suposición no sería válida. Para resolver este problema, el Estándar dice que los compiladores pueden suponer que los objetos que no se declaran
volatile
no tendrán su valor observado o cambiado de formas que están fuera del control del compilador, o estarían fuera del entendimiento de un compilador razonable.Debido a que varias plataformas pueden tener diferentes formas en que los objetos podrían observarse o modificarse fuera del control de un compilador, es apropiado que los compiladores de calidad para esas plataformas difieran en su manejo exacto de la
volatile
semántica. Desafortunadamente, debido a que el Estándar no sugirió que los compiladores de calidad destinados a la programación de bajo nivel en una plataforma deben manejarvolatile
de una manera que reconozca todos los efectos relevantes de una operación de lectura / escritura en particular en esa plataforma, muchos compiladores no cumplen de modo que sea más difícil procesar cosas como E / S en segundo plano de una manera que sea eficiente pero que no se pueda romper con las "optimizaciones" del compilador.fuente
En términos simples, le dice al compilador que no haga ninguna optimización en una variable en particular. Las variables que se asignan al registro del dispositivo son modificadas indirectamente por el dispositivo. En este caso, se debe usar volátil.
fuente
Se puede cambiar un volátil desde fuera del código compilado (por ejemplo, un programa puede asignar una variable volátil a un registro mapeado en memoria). El compilador no aplicará ciertas optimizaciones al código que maneja una variable volátil, por ejemplo, ganó ' t cárguelo en un registro sin escribirlo en la memoria. Esto es importante cuando se trata de registros de hardware.
fuente
Como muchos sugieren aquí, el uso popular de la palabra clave volátil es omitir la optimización de la variable volátil.
La mejor ventaja que viene a la mente, y vale la pena mencionar después de leer sobre volátiles es: evitar el retroceso de la variable en caso de a
longjmp
. Un salto no local.¿Qué significa esto?
Simplemente significa que el último valor se retendrá después de desenrollar la pila , para volver a algún marco de pila anterior; típicamente en caso de algún escenario erróneo.
Dado que estaría fuera del alcance de esta pregunta, no voy a entrar en detalles
setjmp/longjmp
aquí, pero vale la pena leer al respecto; y cómo se puede usar la función de volatilidad para retener el último valor.fuente
no permite que el compilador cambie automáticamente los valores de las variables. Una variable volátil es para uso dinámico.
fuente