Manter arquivo sempre "atualizado" [RESOLVIDO]

1. Manter arquivo sempre "atualizado" [RESOLVIDO]

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 31/08/2016 - 10:56h

Como eu posso manter um arquivo aberto sempre atualizado tanto em C quanto em C++? Digo, sempre atualizado assim:
suponha que eu abra um arquivo com fopen() em modo somente leitura, depois entro num loop para ler somente a primeira linha, ou seja, sempre executando um rewind no arquivo. Então, no arquivo em disco que foi aberto pelo meu programa, eu uso outro programa e modifico a primeira linha.
O que eu quero é não ser obrigado a reexecutar fopen(), ou ter de abrir e fechar o arquivo todo loop, apenas para ter de ler a nova primeira linha que foi modificado pelo programa externo.

Tem como fazer isso?


  


2. Re: Manter arquivo sempre "atualizado" [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 31/08/2016 - 13:39h

Agora eu não tenho como consultar, mas eu sugiro que você consulte a documentação de rewind() e fseek() para confirmar se o conteúdo do buffer é incondicionalmente descartado quando você move o ponteiro do arquivo, mesmo que esse deslocamento seja curto. Eu acho que deve ser, e isso, por si só, deve (ou deveria) bastar para fazer o que você descreveu.


3. Re: Manter arquivo sempre "atualizado" [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 01/09/2016 - 15:25h

Eu testei o seguinte: numa janela, eu coloquei o seguinte script para rodar indefinidamente
while : ; do date +%s > /tmp/x ; sleep 1; done 
e, noutra janela, compilei o redei o seguinte programa.
#include <stdio.h>
#include <unistd.h>

int main(void){
FILE *f;
int i;
f=fopen("/tmp/x", "r");
while(1){
fseek(f, 0, SEEK_SET);
fscanf(f, "%d", &i);
printf("%d\n", i);
sleep(2);
}
return 0;
}


O resultado foi que o valor exibido pela segunda janela ficou invariável, com o valor lido na primeira iteração sendo repetidos indefinidamente.

Confesso que isso me desapontou um pouco, porque eu esperava que, ao perceber que o apontador de posição no arquivo retrocedeu, o sistema desprezasse o conteúdo que já foi lido, e fosse forçado a relê-lo do disco. Pelo visto, não o faz, e não me parece que isso contrarie o que diz o padrão.

Eu fiz algumas brincadeiras, e consegui forçar a releitura. A primeira foi fazer deslocamentos absurdamente grandes (que, no entanto, funcionam no UNIX), para ter cereteza de que nenhuma parte do buffer seria reaproveitada, colocando uma outra chamada a fseek() antes da chamada já mostrada.
    fseek(f, ((unsigned long)-1)>>1, SEEK_SET); 

No entanto, eu não sei se isso funcionaria em qualquer sistema, mesmo usando apenas funções padronizadas, porque o comportamento de fazer um deslocamento potencialmente maior do que o tamanho arquivo é algo que o UNIX consegue mapear bem internamente (com lseek()), e parece que também funciona no Windows (com SetFilePointerEx()), mas outros sistemas podem não tratar tão bem.

Outra brincadeira que funcionou, mas que aparentemente é exclusiva de versões mais ou menos recentes da glibc, foi trocar, no programa original, com apenas uma chamada a fseek(), o modo de abertura do arquivo de "r" para "rm", que faz com que o arquivo seja, se possível, aberto e mapeado com mmap, de modo que operações de escrita e de leitura sejam mapeadas como acessos diretos à memória. Como mapeamento de memória serve justamente para comunicação entre processos, essa tentativa também funcionou. Eu não sei, porém, como funciona mmap() com arquivos que possam variar de tamanho.


4. Re: Manter arquivo sempre

Enzo de Brito Ferber
EnzoFerber

(usa FreeBSD)

Enviado em 02/09/2016 - 10:15h

Bom dia, SamL.

Sei que marcou como resolvido e que queria algo usando fopen() e as funções bufferizadas e padronizadas de manipulação de arquivos, mas vou postar um pequeno código usando open e mmap, inspirado no código do antigo "tailf" do util-linux. Quem sabe te dá alguma idéia nova?

https://github.com/karelzak/util-linux/blob/master/text-utils/tailf.c


/* monitor.c
* gcc -o monitor monitor.c -Wall
*
* (C) 2016 - Enzo Ferber, <enzoferber@gmail.com>
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

void print_buffer(char *buffer, size_t size)
{
system("clear");
printf("Buffer:\n\n");
write(1, buffer, size);
putchar('\n');
}

void reload_file(int fd, struct stat *info)
{
char *buffer;

if(!(buffer = mmap(NULL, info->st_size, PROT_READ, MAP_PRIVATE,
fd, 0))) {
perror("mmap");
exit(-127);
}

print_buffer(buffer, info->st_size);
munmap(buffer, info->st_size);
}

void check_file(char *filename, struct stat *last)
{
int fd;
struct stat current;

if((fd = open(filename, O_RDONLY)) < 0) {
perror("open");
exit(-127);
}

fstat(fd, & current);

if(current.st_mtime != last->st_mtime) {
reload_file(fd, & current);
memcpy(last, & current, sizeof *last);
}

close(fd);
}


int main(int argc, char *argv[])
{
struct stat info;

if(argc < 2) {
printf("%s [file_name]\n", argv[0]);
return -127;
}

for(;;) {
check_file(argv[1], & info);
sleep(1);
}

return 0;
}
/* EoF */


Você pode dar uma olhada no sistema inotify do linux (man inotify).
Pode também tentar com mremap().

O atual "tail" do linux usa a estratégia do fstat(). Aí é uma escolha sua como fazer.

Espero ajudar.
Enzo Ferber
[]'s



$ indent -kr -i8 src.c

"(...)all right-thinking people know that (a) K&R are _right_ and (b) K&R are right."
- linux/Documentation/CodingStyle - TORVALDS, Linus.







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts