Como posso encontrar as implementações de chamadas do sistema do kernel Linux?

375

Estou tentando entender como uma função, digamos mkdir, funciona, olhando para a fonte do kernel. Esta é uma tentativa de entender os componentes internos do kernel e navegar entre várias funções. Eu sei que mkdirestá definido em sys/stat.h. Eu encontrei o protótipo:

/* Create a new directory named PATH, with permission bits MODE.  */
extern int mkdir (__const char *__path, __mode_t __mode)
     __THROW __nonnull ((1));

Agora eu preciso ver em qual arquivo C essa função está implementada. No diretório de origem, tentei

ack "int mkdir"

que exibido

security/inode.c
103:static int mkdir(struct inode *dir, struct dentry *dentry, int mode)

tools/perf/util/util.c
4:int mkdir_p(char *path, mode_t mode)

tools/perf/util/util.h
259:int mkdir_p(char *path, mode_t mode);

Mas nenhum deles corresponde à definição em sys/stat.h.

Questões

  1. Qual arquivo tem a mkdirimplementação?
  2. Com uma definição de função como a acima, como posso descobrir qual arquivo tem a implementação? Existe algum padrão que o kernel segue na definição e implementação de métodos?

NOTA: Estou usando o kernel 2.6.36-rc1 .

Navaneeth KN
fonte
2
A propósito, confira o seguinte: voinici.ceata.org/~tct/resurse/utlk.pdf
Tom Brito

Respostas:

386

As chamadas do sistema não são tratadas como chamadas de função regulares. É necessário um código especial para fazer a transição do espaço do usuário para o espaço do kernel, basicamente um pouco de código de montagem embutido injetado no seu programa no site de chamada. O código do lado do kernel que "captura" a chamada do sistema também é algo de baixo nível que você provavelmente não precisa entender profundamente, pelo menos a princípio.

No include/linux/syscalls.hdiretório de origem do seu kernel, você encontra o seguinte:

asmlinkage long sys_mkdir(const char __user *pathname, int mode);

Então /usr/include/asm*/unistd.h, você encontra o seguinte:

#define __NR_mkdir                              83
__SYSCALL(__NR_mkdir, sys_mkdir)

Este código está dizendo mkdir(2)é a chamada de sistema # 83. Ou seja, as chamadas do sistema são chamadas por número, não por endereço, como ocorre com uma chamada de função normal em seu próprio programa ou para uma função em uma biblioteca vinculada ao seu programa. O código de cola de montagem em linha que mencionei acima usa isso para fazer a transição do espaço do usuário para o kernel, levando seus parâmetros junto com ele.

Outra evidência de que as coisas são um pouco estranhas aqui é que nem sempre existe uma lista estrita de parâmetros para chamadas do sistema: open(2)por exemplo, pode-se usar 2 ou 3 parâmetros. Isso significa que open(2)está sobrecarregado , um recurso do C ++, não do C, mas a interface do syscall é compatível com o C. (Isso não é o mesmo que o recurso varargs de C , que permite que uma única função receba um número variável de argumentos.)

Para responder sua primeira pergunta, não existe um arquivo único onde mkdir()exista. O Linux suporta muitos sistemas de arquivos diferentes e cada um tem sua própria implementação da operação "mkdir". A camada de abstração que permite que o kernel oculte tudo isso atrás de uma única chamada do sistema é chamada de VFS . Então, você provavelmente quer começar a pesquisar fs/namei.c, com vfs_mkdir(). As implementações reais do código de modificação do sistema de arquivos de baixo nível estão em outro lugar. Por exemplo, a implementação ext4 é chamada ext4_mkdir(), definida em fs/ext4/namei.c.

Quanto à sua segunda pergunta, sim, existem padrões para tudo isso, mas nenhuma regra. O que você realmente precisa é de um entendimento bastante amplo de como o kernel funciona, a fim de descobrir onde você deve procurar qualquer chamada específica do sistema. Nem todas as chamadas do sistema envolvem o VFS; portanto, as cadeias de chamadas do lado do kernel nem todas são iniciadas fs/namei.c. mmap(2), por exemplo, inicia mm/mmap.c, porque faz parte do subsistema de gerenciamento de memória ("mm") do kernel.

Eu recomendo que você obtenha uma cópia de " Entendendo o Kernel do Linux ", de Bovet e Cesati.

Warren Young
fonte
Resposta muito boa. Um ponto sobre o livro que você mencionou, "Entendendo o kernel do Linux". Eu não o tenho, mas a partir da data de lançamento (2000) e do TOC (no site da oreilly) me parece que são cerca de 2,2 kernels, além de algumas informações de 2,4 kernels (mas eu posso estar errado). Minha pergunta é: existe um livro equivalente que cubra 2,6 kernels internos? (ou ainda melhor que abrangem 2.2, 2.4 e 2.6)?
DavAlPi
2
@DavAlPi: Até onde eu sei, Bovet & Cesati ainda é o melhor livro sobre esse assunto. Quando eu preciso complementá-lo com material mais atualizado, vou cavando no Documentationsubdiretório da árvore de origem do kernel com o qual estou trabalhando.
Warren Young
1
De fato, open (2) é uma função varargs. Existem apenas duas maneiras de chamá-lo, de modo que a página de manual o documenta dessa maneira, o protótipo real possui ...como qualquer função de variável. Obviamente, isso é implementado no nível libc. Pode passar 0 ou um valor de lixo para a ABI do kernel quando o terceiro parâmetro não for usado.
precisa saber é o seguinte
"É algo que você não precisa entender". O mundo seria um lugar melhor se esse tipo de sentença não estivesse em nenhum lugar na rede stackexchange.
Petr
84

