Processo e método de chamada do sistema no Linux

índice

1. Processo de chamada do sistema

2. Três métodos de chamada de sistema

2.1. Funções de biblioteca fornecidas pela glibc

2.2, use syscall para chamar diretamente

2.3, preso pela instrução int


Chamada de sistema é um conjunto de interfaces fornecidas pelo sistema operacional para a interação entre processos em execução no modo de usuário e dispositivos de hardware (como CPUs, discos, impressoras, etc.). Quando o processo do usuário precisa fazer uma chamada de sistema, a CPU muda para o modo kernel por meio de uma interrupção suave para iniciar a execução da função de chamada de sistema kernel.

1. Processo de chamada do sistema

Pegue o Linux 0.11 como um exemplo para descrever brevemente o processo de chamada e não verifique se o sistema operacional moderno mudou, mas a ideia básica deve ser semelhante. Como mostrado abaixo:

Apresentar o processo de chamada de sistema no Linux Apresentar o processo de chamada de sistema no Linux

Em primeiro lugar, o programa aplicativo pode chamar diretamente a API fornecida pelo sistema, o que pode ser feito no modo de usuário (Ring3).

Em seguida, a API correspondente salvará o número de chamada do sistema correspondente no registro eax (esta etapa é implementada por montagem embutida) e, em seguida, usará int 0x80 para acionar a interrupção (montagem embutida) e inserir a função de processamento de interrupção (a função é Completamente escrito em código assembly), desta vez entra no modo kernel (Ring0).

Na função de tratamento de interrupções, a chamada do sistema correspondente ao número de chamada do sistema é chamada. Nesta função, os dois registradores ds (registrador de segmento de dados) e es (registrador extra) serão configurados para apontar para o espaço do kernel . Desta forma, não podemos transferir dados do modo do usuário para o modo kernel (como open (const char * filename, int flag, ...), o endereço da string apontada pelo ponteiro do nome do arquivo está no espaço do usuário, Se você tomar o lugar correspondente no espaço do kernel, não existe tal string), o que devo fazer? O registro fs na função de tratamento de interrupção é definido para apontar para o espaço do usuário , então o problema está resolvido.

As operações correspondentes são realizadas na chamada do sistema, como abrir e gravar arquivos.

Após o processamento, ele retornará à função de processamento de interrupção, e o valor de retorno será armazenado no registrador eax.

Retornar da função de tratamento de interrupção para a API ainda salva o valor de retorno no registrador eax. Nesse momento, ele é restaurado do modo kernel para o modo do usuário.

Pegue o valor de eax na API e faça o julgamento correspondente para retornar um valor diferente para indicar a conclusão da operação.

Por que tantas chamadas de sistema podem ser chamadas usando a interrupção int 0x80?

No modo protegido, existem várias interrupções e a chamada do sistema está vinculada à interrupção 0x80. Quando uma chamada de sistema deve ser chamada, int 0x80 é disparado e a função de tratamento de interrupção sabe qual chamada de sistema deseja chamar por meio de eax. A razão para isso é que há muitas chamadas de sistema e o número de interrupção será insuficiente, portanto, um é usado para gerenciamento centralizado.

Existe uma tabela no sistema operacional, que é usada para armazenar os endereços de várias funções de chamada do sistema. Esta tabela é um array, portanto os endereços das diferentes funções podem ser acessados ​​por subscritos. Portanto, um número de interrupção + vários números de chamada do sistema podem gerenciar várias chamadas do sistema.

O texto acima foi retirado de "Apresentando o Processo de Chamada de Sistema no Linux"

2. Três métodos de chamada de sistema

O seguinte apresenta três métodos de ocorrência de chamada de sistema no Linux.

2.1. Funções de biblioteca fornecidas pela glibc

glibc é uma biblioteca C padrão de código aberto usada no Linux.É a biblioteca libc lançada pelo GNU, ou seja, a biblioteca de tempo de execução. A glibc fornece aos programadores uma API (Interface de Programação de Aplicativo) rica. Além dos serviços do modo de usuário, como processamento de string e operações matemáticas, o mais importante é encapsular os serviços do sistema fornecidos pelo sistema operacional, ou seja, o encapsulamento das chamadas do sistema. Então, qual é a relação entre a API de chamada do sistema fornecida pela glibc e a chamada do sistema específica do kernel?

  • Normalmente, cada chamada de sistema específica corresponde a pelo menos uma função de biblioteca encapsulada por glibc. Por exemplo, a chamada de sistema de arquivo aberto fornecida pelo sistema  sys_open corresponde a uma open função em glibc  ;
  • Em segundo lugar, glibc uma chamada API separada pode invocar vários sistemas, tais como a fornecida por glibc  printf função vai chamar, tais como  sys_open, sys_mmap, sys_write, sys_close como a chamada do sistema;
  • Além disso, várias APIs podem corresponder apenas à mesma chamada de sistema. Por exemplo , as  funções implementadas em glibc, como ,,  malloce assim por diante calloc, freesão usadas para alocar e liberar memória, todas usando as sys_brk chamadas de sistema do kernel  .

