【Linux 】—— Chamada do sistema operacional de arquivo

1. Função de E / S de arquivo

As funções disponíveis de E / S de arquivo que chamamos incluem abrir um arquivo, ler um arquivo e gravá-lo. A maioria das E / S de arquivos nos sistemas UNIX requer 5 funções: abrir, ler, gravar, ler e fechar. Para o kernel, todos os arquivos abertos são referenciados através de descritores de arquivos. Ao abrir um arquivo existente ou criar um novo arquivo, o kernel retorna um descritor de arquivo para o processo. Ao ler ou gravar um arquivo, use abrir ou criar para retornar o descritor de arquivo para identificar o arquivo e passá-lo como um parâmetro para leitura ou gravação. A seguir, vamos dar uma boa olhada em algumas dessas cinco funções.

1. Open
chama a função open para abrir ou criar um arquivo

#include<fcntl.h>
int open(const char* filename,int flag,.../*mode_t mode */);

(1) nome do arquivo: o nome do arquivo a ser aberto ou criado. Atenção! Se você apenas der o nome do arquivo, ele pesquisará no caminho atual; portanto, deve ser o nome e o caminho do arquivo.
(2) sinalizador: indica a maneira como o arquivo é aberto. Você pode usar uma ou mais das seguintes constantes para executar a operação OR para formar o parâmetro flag
. As três constantes na figura a seguir devem especificar uma e apenas uma
Insira a descrição da imagem aqui. A constante na figura a seguir é o
O_APPEND opcional : é anexado ao arquivo toda vez que é gravado. o fim do
O_CREAT: se este arquivo não existir, criá-la. Porém, neste momento, você precisa usar o terceiro modo de parâmetro, usado para especificar o bit de permissão de acesso
O_EXCL do arquivo: se O_CREAT também for especificado e o arquivo existir, ocorrerá um erro. Este método pode ser usado para testar se um arquivo existe.Se ele não existir, o arquivo é criado.Isso cria a criação e o teste de uma operação atômica.
O_TRUNC: Se o arquivo existir, mas também para somente gravação ou leitura e escrita aberta com êxito, em seguida, ele é truncado para um comprimento de 0
parte sobre a sincronização de entrada e opções de saída
O_DSYNC: dando a cada gravação esperando I física operação E / S está completo, mas se a operação de gravação Não afeta a leitura dos dados gravados, não espera que os atributos do arquivo sejam atualizados
O_RSYNC: faz com que cada descritor de arquivo como uma operação de leitura de parâmetro aguarde até que qualquer operação de gravação pendente na mesma parte do arquivo seja concluída.
O_SYNC: faz com que cada A segunda gravação aguarda até que a operação de E / S física seja concluída, incluindo a E / S necessária para a atualização do atributo de arquivo causada pela operação de gravação

2, close
chama a função close para fechar um arquivo aberto

#include<fcntl.h>
int close(int filename);

Fechar um arquivo também libera todos os bloqueios de registro que o processo adicionou ao arquivo. Atenção! Quando um processo termina, o kernel fecha automaticamente todos os arquivos abertos. Muitos programas aproveitam esse recurso sem fechar explicitamente o arquivo aberto com o fechar.

3. lseek
pode chamar lseek para definir explicitamente o deslocamento do arquivo aberto. Cada arquivo aberto possui um "deslocamento de arquivo atual" associado a ele. Geralmente, as operações de leitura e gravação começam no deslocamento atual do arquivo e aumentam o deslocamento pelo número de bytes lidos e gravados. De acordo com o padrão do sistema, ao abrir um arquivo, a menos que a opção O_APPEND seja especificada, o deslocamento é definido como 0.

#include<fcntl.h>
int lseek(int fd,int size,int flag);

(1) bandeira: é a marca em movimento, a posição inicial do movimento. SEEK_SET é definir o deslocamento do arquivo para bytes de tamanho desde o início do arquivo. SEEK_CUR é definir o deslocamento do arquivo para seu valor atual mais o tamanho, o tamanho pode ser positivo ou negativo. SEEK_END é definir o deslocamento do arquivo para o tamanho e o tamanho do arquivo, e o tamanho pode ser positivo ou negativo.
(2) Valor de retorno: se lseek for executado com sucesso, ele retornará o novo deslocamento de arquivo. Atenção! Para arquivos comuns, o deslocamento deve ser não negativo, mas alguns dispositivos também podem permitir compensações negativas. Portanto, ao comparar o valor de retorno de lseek, não teste se é menor que 0, mas teste se é igual a -1.

O lseek registra apenas o deslocamento do arquivo atual no kernel, não causa nenhuma operação de E / S. Esse deslocamento é usado para a próxima operação de leitura ou gravação. O deslocamento do arquivo pode ser maior que o tamanho atual do arquivo.Nesse caso, a próxima gravação no arquivo aumentará o arquivo e formará um buraco no arquivo, o que é realmente permitido. Os bytes que estão no arquivo, mas não foram gravados, são lidos como 0. Deve-se observar também que, para dados recém-gravados, os blocos de disco precisam ser alocados, mas para a área vazia mencionada anteriormente, nenhum bloco de disco precisa ser alocado.