Provavelmente, isso não responde diretamente à sua pergunta, mas achei stracemuito legal tentar entender as chamadas de sistema subjacentes, em ação, feitas até para os comandos mais simples do shell. por exemplo

strace -o trace.txt mkdir mynewdir

As chamadas do sistema para o comando mkdir mynewdirserão enviadas para trace.txt para seu prazer.

Banjer
fonte
5
+1 truque puro! Eu não tinha usado isso antes
David Oneill
3
Melhor ainda, faça o arquivo de saída trace.strace e abra-o no VIM. O VIM irá destacá-lo, facilitando a leitura.
Marcin
55

Um bom lugar para ler a fonte do kernel do Linux é a referência cruzada do Linux (LXR) ¹. As pesquisas retornam correspondências digitadas (protótipos de funções, declarações de variáveis ​​etc.), além de resultados de pesquisa de texto livre, por isso é mais prático do que um mero grep (e mais rápido também).

O LXR não expande as definições do pré-processador. As chamadas do sistema têm seu nome mutilado pelo pré-processador em todo o lugar. No entanto, a maioria das chamadas de sistema (todas?) São definidas com uma das SYSCALL_DEFINExfamílias de macros. Como mkdirleva dois argumentos, uma pesquisa SYSCALL_DEFINE2(mkdirleva à declaração do mkdirsyscall :

SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)
{
    return sys_mkdirat(AT_FDCWD, pathname, mode);
}

ok, sys_mkdiratsignifica que é o mkdiratsyscall; portanto, clicar nele apenas leva à declaração include/linux/syscalls.h, mas a definição está logo acima.

O principal trabalho de mkdiraté chamar vfs_mkdir(VFS é a camada genérica do sistema de arquivos). Clique aqui para mostrar dois resultados de pesquisa: a declaração em include/linux/fs.he a definição algumas linhas acima. O principal trabalho do vfs_mkdiré chamar a implementação específica do sistema de arquivos: dir->i_op->mkdir. Para descobrir como isso é implementado, você precisa recorrer à implementação de um sistema de arquivos individual e não há uma regra rígida - pode até ser um módulo fora da árvore do kernel.

¹ LXR é um programa de indexação. Existem vários sites que fornecem uma interface para o LXR, com conjuntos ligeiramente diferentes de versões conhecidas e interfaces da web ligeiramente diferentes. Eles tendem a ir e vir, por isso, se você não estiver disponível, faça uma pesquisa na web por “referência cruzada linux” para encontrar outro.

Gilles
fonte
Esse é um pedaço de um recurso. Ótima resposta.
Stabledog
"Erro interno do servidor" no link linux.no .
Fredrick Gauss
@FredrickGauss Por um tempo, o lxr.linux.no foi a interface mais agradável para o LXR, mas teve um tempo de inatividade frequente. Agora eu acho que se foi para sempre. Troquei o primeiro link para outra interface LXR.
Gilles
21

As chamadas do sistema geralmente são agrupadas na SYSCALL_DEFINEx()macro, e é por isso que um simples grepnão as encontra:

fs/namei.c:SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)

O nome da função final após a expansão da macro acaba sendo sys_mkdir. A SYSCALL_DEFINEx()macro adiciona itens padrão, como código de rastreamento que cada definição de syscall precisa ter.

stefanha
fonte
17

Nota: o arquivo .h não define a função. É declarado nesse arquivo .h e definido (implementado) em outro lugar. Isso permite que o compilador inclua informações sobre a assinatura da função (protótipo) para permitir a verificação de tipos de argumentos e corresponder os tipos de retorno a qualquer contexto de chamada em seu código.

Em geral, os arquivos .h (cabeçalho) em C são usados ​​para declarar funções e definir macros.

mkdirem particular, é uma chamada do sistema. Pode haver um wrapper GNU libc em torno dessa chamada de sistema (quase certamente existe, de fato). A verdadeira implementação do kernel mkdirpode ser encontrada pesquisando as fontes do kernel e as chamadas do sistema em particular.

Observe que também haverá uma implementação de algum tipo de código de criação de diretório para cada sistema de arquivos. A camada VFS (sistema de arquivos virtual) fornece uma API comum na qual a camada de chamada do sistema pode chamar. Todo sistema de arquivos deve registrar funções para a camada VFS chamar. Isso permite que diferentes sistemas de arquivos implementem sua própria semântica de como os diretórios são estruturados (por exemplo, se eles são armazenados usando algum tipo de esquema de hash para tornar mais eficiente a pesquisa de entradas específicas). Menciono isso porque você provavelmente tropeçará nessas funções de criação de diretório específicas do sistema de arquivos se estiver pesquisando na árvore de fontes do kernel Linux.

Jim Dennis
fonte
8

Nenhuma das implementações encontradas corresponde ao protótipo em sys / stat.h Talvez a pesquisa de uma instrução de inclusão com esse arquivo de cabeçalho seja mais bem-sucedida?

greg0ire
fonte
1
A implementação (conforme descrito em sys / stat.h) é um negócio da userland e da libc. O material interno do kernel (como realmente é feito) é um negócio interno do kernel. Para todos os hackers do kernel, a função interna pode ser chamada xyzzy e ter 5 parâmetros. O trabalho da libc é atender a chamada do usuário, traduzi-la para quaisquer encantamentos do kernel necessários, enviá-la e coletar quaisquer resultados.
vonbrand
6

Aqui estão algumas ótimas postagens no blog descrevendo várias técnicas para procurar o código-fonte do kernel de baixo nível.

An̲̳̳drew
fonte
12
Por favor, não poste apenas links para blogs ou fóruns, resuma seu conteúdo para que os leitores possam ver do que se tratam e ainda resta algo se os sites desaparecerem. Além disso, seu primeiro link é sobre libc, o que não é relevante para esta pergunta.
Gilles