====== Operações usando descritores de arquivos ======
As principais funções operando sobre descritores de arquivos estão declaradas nos arquivos de cabeçalho ''fcntl.h'' e ''unistd.h''.
===== Abrindo e fechando descritores =====
#include
#include
#include
int open (const char *filename, int flags [, mode_t mode])
Cria e retorna um descritor para o arquivo indicado, ou -1 em caso de erro.
O argumento mode é usado para definir as permissões de acesso ao arquivo quando ele é criado. Os flags permitem controlar a forma de abertura do arquivo. Deve-se selecionar e combinar os flags desejados usando o operador "|" (OR binário). Os principais flags são:
* ''O_RDONLY'' : o arquivo é aberto para leitura.
* ''O_WRONLY'' : o arquivo é aberto para escrita.
* ''O_RDWR'' : o arquivo é aberto para leitura e escrita.
* ''O_CREAT'' : se o arquivo ainda não existe, deve ser criado usando as permissões definidas no parâmetro mode.
* ''O_EXCL'' : se ''O_CREAT'' e ''O_EXCL'' forem selecionados, a operação falha se o arquivo especificado já existe.
* ''O_NOCTTY'' : se o nome do arquivo corresponde a um terminal, o processo não deve ser associado ao terminal.
* ''O_TRUNC'' : ao abrir o arquivo em escrita, trunca seu tamanho para zero.
* ''O_SHLOCK'' : obtém uma trava compartilhada (//shared lock//) sobre o arquivo, de forma atômica.
* ''O_EXLOCK'' : obtém uma trava exclusiva sobre o arquivo, de forma atômica.
* ''O_APPEND'' : abre o arquivo em modo concatenação (//append//). É a única forma segura de fazer concatenações em arquivos compartilhados.
* ''O_NONBLOCK'' : habilita o modo não-bloqueante de leitura e escrita: a função ''read'' retorna imediatamente com erro se não houver entrada disponível; a função ''write'' retorna imediatamente com erro se não puder escrever imediatamente; a função ''open'' retorna imediatamente se não puder abrir ou criar o arquivo de imediato.
* ''O_ASYNC'' : habilita o modo de leitura assíncrono: serão gerados sinais ''SIGIO'' ao processo quando entradas estiverem disponíveis para leitura no arquivo.
* ''O_FSYNC'' ou ''O_SYNC'' : habilita o modo de escrita síncrono: cada chamada à função ''write'' só retorna quando os dados estiverem efetivamente no disco. Acrescenta segurança às operações de escrita, mas implica em queda de desempenho.
Ver também as funções ''open64'', ''creat'' e ''creat64''.
#include
int close (int filedes)
Fecha o arquivo correspondente ao descritor. Implica em descartar entradas não lidas, liberar travas e liberar o descritor para outros usos.
Exemplo: abertura de arquivo para leitura:
#include
#include
int main (int argc, char *argv[])
{
int fd ; /* file descriptor */
printf ("Abrindo o arquivo x para leitura...\n") ;
fd = open ("x", O_RDONLY) ;
if ( fd < 0 )
{
perror ("Erro ao abrir arquivo x") ;
exit (1) ;
}
printf ("Abri o arquivo x !\n") ;
close (fd) ;
exit (0) ;
}
Exemplo: abertura de arquivo para leitura e escrita; caso o arquivo não exista ele é criado, com permissões ''0644'':
#include
#include
int main (int argc, char *argv[])
{
int fd ; /* file descriptor */
printf ("Abrindo o arquivo y para leitura...\n") ;
fd = open ("y", O_RDWR | O_CREAT, 0640) ;
if ( fd < 0 )
{
perror ("Erro ao abrir/criar arquivo y") ;
exit (1) ;
}
printf ("Abri o arquivo y !\n") ;
close (fd) ;
exit (0) ;
}
===== Lendo e escrevendo dados =====
ssize_t
Tipo de dado usado para representar o número de blocos a ler ou escrever em uma operação ''read'' ou ''write''.
#include
ssize_t read (int filedes, void *buffer, size_t size)
Lê até ''size'' bytes do arquivo indicado pelo descritor ''filedes'', armazenando o resultado em buffer. Retorna o número de bytes lidos, zero (EOF) ou -1 (erro). Ver também ''pread'' e ''pread64''.
#include
ssize_t write (int filedes, const void *buffer, size_t size)
Escreve até ''size'' bytes de dados contidos em buffer no arquivo indicado por ''filedes''. Retorna o número de bytes escritos ou -1 (erro). Assim que a operação retorna, os dados estão disponíveis para leitura, mas não estão necessariamente no disco. Ver também ''pwrite'' e ''pwrite64''.
Para leituras/escritas de grandes volumes de dados usando múltiplos buffers, torna-se mais apropriado usar as funções ''readv'' e ''writev''.
#include
#include
off_t lseek (int filedes, off_t offset, int whence)
Permite mudar a posição do ponteiro do arquivo indicado por ''filedes''. O valor do deslocamento ''offset'' depende do modo de operação indicado por ''whence'', que pode ser: relativo ao início do arquivo (''SEEK_SET''), à posição corrente (''SEEK_CUR'') ou ao final do arquivo (''SEEK_END''). Ver também ''lseek64''.
Exemplo: abrir um arquivo em modo de leitura, ler seus primeiros 256 bytes e escreve-los na saída padrão:
#include
#include
#include
#define SIZE 256
int main (int argc, char *argv[])
{
int fd ; /* file descriptor */
char buffer[SIZE] ;
ssize_t bytesLidos, bytesEscritos ;
/* abrir arquivo em leitura */
fd = open ("x", O_RDONLY) ;
if ( fd < 0 )
{
perror ("Erro ao abrir o arquivo x") ;
exit (1) ;
}
/* ler SIZE bytes do arquivo */
bytesLidos = read (fd, &buffer, SIZE) ;
if ( bytesLidos < 0)
{
perror ("Erro na leitura de x") ;
exit (1) ;
}
/* escrever os bytes lidos no terminal (stdout) */
bytesEscritos = write (STDOUT_FILENO, &buffer, bytesLidos) ;
if ( bytesEscritos < 0)
{
perror ("Erro na escrita em stdout") ;
exit (1) ;
}
close (fd) ;
exit (0) ;
}
Exemplo: abrir um arquivo em modo de leitura, ler seus últimos 256 bytes e escreve-los na saída padrão:
#include
#include
#include
#define SIZE 256
int main (int argc, char *argv[])
{
int fd ; /* file descriptor */
char buffer[SIZE] ;
ssize_t bytesLidos, bytesEscritos ;
/* abrir arquivo em leitura */
fd = open ("x", O_RDONLY) ;
if ( fd < 0 )
{
perror ("Erro ao abrir o arquivo x") ;
exit (1) ;
}
/* posicionar a SIZE bytes do final do arquivo */
lseek (fd, -SIZE, SEEK_END) ;
/* ler SIZE bytes do arquivo */
bytesLidos = read (fd, &buffer, SIZE) ;
if ( bytesLidos < 0)
{
perror ("Erro na leitura de x") ;
exit (1) ;
}
/* escrever os bytes lidos no terminal (stdout) */
bytesEscritos = write (STDOUT_FILENO, &buffer, bytesLidos) ;
if ( bytesEscritos < 0)
{
perror ("Erro na escrita em stdout") ;
exit (1) ;
}
close (fd) ;
exit (0) ;
}
===== Interagindo com vários descritores =====
Algumas vezes um processo pode ter de esperar por dados vindos de diversas fontes. Um exemplo disso seria um processo funcionando como servidor para outros processos através de //pipes// ou //sockets//. Não é possível usar a função ''read'' para essa finalidade, pois ela irá aguardar em apenas um descritor por vez, ignorando os demais. Pode-se usar o modo não-bloqueante e varrer continuamente todos os descritores de interesse, mas isso não é eficiente. A função ''select'' permite aguardar até que a entrada ou saída em qualquer descritor de um conjunto de descritores esteja pronta (disponível). Também é possível estabelecer um prazo de espera (//time-out//).
Conjuntos de descritores são definidos pelo tipo ''fd_set'' e são manipulados pelas seguintes macros:
* ''int FD_SETSIZE'': tamanho máximo do conjunto de descritores.
* ''void FD_ZERO (fd_set *set)'': inicializa um conjunto como vazio.
* ''void FD_SET (int filedes, fd_set *set)'': adiciona o descritor ''filedes'' ao conjunto ''set''.
* ''void FD_CLR (int filedes, fd_set *set)'': remove ''filedes'' do conjunto ''set''.
* ''int FD_ISSET (int filedes, const fd_set *set)'': testa se ''filedes'' está no conjunto ''set''.
#include
int select (int nfds, fd_set *read_fds, fd_set *write_fds,
fd_set *except_fds, struct timeval *timeout)
A função select bloqueia o processo até que ocorra alguma atividade nos primeiros ''nfds'' descritores indicados pelos conjuntos ''read_fds'', ''write_fds'' e ''except_fds'', ou que o prazo indicado por ''timeout'' tenha expirado.
* ''read_fds'': descritores a esperar que estejam prontos para leitura, ou //null//.
* ''write_fds'': descritore a esperar que estejam prontos para escrita, ou //null//.
* ''except_fds'': descritores a esperar por condições excepcionais (vide [[sockets]]), ou //null//.
* ''timeout'': estrutura do tipo ''struct timeval'' (vide [[timers]]) ou //null//.
Em caso de sucesso, a função retorna o número total de descritores prontos em todos os conjuntos. Cada conjunto é ajustado para conter apenas seus descritores prontos. Um retorno com 0 indica expiração de prazo e -1 indica erro. A recepção de um sinal fará com que select retorne imediatamente com um erro ''EINTR''.
Obs: A função ''poll'' também permite efetuar esperas em múltiplos descritores, mas não está na norma POSIX básica. Além disso, muitos sistemas a implementam ''poll'' usando ''select''.
===== Sincronizando buffers =====
Em certos sistemas pode ser necessários assegurar que os dados escritos por processo sejam gravados no disco assim que possível, evitando que permaneçam muito tempo em buffers de memória.
#include
int sync (void)
Força que os buffers de todos os descritores do sistema sejam escritos em disco.
#include
int fsync (int filedes)
Aguarda até que todo o buffer de escrita associado a ''filedes'' seja escrito no disco. Isso inclui os dados e meta-dados do arquivo.
#include
int fdatasync (int fildes)
Aguarda até que todos os dados do arquivo tenham sido escritos em disco. Meta-dados não são considerados, portanto esta operação é mais rápida que ''fsync''.
Obs: As operações de entrada/saída assíncronas (''aio_read'', ''aio_write'', etc) somente foram incluídas no núcleo Linux 2.6, não estando disponíveis no núcleo Linux 2.4.
===== Controlando descritores =====
#include
#include
int fcntl (int filedes, int command, ...)
Executa a operação indicada por command no descritor ''filedes''. Alguns comandos podem necessitar de parâmetros adicionais, que são detalhados em sua documentação específica. Os principais comandos são apresentados brevemente a seguir:
* ''F_DUPFD'': duplica o descritor (retorna outro descritor apontando para o mesmo arquivo aberto).
* ''F_GETFD'': obtém os flags associados ao descritor.
* ''F_SETFD'': ajusta os flags associados ao descritor.
* ''F_GETFL'': obtém os flags associados ao arquivo aberto.
* ''F_SETFL'': ajusta os flags associados ao arquivo aberto.
* ''F_GETLK'': obtém uma trava no arquivo.
* ''F_SETLK'': ajusta ou limpa uma trava.
* ''F_SETLKW'': como ''F_SETLK'', mas espera a conclusão do comando.
===== Duplicando descritores =====
Ao duplicar um descritor, cria-se um novo descritor que aponta para o mesmo arquivo aberto, embora seus modos de operação (//flags//) possam ser distintos. O uso mais freqüente da duplicação de descritores é a implementação da redireção de entrada ou saída de um processo.
#include
int dup (int old)
Copia o descritor ''old'' no primeiro descritor disponível. Equivale a ''fcntl (old, F_DUPFD, 0)''.
#include
int dup2 (int old, int new)
Copia o descritor ''old'' no descritor ''new''. Caso já esteja aberto, ''new'' é fechado antes. Equivale a ''close (new); fcntl (old, F_DUPFD, new)'', mas efetuado de forma atômica.
Eis um exemplo de como usar ''dup2'' para redirecionar a saída padrão de um processo para um arquivo, após um ''fork'':
...
pid = fork ();
if (pid == 0)
{
char *filename;
char *program;
int file;
...
file = open (filename, O_RDONLY);
dup2 (file, STDIN_FILENO);
close (file);
execv (program, NULL);
}
...
===== Travas em arquivos =====
As travas (//locks//) permitem a processos cooperantes trabalhar sobre os mesmos arquivos sem conflitos ou erros. Há basicamente dois tipos de travas:
* //Exclusive// ou// write locks// dão acesso exclusivo em escrita a uma parte do arquivo. Enquanto um //write lock// estiver ativo, nenhum outro processo pode travar aquela parte do arquivo.
* //Shared// ou //read locks// impedem outros processos de criar //write locks// sobre aquela parte do arquivo, mas permitem a existência de outros //read locks//.
As funções ''read'' e ''write'' não testam //locks//. Cabe ao programador implementar o controle de travas para impedir acessos conflitantes a partes de arquivos. Isso pode ser feito através das funções ''flock'' (para arquivos inteiros) e ''fcntl'' (para partes de arquivos).
Também é importante observar que //locks// são associados a processos, portanto um processo só pode ter um tipo de trava sobre um arquivo. Além disso, todas as suas travas são liberadas quando o processo fecha o arquivo ou encerra sua execução.
#include
int flock (int filedes, int operation)
Aplica ou remove uma trava sobre o arquivo indicado por ''filedes''. O parâmetro operation pode ser ''LOCK_SH'' (//shared lock//), ''LOCK_EX'' (//exclusive lock//) ou ''LOCK_UN'' (//remove lock//). Além disso, esse parâmetro pode ser combinado com ''LOCK_NB'' (através de um ''OR'') para obter operações não-bloqueantes. As operações sobre travas usando ''fcntl'' são mais complexas e estão detalhadas no manual da GLibC.
Exemplo: obter uma trava exclusiva sobre o arquivo cujo descritor é ''fd'':
if (flock (fd, LOCK_EX) < 0)
{
perror ("Erro ao obter trava") ;
exit (1) ;
}