Por que é volatile
necessário em C? Para que isso é usado? O que isso fará?
c
declaration
volatile
Jonathan Leffler
fonte
fonte
Respostas:
Volátil diz ao compilador para não otimizar nada que tenha a ver com a variável volátil.
Há pelo menos três razões comuns para usá-lo, todas envolvendo situações em que o valor da variável pode ser alterado sem ação do código visível: Quando você faz interface com o hardware que altera o próprio valor; quando houver outro thread em execução que também use a variável; ou quando houver um manipulador de sinal que possa alterar o valor da variável.
Digamos que você tenha um pequeno pedaço de hardware mapeado para a RAM em algum lugar e que tenha dois endereços: uma porta de comando e uma porta de dados:
Agora você deseja enviar algum comando:
Parece fácil, mas pode falhar porque o compilador é livre para alterar a ordem em que os dados e comandos são gravados. Isso faria nosso pequeno gadget emitir comandos com o valor de dados anterior. Veja também o loop de espera enquanto ocupado. Essa será otimizada. O compilador tentará ser inteligente, ler o valor de isbusy apenas uma vez e depois entrar em um loop infinito. Não é isso que você quer.
A maneira de contornar isso é declarar o dispositivo indicador como volátil. Dessa forma, o compilador é forçado a fazer o que você escreveu. Ele não pode remover as atribuições de memória, não pode armazenar em cache variáveis nos registradores e também não pode alterar a ordem das atribuições:
Esta é a versão correta:
fonte
volatile
em C, na verdade, surgiu com o objetivo de não armazenar em cache os valores da variável automaticamente. Ele informará ao compilador para não armazenar em cache o valor dessa variável. Portanto, ele gerará código para tirar o valor davolatile
variável fornecida da memória principal toda vez que a encontrar. Esse mecanismo é usado porque a qualquer momento o valor pode ser modificado pelo sistema operacional ou por qualquer interrupção. Portanto, o usovolatile
nos ajudará a acessar o valor novamente todas as vezes.fonte
volatile
possibilitar aos compiladores otimizar o código e, ao mesmo tempo, permitir que os programadores atinjam a semântica que seria alcançada sem essas otimizações. Os autores do Padrão esperavam que implementações de qualidade suportassem qualquer semântica que fosse útil, considerando suas plataformas de destino e campos de aplicação, e não esperavam que os escritores do compilador procurassem oferecer a semântica de menor qualidade que estivesse em conformidade com o Padrão e não fosse 100% estúpida (note que os autores da Norma reconhecer explicitamente a lógica ...Outro uso para
volatile
é manipuladores de sinal. Se você tiver um código como este:É permitido ao compilador notar que o corpo do loop não toca na
quit
variável e converte o loop em umwhile (true)
loop. Mesmo se aquit
variável estiver definida no manipulador de sinal paraSIGINT
eSIGTERM
; o compilador não tem como saber disso.No entanto, se a
quit
variável for declaradavolatile
, o compilador é forçado a carregá-la sempre, porque pode ser modificado em outro lugar. É exatamente isso que você deseja nesta situação.fonte
quit
, o compilador pode otimizá-lo em um loop constante, assumindo que não há comoquit
mudar entre iterações. Nota: isso não é necessariamente um bom substituto para a programação segura de thread real.volatile
ou de outros marcadores, ele assumirá que nada fora do loop modifica essa variável depois que entra no loop, mesmo que seja uma variável global.extern int global; void fn(void) { while (global != 0) { } }
comgcc -O3 -S
e olhar para o arquivo de montagem resultante, na minha máquina que fazmovl global(%rip), %eax
;testl %eax, %eax
;je .L1
;.L4: jmp .L4
, ou seja, um loop infinito se o global não for zero. Em seguida, tente adicionarvolatile
e veja a diferença.volatile
informa ao compilador que sua variável pode ser alterada por outros meios, além do código que está acessando. por exemplo, pode ser um local de memória mapeado de E / S. Se isso não for especificado nesses casos, alguns acessos variáveis podem ser otimizados, por exemplo, seu conteúdo pode ser mantido em um registro e a localização da memória não é lida novamente.fonte
Veja este artigo de Andrei Alexandrescu, " volátil - o melhor amigo do programador multithread "
O artigo se aplica a ambos
C
eC++
.Veja também o artigo " C ++ e os perigos do bloqueio com dupla verificação ", de Scott Meyers e Andrei Alexandrescu:
fonte
volatile
não garante atomicidade.Minha explicação simples é:
Em alguns cenários, com base na lógica ou no código, o compilador fará a otimização de variáveis que acha que não são alteradas. A
volatile
palavra-chave impede que uma variável seja otimizada.Por exemplo:
A partir do código acima, o compilador pode pensar que
usb_interface_flag
está definido como 0 e que, no loop while, será zero para sempre. Após a otimização, o compilador o trataráwhile(true)
o tempo todo, resultando em um loop infinito.Para evitar esse tipo de cenário, declaramos o sinalizador como volátil, dizendo ao compilador que esse valor pode ser alterado por uma interface externa ou outro módulo do programa, ou seja, não o otimize. Esse é o caso de uso para voláteis.
fonte
Um uso marginal para voláteis é o seguinte. Digamos que você queira calcular a derivada numérica de uma função
f
:O problema é que
x+h-x
geralmente não é igual ah
devido a erros de arredondamento. Pense nisso: ao subtrair números muito próximos, você perde muitos dígitos significativos que podem arruinar o cálculo da derivada (pense em 1.00001 - 1). Uma possível solução alternativa poderia sermas, dependendo das opções da plataforma e do compilador, a segunda linha dessa função pode ser eliminada por um compilador de otimização agressiva. Então você escreve
forçar o compilador a ler o local da memória que contém hh, perdendo uma eventual oportunidade de otimização.
fonte
h
ouhh
na fórmula derivada? Quandohh
é calculada, a última fórmula a usa como a primeira, sem diferença. Talvez devesse ser(f(x+h) - f(x))/hh
?h
ehh
é quehh
é truncada para uma potência negativa de dois pela operaçãox + h - x
. Nesse caso,x + hh
ex
diferem exatamente porhh
. Você também pode ter sua fórmula, que lhe dará o mesmo resultado, uma vezx + h
ex + hh
são iguais (é o denominador que é importante aqui).x1=x+h; d = (f(x1)-f(x))/(x1-x)
? sem usar o volátil.-ffast-math
ou equivalente.Existem dois usos. Estes são especialmente usados com mais frequência no desenvolvimento incorporado.
O compilador não otimizará as funções que usam variáveis definidas com palavras-chave voláteis
Volátil é usado para acessar os locais exatos da memória na RAM, ROM, etc ... É usado com mais freqüência para controlar dispositivos mapeados na memória, acessar registros da CPU e localizar locais específicos da memória.
Veja exemplos com a lista de montagem. Uso da palavra-chave "volátil" em C no desenvolvimento incorporado
fonte
O volátil também é útil quando você deseja forçar o compilador a não otimizar uma sequência de código específica (por exemplo, para escrever um micro-benchmark).
fonte
Vou mencionar outro cenário em que os voláteis são importantes.
Suponha que você mapeie um arquivo de memória para E / S mais rápida e esse arquivo possa ser alterado nos bastidores (por exemplo, o arquivo não está no seu disco rígido local, mas é servido na rede por outro computador).
Se você acessar os dados do arquivo mapeado na memória por meio de ponteiros para objetos não voláteis (no nível do código-fonte), o código gerado pelo compilador poderá buscar os mesmos dados várias vezes sem que você esteja ciente.
Se esses dados mudarem, o seu programa poderá usar duas ou mais versões diferentes dos dados e entrar em um estado inconsistente. Isso pode levar não apenas ao comportamento logicamente incorreto do programa, mas também a falhas de segurança exploráveis, se ele processar arquivos não confiáveis ou arquivos de locais não confiáveis.
Se você se preocupa com segurança e deve, este é um cenário importante a ser considerado.
fonte
volátil significa que o armazenamento provavelmente mudará a qualquer momento e será alterado, mas algo fora do controle do programa do usuário. Isso significa que, se você referenciar a variável, o programa deve sempre verificar o endereço físico (isto é, uma entrada mapeada fifo) e não usá-lo em cache.
fonte
O Wiki diz tudo sobre
volatile
:E o documento do kernel Linux também faz uma excelente notação sobre
volatile
:fonte
Na minha opinião, você não deve esperar muito
volatile
. Para ilustrar, veja o exemplo da resposta altamente votada de Nils Pipenbrinck .Eu diria que o exemplo dele não é adequado
volatile
.volatile
é usado apenas para: impedir que o compilador faça otimizações úteis e desejáveis . Não é nada sobre o segmento seguro, acesso atômico ou mesmo ordem de memória.Nesse exemplo:
o
gadget->data = data
antesgadget->command = command
somente é garantido no código compilado pelo compilador. No tempo de execução, o processador ainda pode reordenar a atribuição de dados e comandos, em relação à arquitetura do processador. O hardware pode obter os dados incorretos (suponha que o gadget esteja mapeado para a E / S de hardware). A barreira da memória é necessária entre a atribuição de dados e comandos.fonte
volatile
está prejudicando o desempenho sem motivo. Se é suficiente, isso dependerá de outros aspectos do sistema que o programador possa conhecer mais do que o compilador. Por outro lado, se um processador garantir que uma instrução para gravar em um determinado endereço liberar o cache da CPU, mas um compilador não tiver como liberar variáveis armazenadas em cache de registro que a CPU não conhece, a descarga do cache seria inútil.Na linguagem projetada por Dennis Ritchie, todo acesso a qualquer objeto, exceto objetos automáticos cujo endereço não havia sido obtido, se comportaria como se calculasse o endereço do objeto e depois lesse ou gravasse o armazenamento naquele endereço. Isso tornou o idioma muito poderoso, mas limitou severamente as oportunidades de otimização.
Embora possa ter sido possível adicionar um qualificador que convidaria um compilador a assumir que um objeto específico não seria alterado de maneiras estranhas, essa suposição seria apropriada para a grande maioria dos objetos nos programas em C, e teria Foi impraticável adicionar um qualificador a todos os objetos para os quais essa suposição seria apropriada. Por outro lado, alguns programas precisam usar alguns objetos para os quais tal suposição não seria válida. Para resolver esse problema, o Padrão diz que os compiladores podem assumir que objetos que não são declarados
volatile
não terão seu valor observado ou alterado de maneiras que estão fora do controle do compilador ou estariam fora do entendimento de um compilador razoável.Como várias plataformas podem ter maneiras diferentes pelas quais objetos podem ser observados ou modificados fora do controle de um compilador, é apropriado que os compiladores de qualidade para essas plataformas sejam diferentes no tratamento exato da
volatile
semântica. Infelizmente, como o Padrão falhou em sugerir que os compiladores de qualidade destinados à programação de baixo nível em uma plataforma devem lidar devolatile
maneira a reconhecer todos e quaisquer efeitos relevantes de uma operação específica de leitura / gravação nessa plataforma, muitos compiladores não conseguem fazer isso. portanto, de maneiras que tornam mais difícil processar coisas como E / S em segundo plano de uma maneira eficiente, mas que não pode ser interrompida pelas "otimizações" do compilador.fonte
Em termos simples, ele diz ao compilador para não fazer nenhuma otimização em uma variável específica. Variáveis que são mapeadas para o registro do dispositivo são modificadas indiretamente pelo dispositivo. Nesse caso, volátil deve ser usado.
fonte
Um volátil pode ser alterado de fora do código compilado (por exemplo, um programa pode mapear uma variável volátil para um registro mapeado na memória.) O compilador não aplicará certas otimizações ao código que manipula uma variável volátil - por exemplo, ele ganhou ' não o carregue em um registro sem gravá-lo na memória. Isso é importante ao lidar com registros de hardware.
fonte
Conforme sugerido por muitos aqui, o uso popular da palavra-chave volátil é ignorar a otimização da variável volátil.
A melhor vantagem que vem à mente, e vale a pena mencionar depois de ler sobre o volátil, é - impedir a reversão da variável no caso de a
longjmp
. Um salto não local.O que isto significa?
Simplesmente significa que o último valor será retido após o desenrolamento da pilha , para retornar a algum quadro anterior da pilha; normalmente no caso de algum cenário incorreto.
Como estaria fora do escopo desta questão, não vou entrar em detalhes
setjmp/longjmp
aqui, mas vale a pena ler sobre isso; e como o recurso de volatilidade pode ser usado para reter o último valor.fonte
não permite que o compilador altere automaticamente os valores das variáveis. uma variável volátil é para uso dinâmico.
fonte