c linux gcc x86 setjmp

¿Por qué volatile funciona para setjmp/longjmp?



linux gcc (3)

El quid está en la optimización en este escenario: el optimizador naturalmente esperaría que una llamada a una función como setjmp () no cambie ninguna variable local, y optimice los accesos de lectura a la variable. Ejemplo:

int foo; foo = 5; if ( setjmp(buf) != 2 ) { if ( foo != 5 ) { optimize_me(); longjmp(buf, 2); } foo = 6; longjmp( buf, 1 ); return 1; } return 0;

Un optimizador puede optimizar la línea optim_me porque foo se ha escrito en la línea 2, no es necesario que se lea en la línea 4 y se puede suponer que es 5. Además, la asignación en la línea 5 se puede eliminar porque nunca se leería foo de nuevo si longjmp era una función normal de C. Sin embargo, setjmp () y longjmp () perturban el flujo de código de una manera que el optimizador no puede explicar, rompiendo este esquema. El resultado correcto de este código sería una terminación; Con la línea optimizada, tenemos un bucle sin fin.

Después de invocar a longjmp (), no se debe acceder a los objetos locales calificados no volátiles si sus valores podrían haber cambiado desde la invocación de setjmp (). Su valor en este caso se considera indeterminado, y acceder a ellos es un comportamiento indefinido.

Ahora mi pregunta es ¿por qué los trabajos volátiles en esta situación? ¿No cambiaría esa variable volátil aún fallaría el longjmp? Por ejemplo, ¿cuánto tiempo funcionará correctamente en el ejemplo que se muestra a continuación? Cuando el código vuelve a setjmp después de longjmp, ¿el valor de local_var no sería 2 en lugar de 1?

void some_function() { volatile int local_var = 1; setjmp( buf ); local_var = 2; longjmp( buf, 1 ); }


La razón más común para los problemas en ausencia de un calificador "volátil" es que los compiladores a menudo colocan variables locales en los registros. Estos registros casi seguramente se usarán para otras cosas entre setjmp y longjmp. La forma más práctica de garantizar que el uso de estos registros para otros fines no hará que las variables contengan los valores incorrectos después de que longjmp almacene en caché los valores de esos registros en jmp_buf. Esto funciona, pero tiene el efecto secundario de que no hay forma de que el compilador actualice el contenido de jmp_buf para reflejar los cambios realizados en las variables después de que los registros se almacenen en caché.

Si ese fuera el único problema, el resultado de acceder a variables locales no declaradas volátiles sería indeterminado, pero no un comportamiento indefinido. Sin embargo, hay un problema incluso con las variables de memoria a las que alude Thiton: incluso si una variable local está asignada en la pila, un compilador podría reescribir esa variable con otra cosa cada vez que determine que su valor ya no es necesario. Por ejemplo, un compilador podría identificar que algunas variables nunca están ''activas'' cuando una rutina llama a otras rutinas, coloca esas variables más bajas en su marco de pila y las abre antes de llamar a otras rutinas. En tal escenario, aunque las variables existían en la memoria cuando se llama a setjmp (), esa memoria podría haberse reutilizado para otra cosa, como mantener la dirección de retorno. Como tal, después de que se realiza el longjmp (), la memoria se consideraría sin inicializar.

Agregar un calificador ''volátil'' a la definición de una variable hace que el almacenamiento se reserve exclusivamente para el uso de esa variable, siempre que esté dentro del alcance. Independientemente de lo que ocurra entre setjmp y longjmp, siempre que el control no haya dejado el alcance donde se declaró la variable, nada puede usar esa ubicación para ningún otro propósito.


Registros setjmp y longjmp clobber. Si una variable se almacena en un registro, su valor se pierde después de un longjmp .

A la inversa, si se declara como volatile , cada vez que se escribe, se almacena de nuevo en la memoria, y cada vez que se lee, se vuelve a leer de la memoria cada vez. Esto perjudica el rendimiento, ya que el compilador tiene que hacer más accesos a la memoria en lugar de usar un registro, pero hace que el uso de la variable sea seguro frente al uso longjmp .