Conhecendo a macro container_of

funções X macros compilação condicional Diagnóstico

Olá, caro leitor! Neste artigo apresentarei algumas operações com ponteiros e estruturas que podem ser bem úteis para estruturar um projeto. Como solução ao problema que será abordado, apresentarei uma macro muito utilizada no Linux: A macro container_of. Tal macro serve para obter o endereço de uma estrutura que contém um campo do qual apenas conhecemos seu endereço. Dessa maneira, o artigo foi estruturado em duas partes: A primeira descreve um estudo de caso, já a segunda apresenta em detalhes a macro container_of.

Estudo de Caso de container_of

Considere, por exemplo, que uma estrutura t_device foi definida no seu projeto. Essa estrutura é utilizada como base para todos os dispositivos que são utilizados. Além disso, uma segunda estrutura contém informações específicas de um dispositivo chamado led. Nesse caso, a estrutura t_led contém dois elementos específicos mais a estrutura dev, do tipo t_device.

Por definição, todo módulo do seu projeto tem uma função chamada DoSomething. Tal função recebe como argumento um endereço para uma estrutura do tipo t_device.

De fato, como temos o endereço do dado, podemos acessá-lo normalmente. Mas, e se ao invés de acessar o tipo dado t_device você quisesse acessar a estrutura t_led? Isso é válido pois dev é membro de t_led. É claro que podemos aplicar aquelas boas práticas de codificação para obter o resultado desejado... mas, vamos ver como isso é feito no Linux.

Considere que a função DoSomething é chamada passando como argumento o endereço de 'led.dev', representado por 'ptr'. Dentro da função, o objetivo é obter um outro ponteiro, porém, apontando para 'base address', isto é, o endereço de led.

Representação das estruturas na memória, para teste da macro container_of
Figura 1: Representação das estruturas na memória.

A macro container_of pode ser utilizada nessa situação. Isto é, para obter o endereço de uma estrutura que contém um campo do qual apenas conhecemos seu endereço, precisamos apenas informar qual o tipo da estrutura que queremos e de qual membro temos o endereço. Dessa maneira, a macro retorna o endereço para o tipo especificado. Cabe ressaltar que um tipo de dado comum também pode ser utilizado.

Para testar isso o seguinte trecho de código pode ser utilizado:

A Macro container_of

O Linux kernel possui um procedimento para tratar o caso apresentado anteriormente. Tal procedimento é realizado pela macro container_of, definida em include/linux/kernel.h.

Além disso, a macro utiliza algumas definições específicas do gcc, como o caso do typeof. Já a macro offsetof é C ANSI, sendo definida em stddef.h. A macro offsetof recebe o tipo de dado composto (TYPE) e o nome de um membro (MEMBER).

Assim, a macro offsetof considera um ponteiro do tipo da estrutura com endereço zero ((TYPE *)0). Dessa maneira, ao acessar um membro ((TYPE *)0)->MEMBER e, em seguida, obtendo seu endereço &(((TYPE *)0)->MEMBER), estaremos calculando o offset desse membro em relação ao endereço base. Pois o endereço do membro menos o endereço base, que é igual a zero, resulta no próprio deslocamento.

Agora, quando a macro container_of é utilizada, o seu retorno será o endereço para o tipo da estrutura especificada. Esse procedimento é realizado em duas etapas e, por isso, possui o código entre chaves (compound statement). container_of(ptr, type, member) ({ .... }). Dessa maneira, o resultado da última operação será atribuído à variável de destino.

Primeiro é definido um ponteiro __mptr do tipo do membro. Esse ponteiro é inicializado com o endereço recebido.

const typeof( ((type *)0)->member ) *__mptr = (ptr);

Depois é realizada a diferença desse endereço (com cast para char *) com o offset do membro.

(TYPE *)( (char *)__mptr - offsetof(TYPE,MEMBER));

Essa última operação já resulta no endereço real da estrutura informada.

Cabe ressaltar que o código não faz verificação de tipo em tempo de execução. Dessa maneira, o desenvolvedor fica responsável por essa especificação. Dito de outra maneira, por ser uma macro, os seus parâmetros não têm especificadores de tipo. Assim, o ponteiro (ptr) recebido não tem seu tipo conhecido. Logo, a primeira linha funciona como um especificador de tipo para o ponteiro e, portanto, o membro especificado deve pertencer ao tipo de dado. Desse modo, o compilador pode detectar incompatibilidade entre as informações passadas na macro. 

Para sabe mais

Essa macro foi escrita inicialmente por Gregh Kroah-Hartman. Neste link, Gregh apresenta um artigo que foi publicado na Linux Journal em Junho de 2003, tópico 110. O propósito era escrever um código mais estável para gravar e configurar dispositivos hot plug. Tal macro facilita a construção e manipulação de projetos que têm como característica a “herança de estruturas”. Exemplo disso é a unificação do sistema de drivers durante o desenvolvimento do Kernel 2.5. A partir desse ponto todos os drivers passaram a usar uma estrutura em comum chamada device (struct struct). Assim, a macro container_of é utilizada para obter o endereço da estrutura que contém essa informação do dispositivo. Alguns padrões comuns no desenvolvimento de drivers são descritos neste link.

Referências

Imagem destacada.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Software » Conhecendo a macro container_of
Comentários:
Notificações
Notificar
guest
2 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Gustavo Laureano Cardoso
Gustavo
26/01/2017 07:15

Ótimo artigo, parabens!

Hugo Borges
Hugo Borges
24/01/2017 08:25

Bem legal! Não conhecia... bem útil pra obter um comportamento parecido com polimorfismo.
Das vezes que precisei fazer algo do tipo, eu sempre deixava um ponteiro void * pro parent.

Talvez você goste:

Séries

Menu

WEBINAR
 
NVIDIA JETSON – A Inteligência Artificial na palma de sua mão

Data: 08/07 às 14:00h Apoio: Arrow | NVIDIA
 
INSCREVA-SE AGORA »



 
close-link

WEBINAR
 
Redes Mesh para Monitoramento
e Controle de Sensores

Data: 15/07 às 14:00h Apoio: Artimar| Microchip| Tecsus
 
INSCREVA-SE AGORA »



 
close-link