4, leitura
chama a função de leitura para ler dados do arquivo aberto

#include<fcntl.h>
int read(int fd,void *buf,size_t size);

(1) fd: o arquivo lido, especificado pelo valor de retorno de aberto
(2) Valor de retorno: se for bem-sucedido, retorna o número de bytes lidos. Se o final do arquivo for atingido, ele retornará 0. As situações a seguir podem fazer com que o número real de bytes seja lido menos que o número de bytes necessário para ler.
a) Ao ler o arquivo, o final do arquivo foi atingido antes de ler o número necessário de bytes,
b) Ao ler a partir do dispositivo terminal, geralmente lê no máximo uma linha por vez
c) Ao ler na rede, o mecanismo de buffer na rede pode causar um valor de retorno Menor que o número de bytes necessários para a leitura
d. Ao ler de um canal ou FIFO, se o canal contiver menos do que o número necessário de bytes, a leitura retornará apenas o número real de bytes disponíveis
e. O dispositivo (como fita) lê no máximo um registro por vez
f. Quando um sinal causa uma interrupção
(3), o nulo * é usado para indicar um ponteiro geral.

5, gravação
chama a função de gravação para gravar dados no arquivo aberto

#include<fcntl.h>
int write(int fd,void *buf,size_t size);

Seu valor de retorno é igual ao valor do tamanho do parâmetro, caso contrário, significa um erro. A causa comum de erros é que o disco está cheio ou excede o limite de tamanho de arquivo de um determinado processo.
Para arquivos comuns, a operação de gravação começa no deslocamento atual do arquivo. Se a opção O_APPEND for especificada quando o arquivo for aberto, o deslocamento do arquivo será definido para o final atual do arquivo antes de cada operação de gravação. Após uma gravação bem-sucedida, o deslocamento do arquivo aumenta o número de bytes realmente gravados.

6, funções dup e dup2
Essas duas funções são usadas para copiar um descritor de arquivo existente

#include<unistd.h>
int dup(int fileds);
int dup2(int fileds,int fileds2);

O novo descritor de arquivo retornado pelo dup deve ser o menor valor no descritor de arquivo disponível no momento. Com o dup2, você pode usar o parâmetro filedes2 para frear o valor do novo descritor. Se filedes2 já estiver aberto, feche-o primeiro. Se fileds for igual a fileds2, o dup2 retornará filedes2 sem fechá-lo.

7, stat, fstat e lstat funções

#include<sys/stat.h>
int stat(const char *restrict pathname,struct stat *restrict buf);
int fstat(int fileds,struct stat *buf);
int lstat(const char *restrict pathname,struct stat *restrict buf);

Valor de retorno: Todas as três funções retornam 0 em caso de sucesso e -1 em caso de erro. stat retorna a estrutura de informações relacionada ao arquivo nomeado. A função fstat obtém informações sobre o arquivo que foi aberto nos arquivadores do descritor de arquivo. A função lstack é semelhante à stat, mas quando o arquivo nomeado é um link simbólico, lstat retorna as informações sobre o link simbólico. Informações.

Dois, operação atômica

Uma operação atômica refere-se a uma operação composta de várias etapas.Se a operação for executada atomicamente, todas as etapas serão executadas ou uma etapa não será executada.É impossível executar apenas um subconjunto de todas as etapas.
(1) Adicionando a um arquivo
Não há opção O_APPEND na operação aberta descrita anteriormente. Não há efeito em um único processo, mas para um processo múltiplo usar esse método para adicionar dados ao mesmo arquivo ao mesmo tempo, isso causará problemas. Como o relacionamento entre as estruturas de dados é compartilhado, supõe-se que haja dois processos independentes A e B que adicionam o mesmo arquivo, cada processo abriu o arquivo, mas não usa o sinalizador O_APPEND, cada O processo possui sua própria entrada de arquivo, mas compartilha uma entrada do nó v, de modo que a operação de gravação dos dois processos fará com que os dados no arquivo sejam substituídos.
A operação lógica "localizar no final do arquivo e gravar" usa duas chamadas de função separadas. A solução para o problema é que essas duas operações se tornam uma operação atômica para outros processos.
(2) Funções de leitura e gravação O
protótipo da função é o seguinte:

#include<unistd.h>
ssize_t pread(int flags,void *buf,size_t nbytes,off_t offset);
ssize_t pwrite(int flags,void *buf,size_t nbytes,off_t offset);

Valor de retorno: Pread lê o número de bytes, se chegou ao final do arquivo, retorna 0, se falhar, retorna -1; pwrite retorna o número de bytes gravados se for bem-sucedido e retorna -1 se houver um erro.
Chamar pred é equivalente a chamar sequencialmente. lseek e read, chamar pwrite é equivalente a chamar lseek e escrever sequencialmente

3. Exemplos

Prática 1:
Com as funções básicas acima para executar operações de E / S, vamos praticar e armazenar os dados inseridos pelo usuário na interface em a.txt e exibir todo o conteúdo de a.txt no terminal como um todo Para cima

