Las variables volátiles
Son aquellas variables cuyo valor siempre que se lee, se lee de memoria, y siempre que se escribe, se escribe en memoria. Pasan el mínimo tiempo posible en los registros del microprocesador.
Al declararlas volátiles se le está indicando al compilador que no haga optimizaciones que eviten accesos a memoria para leerlas/escribirlas.
En un código como éste:
moehren*=2;
El compilador podría optimizar y generar código que:
- lee el valor de la variable de la pila (p.ej. x<-[moehren]) y lo guarda en un registro del micro
- suma 7 al registro (x+7)
- multiplica el registro por 2 ((x+7)*2)
- guarda el registro en la posición de la pila dedicada a la variable moehren ((x+7)*2->[moehren])
Hasta aquí todo en orden, el valor de mohren==(mohren+7)*2 y el número de accesos a memoria se minimiza, lo que (casi) siempre es de agradecer.
Y ahora vamos a suponer, que mientras una sentencia y otra, se recibe una interrupción y se ejecuta entre medias una sentencia:
El orden de ejecución sería como sigue:
- moehren+=7;
- moehren-=8;
- moehren*=2;
Con las optimizaciones del compilador esto queda:
- lee el valor de la variable (x<-[moehren]) de la pila y lo guarda en un registro del micro
- suma 7 al registro (x+7)
- salta a la ISR (rutina de servicio de la interrupción, que es, sencillamente, una función que se ejecutará en el medio)
- guarda los registros en la pila
- lee el valor de la variable de la pila (y<-[moehren]) y lo guarda en un registro del micro
- le resta 8 a ese registro (y-8)
- guarda ese registro en la posición de la pila dedicada a la variable moehren (y-8 -> [moehren])
- restaura los registros guardados en la pila y retorna a donde estaba antes de saltar a la ISR
- multiplica el registro por 2 ((x+7)*2)
- guarda el registro en la posición de la pila dedicada a la variable moehren ((x+7)*2->[moehren])
Es decir, que el valor de moehren==(moehren+7)*2, como si la ISR no se hubiera ejecutado.
Si por el contratio declaramos moehren como volatile:
Cada vez que se lea/escriba esa variable, se accederá a memoria, lo cual es más lento, pero nos evita situaciones como la anterior.
El orden de ejecución del código generado en un caso similar al ejemplo anterior sería algo así:
- lee el valor de la variable (x<-[moehren]) de la pila a un registro
- suma 7 al registro (x+7)
- guarda el registro en la variable moehren (x+7 -> [moehren])
- salta a la ISR
- guarda los registros en la pila
- lee el valor de la variable (y<-[moehren], o sea y==x+7) de la pila a un registro
- le resta 8 a ese registro (y-8, que es lo mismo que x+7-8)
- guarda el registro en la variable moehren (y -> [moehren], o bien x+7-8 -> [moehren])
- restaura los registros guardados en la pila y retorna a donde estaba antes de saltar a la ISR
- lee el valor de la variable (z<-[moehren], z==x+7-8) de la pila a un registro
- multiplica el registro por 2 (z*2, (x+7-8)*2)
- guarda el registro en la variable moehren (z -> [moehren], o bien (x+7-8)*2 -> [moehren])
El valor que tiene ahora moehren==(moehren-1)*2. Ahora sí que se nota la ejecución de la ISR.
Ojo, porque con esto no se evita la necesidad de las primitivas de sincronización. Nada garantiza que dos procesos no accedan paralelamente y de forma no atómica a la variable en memoria. Si estamos trabajando en un entorno monotarea (un microcontrolador, normalmente), esto nos dará igual.






