Em todas as linguagens de programação (que eu uso pelo menos), você deve abrir um arquivo antes de poder ler ou gravar nele.
Mas o que essa operação aberta realmente faz?
As páginas de manual para funções típicas não dizem nada além de 'abrir um arquivo para leitura / gravação':
http://www.cplusplus.com/reference/cstdio/fopen/
https://docs.python.org/3/library/functions.html#open
Obviamente, através do uso da função, você pode dizer que ela envolve a criação de algum tipo de objeto que facilita o acesso a um arquivo.
Outra maneira de colocar isso seria, se eu implementasse uma open
função, o que seria necessário fazer no Linux?
C
Linux; já que o que Linux e Windows fazem é diferente. Caso contrário, é um pouco amplo demais. Além disso, qualquer linguagem de nível superior acabará chamando uma API C para o sistema ou compilando até C para executar, portanto, sair no nível de "C" é colocá-lo no mínimo denominador comum.Respostas:
Em quase todos os idiomas de alto nível, a função que abre um arquivo é um invólucro em torno da chamada de sistema do kernel correspondente. Também pode fazer outras coisas sofisticadas, mas nos sistemas operacionais contemporâneos, a abertura de um arquivo sempre deve passar pelo kernel.
É por isso que os argumentos da
fopen
biblioteca funcionam, ou os do Pythonopen
se parecem muito com os argumentos daopen(2)
chamada do sistema.Além de abrir o arquivo, essas funções geralmente configuram um buffer que será conseqüentemente usado nas operações de leitura / gravação. O objetivo desse buffer é garantir que sempre que você desejar ler N bytes, a chamada de biblioteca correspondente retorne N bytes, independentemente de as chamadas para as chamadas subjacentes do sistema retornarem menos.
Nos sistemas operacionais do tipo Unix, uma chamada bem-sucedida
open
retorna um "descritor de arquivo" que é meramente um número inteiro no contexto do processo do usuário. Consequentemente, esse descritor é passado para qualquer chamada que interaja com o arquivo aberto e, após a chamadaclose
, o descritor se torna inválido.É importante observar que a chamada
open
atua como um ponto de validação no qual várias verificações são feitas. Se não todas as condições forem atendidas, a chamada falhar, retornando-1
ao invés do descritor, e do tipo de erro é indicado noerrno
. As verificações essenciais são:No contexto do kernel, deve haver algum tipo de mapeamento entre os descritores de arquivos do processo e os arquivos abertos fisicamente. A estrutura de dados interna que é mapeada para o descritor pode conter ainda outro buffer que lida com dispositivos baseados em bloco ou um ponteiro interno que aponta para a posição atual de leitura / gravação.
fonte
man dup2
e verifique a sutileza entre um descritor de arquivo aberto (que é um FD que está aberto) e uma descrição de arquivo aberto (um OFD).Eu sugiro que você dê uma olhada neste guia através de uma versão simplificada da
open()
chamada do sistema . Ele usa o seguinte trecho de código, que é representativo do que acontece nos bastidores quando você abre um arquivo.Resumidamente, eis o que esse código faz, linha por linha:
A
filp_open
função tem a implementaçãoque faz duas coisas:
struct file
com as informações essenciais sobre o inode e retorne-o. Essa estrutura se torna a entrada nessa lista de arquivos abertos que mencionei anteriormente.Armazene ("instale") a estrutura retornada na lista de arquivos abertos do processo.
read()
,write()
, eclose()
. Cada um deles entregará o controle ao kernel, que pode usar o descritor de arquivo para procurar o ponteiro de arquivo correspondente na lista do processo e usar as informações nesse ponteiro de arquivo para realmente executar a leitura, gravação ou fechamento.Se você estiver se sentindo ambicioso, poderá comparar este exemplo simplificado com a implementação da
open()
chamada do sistema no kernel do Linux, uma função chamadado_sys_open()
. Você não deve ter problemas para encontrar as semelhanças.Obviamente, essa é apenas a "camada superior" do que acontece quando você chama
open()
- ou, mais precisamente, é a parte do código do kernel de mais alto nível que é invocada no processo de abertura de um arquivo. Uma linguagem de programação de alto nível pode adicionar camadas adicionais. Há muita coisa acontecendo em níveis mais baixos. (Agradecemos a Ruslan e pjc50 pela explicação.) Aproximadamente, de cima para baixo:open_namei()
edentry_open()
invoque o código do sistema de arquivos, que também faz parte do kernel, para acessar os metadados e o conteúdo dos arquivos e diretórios. O sistema de arquivos lê bytes brutos do disco e interpreta esses padrões de bytes como uma árvore de arquivos e diretórios./dev/sda
e similares.)Isso também pode estar um pouco incorreto devido ao armazenamento em cache . :-P Mas, falando sério, há muitos detalhes que deixei de fora - uma pessoa (não eu) poderia escrever vários livros descrevendo como todo esse processo funciona. Mas isso deve lhe dar uma idéia.
fonte
Qualquer sistema de arquivos ou sistema operacional sobre o qual você queira falar está bem para mim. Agradável!
Em um ZX Spectrum, a inicialização de um
LOAD
comando colocará o sistema em um loop restrito, lendo a linha Audio In.O início dos dados é indicado por um tom constante e, em seguida, segue uma sequência de pulsos longos / curtos, onde um pulso curto é para um binário
0
e um pulso mais longo para um binário1
( https://en.wikipedia.org/ wiki / ZX_Spectrum_software ). O loop de carga apertada reúne bits até preencher um byte (8 bits), armazena isso na memória, aumenta o ponteiro da memória e volta para procurar mais bits.Normalmente, a primeira coisa que um carregador lê é um cabeçalho de formato curto e fixo , indicando pelo menos o número de bytes a serem esperados e possivelmente informações adicionais, como nome do arquivo, tipo de arquivo e endereço de carregamento. Depois de ler esse cabeçalho curto, o programa pode decidir se continua carregando a maior parte dos dados ou sai da rotina de carregamento e exibe uma mensagem apropriada para o usuário.
Um estado de fim de arquivo pode ser reconhecido ao receber o número de bytes esperado (um número fixo de bytes, conectado no software ou um número variável, como indicado em um cabeçalho). Foi gerado um erro se o loop de carregamento não receber um pulso na faixa de frequência esperada por um determinado período de tempo.
Um pouco de fundo sobre esta resposta
O procedimento descrito carrega dados de uma fita de áudio comum - daí a necessidade de digitalizar a entrada de áudio (conectada com um plugue padrão para gravadores). Um
LOAD
comando é tecnicamente o mesmo queopen
um arquivo - mas está fisicamente ligada a realmente carregar o arquivo. Isso ocorre porque o gravador não é controlado pelo computador e você não pode (com êxito) abrir um arquivo, mas não carregá-lo.O "loop apertado" é mencionado porque (1) a CPU, uma Z80-A (se a memória servir), era realmente lenta: 3,5 MHz e (2) o Spectrum não tinha relógio interno! Isso significa que ele precisou manter a contagem precisa dos estados T (tempos de instrução) para todos. solteiro. instrução. dentro desse loop, apenas para manter o tempo exato do sinal sonoro.
Felizmente, essa baixa velocidade da CPU tinha a vantagem distinta de poder calcular o número de ciclos em um pedaço de papel e, portanto, o tempo real que eles levariam.
fonte
Depende do sistema operacional o que exatamente acontece quando você abre um arquivo. Abaixo, descrevo o que acontece no Linux, pois dá uma idéia do que acontece quando você abre um arquivo e pode verificar o código-fonte se estiver interessado em mais detalhes. Não estou cobrindo permissões, pois isso tornaria a resposta muito longa.
No Linux, todo arquivo é reconhecido por uma estrutura chamada inode. Cada estrutura possui um número único e cada arquivo obtém apenas um número de inode. Essa estrutura armazena metadados para um arquivo, por exemplo, tamanho do arquivo, permissões de arquivo, carimbos de data e hora e blocos de ponteiro para disco, no entanto, não o nome do arquivo real. Cada arquivo (e diretório) contém uma entrada de nome de arquivo e o número do inode para pesquisa. Quando você abre um arquivo, assumindo que você possui as permissões relevantes, um descritor de arquivo é criado usando o número de inode exclusivo associado ao nome do arquivo. Como muitos processos / aplicativos podem apontar para o mesmo arquivo, o inode possui um campo de link que mantém a contagem total de links para o arquivo. Se um arquivo estiver presente em um diretório, sua contagem de links será uma, se houver um link físico, sua contagem será dois e se um arquivo for aberto por um processo, a contagem de links será incrementada em 1.
fonte
Escrituração, principalmente. Isso inclui várias verificações como "O arquivo existe?" e "Tenho permissões para abrir este arquivo para gravação?".
Mas isso é tudo sobre o kernel - a menos que você esteja implementando seu próprio sistema operacional de brinquedos, não há muito o que se aprofundar (se você se divertir, é uma ótima experiência de aprendizado). Obviamente, você ainda deve aprender todos os códigos de erro possíveis que pode receber ao abrir um arquivo, para poder manipulá-los adequadamente - mas essas geralmente são pequenas abstrações agradáveis.
A parte mais importante no nível do código é que ele fornece um identificador para o arquivo aberto, usado para todas as outras operações que você faz com um arquivo. Você não poderia usar o nome do arquivo em vez deste identificador arbitrário? Bem, com certeza - mas usar uma alça oferece algumas vantagens:
read
da última posição no seu arquivo. Usando um identificador para identificar uma "abertura" específica de um arquivo, você pode ter vários identificadores simultâneos no mesmo arquivo, cada um lendo seus próprios locais. De certa forma, o identificador atua como uma janela móvel para o arquivo (e uma maneira de emitir solicitações de E / S assíncronas, que são muito úteis).Também há outros truques que você pode fazer (por exemplo, compartilhar identificadores entre processos para ter um canal de comunicação sem usar um arquivo físico; em sistemas unix, os arquivos também são usados para dispositivos e vários outros canais virtuais, portanto, isso não é estritamente necessário ), mas eles não estão realmente ligados à
open
operação em si, então não vou me aprofundar nisso.fonte
No cerne da questão, ao abrir para leitura, nada sofisticado realmente precisa acontecer. Tudo o que precisa fazer é verificar se o arquivo existe e se o aplicativo possui privilégios suficientes para lê-lo e criar um identificador no qual você pode emitir comandos de leitura para o arquivo.
É nesses comandos que a leitura real será despachada.
O sistema operacional geralmente obtém vantagem na leitura iniciando uma operação de leitura para preencher o buffer associado ao identificador. Então, quando você realmente faz a leitura, pode retornar o conteúdo do buffer imediatamente, em vez de precisar aguardar na E / S do disco.
Para abrir um novo arquivo para gravação, o sistema operacional precisará adicionar uma entrada no diretório para o novo arquivo (atualmente vazio). E, novamente, é criada uma alça na qual você pode emitir os comandos de gravação.
fonte
Basicamente, uma chamada para abrir precisa localizar o arquivo e, em seguida, registrar o que for necessário para que operações posteriores de E / S possam encontrá-lo novamente. Isso é bastante vago, mas será verdade em todos os sistemas operacionais em que consigo pensar imediatamente. As especificidades variam de plataforma para plataforma. Muitas respostas já aqui falam sobre os sistemas operacionais de desktop modernos. Eu fiz uma pequena programação no CP / M, por isso vou oferecer meu conhecimento sobre como ele funciona no CP / M (o MS-DOS provavelmente funciona da mesma maneira, mas por razões de segurança, normalmente não é feito hoje em dia. )
No CP / M, você tem uma coisa chamada FCB (como você mencionou C, você pode chamá-la de struct; é realmente uma área contígua de 35 bytes na RAM contendo vários campos). O FCB possui campos para gravar o nome do arquivo e um número inteiro (4 bits) identificando a unidade de disco. Então, quando você chama o Open File do kernel, passa um ponteiro para essa estrutura colocando-o em um dos registros da CPU. Algum tempo depois, o sistema operacional retorna com a estrutura ligeiramente alterada. Qualquer que seja a E / S que você faça nesse arquivo, você passa um ponteiro para essa estrutura para a chamada do sistema.
O que o CP / M faz com este FCB? Ele reserva determinados campos para seu próprio uso e os utiliza para acompanhar o arquivo, portanto é melhor nunca tocá-los de dentro do seu programa. A operação Abrir arquivo procura na tabela no início do disco por um arquivo com o mesmo nome do conteúdo do FCB (o caractere curinga '?' Corresponde a qualquer caractere). Se ele encontrar um arquivo, ele copia algumas informações no FCB, incluindo o (s) local (is) físico (s) do arquivo no disco, para que as chamadas de E / S subsequentes chamem o BIOS, que pode passar esses locais para o driver de disco. Nesse nível, as especificidades variam.
fonte
Em termos simples, quando você abre um arquivo, na verdade está solicitando ao sistema operacional que carregue o arquivo desejado (copie o conteúdo do arquivo) do armazenamento secundário para o ram para processamento. E a razão por trás disso (carregar um arquivo) é porque você não pode processar o arquivo diretamente do disco rígido, devido à sua velocidade extremamente lenta em comparação com o Ram.
O comando open irá gerar uma chamada do sistema que, por sua vez, copia o conteúdo do arquivo do armazenamento secundário (disco rígido) para o armazenamento primário (RAM).
E fechamos um arquivo porque o conteúdo modificado deve ser refletido no arquivo original que está no disco rígido. :)
Espero que ajude.
fonte