Arquivos - Tratamento Baixo Nível - Programação 1 - Engenharia

De MediaWiki do Campus São José
Revisão de 13h17min de 7 de novembro de 2018 por 127.0.0.1 (discussão) (Criou página com '==Objetivos== *Funções de acesso a arquivos em baixo nível *Acesso a múltiplos arquivos usando ''select()'' ==Funções de acesso a arquivos em baixo nível.== ===Alguns c...')
(dif) ← Edição anterior | Revisão atual (dif) | Versão posterior → (dif)
Ir para navegação Ir para pesquisar

Objetivos

  • Funções de acesso a arquivos em baixo nível
  • Acesso a múltiplos arquivos usando select()

Funções de acesso a arquivos em baixo nível.

Alguns conceitos iniciais

No acesso a baixo nível, as funções da biblioteca basicamente chamam o sistema operacional (via SYSTEM CALLs) sem qualquer intervenção. Qualquer arquivo será referenciado por um número inteiro que reflete o índice em uma TABELA DE ARQUIVOS abertos, mantida pelo sistema operacional para cada processo execução.

Tipicamente, quando um processo é iniciado ele já possui a tabela de arquivos abertos populada nas três primeiros posições. A posição 0 corresponde a entrada padrão (normalmente será o terminal associado), a posição 1 (saída padrão, também o terminal) e a saída de erro padrão (também o terminal).

Exemplo: O comando ls é implementado na forma de um programa que quando se executa se torna um processo no sistema com os arquivos de entrada e saída padrão abertos. Faça:

ls -l

A listagem sai no arquivo de saída padrão. Você poderia redirecionar esta saída:

 ls -l > lista.txt

Se você tentar listar algo que não existe é um erro. A mensagem de erro é remetida para saída de erro padrão.

 ls -l /gdhfgjdkhsjdf

Não adianta redirecionar a saída padrão que a mensagem continua a aparecer.

 ls -l /fsadasdfsadf 1> lista.txt

Note que o 1 no redirecionador 1> pode ser omitido.

Mas é possível redirecionar a saída de erros também:

 ls -l /fdsdfgsdfgfsdg 2> erro.txt

Abra um outro terminal e verifique com o comando ps, o terminal virtual associado. Suponha que seja /dev/pts/1

Redirecione esta saída para p terminal virtual do outro terminal:

 ls -l > /dev/pts/1

Funções de acesso

O acesso é basicamente realizado por funções open(), close(), read() e write() (Ver aqui o manual da glib).


Escrevendo na saída padrão e de erro usando write()

#include <unistd.h>
#include <stdlib.h>
int main()
{
  write(1, "Escrevendo dados na saída padrão\n", 35);
  write(2, "Agora escrevendo na saída de erros\n",37);
  exit(0);
}

Agora execute testando as redireções:

./esc
./esc 1> lixo.dat
./esc 1> lixo.dat 2> erro.dat

Vamos fazer um exemplo de cópia de arquivos passados como parâmetro:

#include  <unistd.h>
#include  <sys/stat.h>
#include  <fcntl.h>
#include  <stdlib.h>
int main()
{
    char c;
    int in, out;
    in = open("file.in", O_RDONLY);
    out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
    while(read(in,&c,1) == 1)
         write(out,&c,1);
    exit(0);
}

Exercício 1: Implementar um programa de cópia similar ao que existe no linux. Capture o nome dos arquivos da linha de comando e inclua testes de erro na abertura de arquivo.

Exercício 2: Modificar o exercício anterior para realizar múltiplas cópias (um arquivo para vários).

Acesso a múltiplos arquivos usando select()

Por vezes é necessário esperar dados de várias fontes diferentes. A função select permite testar a existência de dados em uma dada fonte de dados (arquivo). A deia geral é abrir os arquivos de interesse e colocar o descritor em conjunto do tipo fd_set. O programa ao executar o select sobre este conjunto é BLOQUEADO até que exista dados disponíveis em um dos arquivos. Uma macro FD_ISSET executada sobre o conjunto de descritores de arquivo permite detectar em qual arquivo foi alterado o status.

No exemplo a seguir, você deve fornecer para o programa quais terminais virtuais deseja escutar. O programa pergunta por um usuário em cada terminal. O usuário fornece seu nome e recebe em troca uma mensagem de boas vindas.

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

char *MsgLogin="Entre com USERID\n";

int main(int argc, char *argv[])
{
    int fd;
    int fdmax;
    int i,j;
    size_t bytes_read;
    fd_set master_set, myset;
    char buffer[256]; 

    /* Limpa o conjunto de descritores */
    FD_ZERO(&master_set);

    /* abre cada arquivo passado na linha de comando e insere no conjunto master_set */
    for (i=1;i<argc;i++) {
            if ((fd=open(argv[i],O_RDWR))==-1) {
                perror("open");
                exit(1);
            }
            FD_SET(fd,&master_set);
            write (fd,MsgLogin,strlen(MsgLogin));
    }

    /* registra o maior descritor */ 
    fdmax=fd;

    for (;;) {
        myset=master_set; /* deve ser copiado sempre pois o conjunto é alterado */
        if (select(fdmax+1,&myset,NULL,NULL,NULL)==-1) {
            perror("select");
            exit(1);    
        }

        /* rastreia o descritor onde houve entrada de dados */
        for (i=0;i<=fdmax;i++) {
            if (FD_ISSET(i,&myset)) {
                if ((bytes_read=read(i,buffer,sizeof(buffer)))==-1) {
                    perror("read");
                    exit(1);
                }
                  
                if (bytes_read>0) {
                    write (i, "Bom Dia ", 8);
                    for (j=0;j<bytes_read;j++) { 
                        write(i, &buffer[j], 1);
                    }
                    buffer[j-1]=0; /* eliminar o Line feed e colocar NULL no final da string */
                    if (!strcmp("f", buffer))
                        goto final; /* ok um goto para polemizar... */
                    write (i,"\n",1);
                } 
            }
        }
     
    }
final:
    for (i=0;i<=fdmax;i++) {
       close(i);
    }    
    return 0;
}

Para executar o programa faã o seguinte:

  1. abra um terminal, e execute o comando ps no terminal. Verifique qual TTY está associado ao seu terminal, por exemplo pts/0
  2. abra outro terminal e também verifique o terminal com ps. Por exemplo, pts/1
  3. neste último terminal bloqueio o shell fazendo: while true; do sleep 3600; done </dev/null
  4. volte ao terminal original e execute o programa da forma:
 readmult /dev/pts/0 /dev/pts/1
  1. entre com um userid em cada terminal e verifique o resultado
  2. para finalizar tecle f como userid