EI_SO_D+PL_2023-24_chap_file_IO_v02---PT.pdf
Document Details
Uploaded by AdventuresomeClover
2023
Tags
Full Transcript
File I/O API de baixo- nível Patrício R. Domingues ESTG/Politécnico de Leiria Introdução A linguagem C disponibiliza duas APIs Aplicação buffers...
File I/O API de baixo- nível Patrício R. Domingues ESTG/Politécnico de Leiria Introdução A linguagem C disponibiliza duas APIs Aplicação buffers da para interação com ficheiros aplicação – API de baixo nível [Low-level API (file IO)] API alto nível fprintf,… open, close, read, write,… buffers API Muitas dessas funções mapeiam diretamente alto nível user space para chamadas ao sistema kernel space Assente em descritores de ficheiro do tipo int Modo privilegiado Bufferização no kernel API baixo nível read,write,… – API de alto nível [High-level API (buffered IO)] buffers API fopen, fclose, fprintf,… baixo nível Orientado a canais/streams Assente em ponteiros do tipo FILE* memória buffers discos Bufferização no user space buffer: zona de memória para amortizar/suavizar escritas e leituras API: Application Programming Interface (c) Patricio Domingues 2 Chamadas ao sistema O que são chamadas ao sistema? – Forma com o SO disponibiliza serviços às aplicações Aplicação Serviços: Funções do C (libc) – Abertura e manipulação de ficheiros user space – Estabelecimento de ligações de rede kernel space –… Modo privilegiado Atividade – uso direto de chamada ao sistema 0 sys_read Programa exemplo_syscall.c // Compilar: gcc --Wall -Wextra exemplo_syscall.c -o syscall.exe 1 sys_write #include 2 sys_open #include int main(void) { 3 sys_close syscall(SYS_write, 1, "Hello, world\n", 13); (…) return 0; Chamadas ao sistema } (c) Patricio Domingues 3 What’s a system call? (c) Patricio Domingues 4 O que é um ficheiro? (#1) Abstração para organizar e manter dados em memória persistente (discos, pens,…) – Armazenamento é persistente Dados mantém-se (quase sempre!) mesmo com o reiniciar de sistemas e cortes de energia elétrica – Um ficheiro é um conjunto de bits - 0 e 1 Conteúdo binário de ficheiro PNG (c) Patricio Domingues 5 O que é um ficheiro? (#2) Exemplo - Ficheiro de texto no SO Windows – Ficheiro com 0/1, octetos,… Carriage Return Line Feed (CRLF) f(0x66) i (0x69) c (0x63) h (0x68) e (0x65) 0110.0110 0110.0011 (c) Patricio Domingues 6 Unix e ficheiros O SO Unix usa o conceito de ficheiro (“file”) para dar suporte a outros elementos do sistema Exemplos: – Pipes » Comunicação entre dois processos (e.g.: ps aux | wc -l) – Sockets » comunicação em rede acessível através de descritores – /proc » pseudo-sistema de ficheiros – Dispositivos especiais » Diretório /dev (e.g., /dev/null, /dev/random, …) – Utilitário lsof (list of open files) (c) Patricio Domingues 7 E/S baixo nível (#1) No E/S baixo nível, o descritor de ficheiro é um inteiro – Chamada ao sistema open Mapeia o ficheiro indicado pelo nome para um descritor de ficheiro Em caso de sucesso, devolve um descritor para o ficheiro aberto – A função open tem duas versões: int open(const char *name,int flags); int open(const char *name,int flags, mode_t mode); – mode » Apenas empregue com a flag O_CREAT (criação de ficheiro) » Serve para indicar as permissões a serem atribuídas ao ficheiro » Exemplo: mode (expresso em octal) ➔ 0644 (rw-,r--,r--) – Documentação: man 2 open NOTA: A secção 2 do manual (man) corresponde a chamadas ao sistema (c) Patricio Domingues 9 E/S baixo nível (#2) Existe uma função para a criação de ficheiros – int creat(const char *name, mode_t mode); Nota: creat(filename,0644); corresponde a... open(filename,O_WRONLY|O_CREAT|O_TRUNC,0644); open/creat devolve um valor inteiro – -1 se ocorreu erro, sendo atribuída à variável global do processo errno um código numérico associado ao erro Exemplo: errno a 2 associado ao erro “No such file or directory” – Descritor se a operação open/creat correu bem Descritor nas E/S baixo nível é um número inteiro positivo (c) Patricio Domingues 10 Descritores do tipo “int” A API de baixo nível assenta em descritores do tipo int O descritor é devolvido pelas funções open/creat: int open(…); stdin O descritor é o índice na tabela de descritores abertos do ficheiro agora aberto/criado stdout – Uma tabela de descritores de ficheiros abertos por stderr processo ficheiro1 – As três primeiras entradas são: 0: stdin; 1:stdout; 2:stderr ficheiro2 Atividade int main(void){ char *fname = "a.txt"; (...) https://pastebin.com/s4DwWQV7 int f = creat(fname,0755); if( f == -1 ){ fprintf(stderr,"Can't creat '%s'",fname); Tabela de exit(1); descritores } printf("'%s' created: %d\n", fname, f); close(f); (uma tabela return 0; por processo) } a) Compile e execute o código C acima mostrado. b) O que faz o Código? Quais são as permissões do ficheiro ‘a.txt’? (c) Patricio Domingues 11 (c) Patricio Domingues 12 Função read Leitura – read (#1) Função read read: (tentativa de) leitura de um máximo de len octetos do ficheiro para a zona de memória apontada por buf ssize_t read (int fd, void *buf, size_t len); – fd: descritor do ficheiro (obtido via open) – buf: aponta para uma zona de memória que irá receber os octetos lidos do ficheiro – len: número máximo de octetos que se pretende ler do ficheiro buf len (octetos) Disco,… (c) Patricio Domingues 16 Leitura – read (#2) função read devolve… ponteiro de ficheiro 1 – Se erro: -1 2 É atribuída à variável errno, um código de erro apropriado à situação read(…) – Se no fim do ficheiro: 0 EOF (end-of-file) – Se sucesso: número de octetos 3 lidos/copiados para buf O número de octetos lidos pode ser EOF ficheiro inferior ao parâmetro len. – Exemplo: o ponteiro de ficheiro pode estar próximo do término do ficheiro (c) Patricio Domingues 17 Ponteiro de ficheiro Ponteiro de ficheiro ponteiro de ficheiro 1 – Posição corrente associada ao descritor do ficheiro 20 octetos 2 – Uma operação leitura/escrita é feita em relação ao ponteiro de ficheiro A operação leva ao deslocar do ponteiro de ficheiro read(…) – Exemplo 100 octetos (1) Abertura: ponteiro no início (2) Após read de 20 octetos 3 – Ponteiro posiciona-se inicio + 20 10 octetos 4 (3) Após read de 100 octetos EOF – Ponteiro posiciona-se inicio + 20 + 100 ficheiro (4) Após (tentativa) read de 50 octetos, quando só faltam 10 octetos – Ponteiro posiciona-se no EOF (c) Patricio Domingues 18 Exemplo: read (#1) #include https://pastebin.com/zesSMF83 #include #include #include #include #include #include #include int main(int argc, char *argv[]){ if( argc != 2 ){ fprintf(stderr,"%s \n", argv); exit(1); } argv: nome do ficheiro a ser lido char *fname = argv; printf("Processing '%s'\n", fname); int f = open(fname,O_RDONLY); if( f == -1 ){ fprintf(stderr,"Can't open '%s'",fname); exit(2); } Buff: zona de memória empregue para leitura do char Buff; ficheiro (16 octetos de cada vez) long int total = 0; (c) Patricio Domingues 19 Exemplo: read (#2) while (1){ // Loop is terminated via break and exit size_t count = sizeof(Buff); read(…): leitura de um máximo ssize_t read_ret = read(f,Buff,count); de count octetos if( read_ret==-1 ){ fprintf(stderr,"ERROR: can't read '%s':%s\n", fname, strerror(errno)); exit(3); } read(…) devolveu zero: EOF if( read_ret == 0 ){ printf("At EOF of '%s' (%ld bytes)\n",fname,total); break; } if ( read_ret > 0 ){ Mostrar em hexadecimal os total += read_ret; octetos lidos for(int i=0; i /dev/full ls: write error: No space left on device (c) Patricio Domingues 27 Escrita – write (#4): append Um ficheiro pode ser aberto em modo ponteiro de ficheiro APPEND (acréscimo) – Especificar O_APPEND no parâmetro flags ficheiro do open int F = open(nome_ficheiro,O_RDWR|O_APPEND); Proc1 Modo O_APPEND – A escrita ocorrerá sempre no final do Proc2 ficheiro – É garantido o sincronismo da escrita nas Proc3 situações em que o ficheiro se encontre aberto por vários processos Apropriado para operações de escrita em logs (c) Patricio Domingues 28 E/S sincronizado O SO não efetua imediatamente a escrita para o dispositivo físico (e.g., disco, pen) – Apenas quando tem o buffer associado ao ficheiro é que efetiva a escrita para o dispositivo – Buffer atua como memória tampão/amortecimento Como pode o programador forçar uma escrita imediata? – Função fsync int fsync (int fd); – As escritas pendentes são efetivadas (“committed”) para o respetivo dispositivo Nota: também existe o utilitário sync que efetua o mesmo em todo o SO (c) Patricio Domingues 29 errno e seek A variável errno (#1) Variável inteira, global ao processo – #include – int errno; Certas funções ativam o errno quando ocorre um erro – open, close, read, write, etc. – Algumas funções da biblioteca do C (ver man) O valor de errno indica o que correu mal – O valor só deve ser considerado quando a função retorna uma indicação de erro -1 em muitas funções (open, close, read, write, etc.) -1 ou NULL para a maior parte da funções da libc (c) Patricio Domingues 33 A variável errno (#2) Alguns dos possíveis valores da variável errno – EACCES ➔ Permission denied – ENFILE ➔ Too many open files in system man errno (…) ENFILE – Constante pre-processador Como obter o valor numérico? #include printf("ENFILE=%d\n", ENFILE); Result: ENFILE=23 (c) Patricio Domingues 34 A variável errno (#3) A constante simbólica representa um valor numérico – Exemplo: ENFILE ➔ 23 Como obter uma mensagem? – char *strerror(int errnum); #include #include printf("ENFILE=%d\n", ENFILE); printf(“Error string for ENFILE:'%s'\n",strerror(ENFILE)); Resultado: ENFILE=23 Error string for ENFILE:'Too many open files in system' (c) Patricio Domingues 35 lseek (#1) Leitura/escrita ocorre usualmente de forma sequencial – Exemplo ponteiro de ficheiro read do byte 0 ao 255. Depois, leitura de 256 a 511, etc., O mesmo sucede com o write read(…) Como posicionar o ponteiro de ficheiro numa determinada posição? função lseek – off_t lseek (int fd, off_t pos, int origin); EOF ficheiro (c) Patricio Domingues 36 lseek (#2) ponteiro de ficheiro protótipo (man 2 lseek) – off_t lseek (int fd, off_t pos, int origin); lseek(f,100,SEEK_SET) pos: deslocamento em octetos a ser aplicado Origin: corresponde ao “referencial”. Pode ser: – SEEK_CUR: from current file position + pos Pos can be 0, >0 or 0 or