Por exemplo, usamos a chmod função fornecida pela glibc para alterar o etc/passwd atributo do arquivo  para 444:

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>

int main()
{
	int rc = 0;
	rc = chmod("/etc/passwd", 0444);
	if (rc == -1)
		fprintf(stderr, "chmod failed, errno = %d\n", errno);
	else
		printf("chmod success!\n");
	return 0;
}

Em uso normal, o usuário para compilar , como saída:
o chmod falhou, errno = 1
A chamada do sistema acima retorna um valor de -1, indicando que a chamada do sistema falhou, um código de erro, no / usr / include / asm-generic / errno-base O arquivo .h tem a seguinte descrição de código de erro:
#define EPERM 1 / * Operação não permitida * /
Ou seja, não há permissão para realizar a operação. Não podemos modificar os atributos do arquivo / etc / passwd com permissões de usuário comuns. O resultado está correto.

2.2, use syscall para chamar diretamente

Há muitas vantagens em usar o método acima. Primeiro, você não precisa saber mais detalhes, como o número de chamada do sistema chmod. Você só precisa entender o protótipo da API fornecida pela glibc. Em segundo lugar, o método tem melhor portabilidade e você pode mudar facilmente O programa é transplantado para outras plataformas, ou a biblioteca glibc é substituída por outra biblioteca, o programa só precisa ser alterado algumas vezes.
Mas uma desvantagem é que se a glibc não encapsular uma chamada de sistema fornecida por um determinado kernel, não posso chamar a chamada de sistema por meio do método acima . Se eu adicionar uma chamada de sistema compilando o kernel sozinho, a glibc não poderá ter a API de encapsulamento de sua nova chamada de sistema. Neste momento, podemos usar a syscall função fornecida pela glibc para chamar diretamente. A função é definida no  unistd.h arquivo de cabeçalho e o protótipo da função é o seguinte:

long int syscall (long int sysno, ...)
  • sysno  é o número da chamada do sistema e cada chamada do sistema possui um número de chamada do sistema exclusivo para identificá-la. Em sys/syscall.h há todo o sistema possível chama a definição de macro.
  • ...  São os parâmetros de comprimento variável restantes, que são os parâmetros da chamada do sistema. Dependendo da chamada do sistema, pode levar de 0 a 5 parâmetros. Se exceder os parâmetros que uma chamada de sistema específica pode assumir, os parâmetros extras serão ignorar.
  • Valor de retorno  O valor de retorno desta função é o valor de retorno de uma chamada do sistema específica. Depois que a chamada do sistema for bem-sucedida, você pode converter o valor de retorno para um tipo específico. Se a chamada do sistema falhar, ele retorna -1 e o código de erro é armazenado errno nele.

Pegue também a modificação acima dos atributos do arquivo / etc / passwd como exemplo, desta vez use syscall para chamar diretamente:

    ...
	//rc = chmod("/etc/passwd", 0444);
	rc = syscall(SYS_chmod, "/etc/passwd", 0444);
    ...

Compile e execute sob usuários normais, e o resultado de saída é o mesmo que o exemplo acima.

2.3, preso pela instrução int

Se conhecermos todo o processo da chamada do sistema, devemos ser capazes de saber que o programa do modo de usuário int 0x80 entra no modo kernel por meio da instrução de interrupção suave (as sysenterinstruções são introduzidas no Intel Pentium II ), o parâmetro é passado pelo registrador e eax é o número de chamada do sistema. , Ebx, ecx, edx, esi e edi para passar até cinco parâmetros por sua vez, quando a chamada do sistema retorna, o valor de retorno é armazenado em eax.

Ainda tomando a modificação acima dos atributos do arquivo como exemplo, escreva a seção que chama a chamada do sistema como código de assembly embutido:

#include <stdio.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <errno.h>

int main()
{
	long rc;
	char *file_name = "/etc/passwd";
	unsigned short mode = 0444;

	asm(
		"int $0x80"
		: "=a" (rc)
		: "0" (SYS_chmod), "b" ((long)file_name), "c" ((long)mode)
	);

	if ((unsigned long)rc >= (unsigned long)-132) {
		errno = -rc;
		rc = -1;
	}

	if (rc == -1)
		fprintf(stderr, "chmode failed, errno = %d\n", errno);
	else
		printf("success!\n");

	return 0;
}

Se o valor de retorno armazenado no registrador eax (armazenado na variável rc) estiver entre -1 e -132, deve ser interpretado como um código de erro ( /usr/include/asm-generic/errno.h o código de erro máximo definido no arquivo é 132). Neste momento, escreva o código de erro Em errno, defina o valor de retorno da chamada do sistema para -1; caso contrário, ele retorna o valor em eax.

O programa acima é compilado e executado em Linux de 32 bits com direitos de usuário comuns, e o resultado é o mesmo que os dois anteriores! , Em um ambiente de 64 bits, chmode falhou, errno = 22: O parâmetro é inválido.

 

Acho que você gosta

Origin blog.csdn.net/wangquan1992/article/details/108496821
Recomendado
Clasificación