C++:Abrindo um arquivo em modo não exclusivo
-
09-06-2019 - |
Pergunta
Tenho que desenvolver um aplicativo que analise um arquivo de log e envie dados específicos para um servidor.Ele deve rodar em Linux e Windows.
O problema aparece quando quero testar o sistema de rolagem de log (que anexa .1 ao nome do cria um novo com o mesmo nome).No Windows (ainda não testei no Linux) não consigo renomear um arquivo que abri com std::ifstream() (acesso exclusivo?) mesmo se eu abri-lo no "modo de entrada" (ios::in) .
Existe uma maneira multiplataforma de abrir arquivos de maneira não exclusiva?
Solução
Existe uma maneira de abrir arquivos de forma não exclusiva,
Sim, usando Win32, passando os vários sinalizadores FILE_SHARE_Xxxx para CreateFile.
é multiplataforma?
Não, requer código específico da plataforma.
Devido a preocupações irritantes de compatibilidade com versões anteriores (aplicativos DOS, sendo de tarefa única, assumem que nada pode excluir um arquivo deles, ou seja,que eles podem fclose() e então fopen() sem que nada dê errado;O Win16 preservou essa suposição para facilitar a portabilidade de aplicativos DOS, o Win32 preservou essa suposição para facilitar a portabilidade de aplicativos Win16, e é horrível), o padrão do Windows é abrir arquivos exclusivamente.
A infraestrutura do sistema operacional subjacente suporta exclusão/renomeação de arquivos abertos (embora eu acredite que tenha a restrição de que arquivos mapeados na memória não possam ser excluídos, o que acho que não é uma restrição encontrada no *nix), mas a semântica de abertura padrão não.
C++ não tem noção de nada disso;o ambiente operacional C++ é praticamente igual ao ambiente operacional DOS – nenhum outro aplicativo é executado simultaneamente, portanto, não há necessidade de controlar o compartilhamento de arquivos.
Outras dicas
Não é a operação de leitura que exige o modo exclusivo, é a renomeação, porque é essencialmente o mesmo que mover o arquivo para um novo local.
Não tenho certeza, mas não acho que isso possa ser feito.Em vez disso, tente copiar o arquivo e, posteriormente, excluir/substituir o arquivo antigo quando ele não for mais lido.
A semântica do sistema de arquivos Win32 exige que um arquivo renomeado não esteja aberto (em nenhum modo) no momento da renomeação.Você precisará fechar o arquivo, renomeá-lo e criar o novo arquivo de log.
A semântica do sistema de arquivos Unix permite renomear um arquivo que está aberto porque o nome do arquivo é apenas um ponteiro para o inode.
Se você está apenas lendo o arquivo, sei que isso pode ser feito com a API do Windows CreateFile.Basta especificar file_share_delete | File_share_read | File_share_write como entrada para DWSharemode.
Infelizmente, isso não é multiplataforma.Mas pode haver algo semelhante para Linux.
Consulte msdn para obter mais informações sobre CreateFile.
EDITAR:Apenas uma nota rápida sobre o comentário de Greg Hewgill.Acabei de testar com o material FILE_SHARE* (tenha 100% de certeza).E é possível excluir e renomear arquivos no Windows se você abrir somente leitura e especificar os parâmetros FILE_SHARE*.
Eu garantiria que você não mantivesse os arquivos abertos.Isso leva a coisas estranhas se o seu aplicativo travar, por exemplo.O que eu faria:
- Resumo (leitura/gravação/rolagem para um novo arquivo) em uma classe e organize o fechamento do arquivo quando quiser passar para um novo nessa classe.(esta é a maneira mais simples e, como você já possui o código de rollover, já está na metade do caminho.)
- Se você deve ter vários pontos de acesso de leitura/gravação, precisa de todos os recursos do fstreams e não deseja escrever um wrapper completo, então a única solução multiplataforma que consigo pensar é sempre fechar o arquivo quando você não precisar dele e faça com que o código de rollover tente adquirir acesso exclusivo ao arquivo algumas vezes quando ele precisar ser rollover antes de desistir.