int main()
{
	int fd = open("a.txt", O_RDWR | O_CREAT, 0664);//权限设置值
	assert(-1 != fd);
	
	while(1)
	{
		printf("input: ");
		char buff[128] = {0};
		fgets(buff,128,stdin);//从用户获取数据,stdin标准输入,会把最后的回车符也放在buff中
		
		if(strncmp(buff,"end",3) == 0)
		{
			break;
		}
		
		int n =write(fd,buff,strlen(buff));
		if(n<=0)
		{
			perror("write error:");//和printf很像,但是他主要是打的出错信息
			exit(0);
		}
	}
	
	printf(****************************a.txt:*************************\n);
	lseek(fd,0,SEEK_SET);//将文件读写游标移动到开始位置
	
	while(1)
	{
		char buff[128] = {0};//从文件里面读取数据往buff中写
		int n = read(fd,buff,127);
		if( n == 0 )
		{
			printf("END\n");
			break;
		}
		else if(n<0)
		{
			perror("read error: ");
			exit(0);
		}
		else
		{
			printf("%s",buff);
		}
	}
	close(fd);
}

Prática dois:
testar o processo pai e filho através do código para compartilhar o descritor de arquivo aberto antes da bifurcação

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>

int main()
{
	int fd = open("a.txt", O_RDWR | O_CREAT, 0664);//权限设置值
	assert(-1 != fd);

	pid_t n = fork();
	assert(-1 != n);

	if(0 == n)
	{
		while(1)
		{
			char c = 0;
			int len = read(fd, &c, 1);
			if(n <= 0)
			{
				break;
			}
			printf("child:: %c\n", c);
			sleep(1);
		}
	}
	else
	{
		while(1)
		{
			char c = 0;
			int len = read(fd, &c, 1);
			if(n <= 0)
			{
				break;
			}
			printf("father:: %c\n", c);
			sleep(1);
		}
	}

	close(fd);

	exit(0);
}

Os dois resultados de execução diferentes são os seguintes:
Insira a descrição da imagem aqui
A partir dos resultados de execução acima, é possível que o descritor de arquivo seja aberto antes que o fork seja acessado pelos processos pai e filho, e o deslocamento de leitura e gravação do arquivo seja compartilhado. Os caracteres não são compartilhados . O processo de implementação interna é o seguinte:
Insira a descrição da imagem aqui

Quarto, a diferença entre funções de biblioteca e funções de chamada do sistema

(1) Conceito
Primeiro, precisamos esclarecer o que é uma função de biblioteca e o que é uma função de chamada do sistema. As funções fopen, fread, fwrite, fclose e fseek que aprendemos antes são todas as funções de biblioteca às quais nos referimos, e a leitura, gravação, fechamento etc. listadas no início deste artigo são funções de chamada do sistema. Por exemplo, costumamos encontrar uma pergunta dessas: qual leitura e medo é mais eficiente? Na verdade, não é uma leitura absoluta com alta eficiência.Quando há poucos arquivos lidos, porque o fread terá consumo de chamadas do modo de usuário para o modo kernel, mas para ler uma grande quantidade de dados, a operação do fread é toda Coloque na área de acesso do usuário quanto os usuários usam para obter quanto, mas a operação de leitura é a quantidade de dados para ler quantos dados.
Isso nos leva ao conceito de funções de biblioteca e funções de chamada do sistema. A função de chamada do sistema é uma interface que o kernel do sistema executa para chamar no espaço do usuário.A função de chamada do sistema é chamada pelo modo do usuário e executada no modo do kernel. Correspondente a isso é a função de biblioteca, a função de biblioteca é implementada no arquivo da biblioteca de funções e só precisa ser executada no modo de usuário durante a execução.

(2) Diferença
De fato, no conceito, podemos saber claramente sua diferença: a função de biblioteca está no arquivo de biblioteca de funções e a função de chamada do sistema é implementada no kernel do sistema. A seguir, explicamos cuidadosamente o princípio de implementação da função de chamada do sistema com open como a coluna.
Em nosso sistema, a relação entre eles é mostrada na figura a seguir:
Insira a descrição da imagem aqui
1. Encontre primeiro o número de chamada do sistema correspondente à função, salve-o no registro exa
2, a função de chamada do sistema aciona a interrupção 0x80 e depois cai no kernel, o kernel começa a executar a interrupção Manipulador. As instruções importantes da interrupção 0x80 são as seguintes

call [_sys_call_table+eax*4]

Esta instrução é principalmente para permitir que o número de chamada do sistema armazenado no registro eax encontre o método da função do kernel na tabela de chamada do sistema do kernel e execute-o.
3. Depois que a função for chamada, haverá um retorno fd e um valor inteiro.Insira o valor inteiro no registro eax e depois mude para o modo de usuário.Em seguida, uma instrução mov move o valor do registro eax para o endereço apontado por fd. Isso é equivalente a salvar o valor de retorno da função.
O processo específico é o seguinte:
Insira a descrição da imagem aqui

Publicado 98 artigos originais · ganhou elogios 9 · vista 3641

Acho que você gosta

Origin blog.csdn.net/qq_43412060/article/details/105460239
Recomendado
Clasificación