Библиотека сайта rus-linux.net
Цилюрик О.И. Модули ядра Linux | ||
Назад | Внешние интерфейсы модуля | Вперед |
Путь пакета сквозь стек протоколов
Теперь у нас достаточно деталей, чтобы проследить путь пакетов (буферов сокетов) сквозь сетевой стек, проследить то, как буфера сокетов возникают в системе, и когда они её покидают, а также ответить на вопрос, почему вышележащие протокольные уровни (будут рассмотрены чуть ниже) никогда не порождают и не уничтожают буферов сокетов, а только обрабатывают (или модифицируют) содержащуюся в них информацию (работают как фильтры). Итак, последовательность связей мы можем разложить в таком порядке:
1. Читая конфигурационную область PCI адартера сети при инициализации модуля, определяем линию прерывания IRQ, которая будет обслуживать сетевой обмен:
char irq; pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &byte );
Точно таким же манером будет определена и область адресов ввода-адресов адаптера, скорее всего, через DMA ... - всё это рассматривается позже, при рассмотрении аппаратных шин.
2. При инициализации сетевого интерфейса, для этой линии IRQ устанавливается обработчик прерывания my_interrupt():
request_irq( (int)irq, my_interrupt, IRQF_SHARED, "my_interrupt", &my_dev_id );
3. В обработчике прерывания, по приёму нового пакета из сети (то же прерывание может происходить и при завершении отправки пакета в сеть, здесь нужен анализ причины), создаётся (или запрашивается из пула используемых) новый экземпляр буфера сокетов:
static irqreturn_t my_interrupt( int irq, void *dev_id ) { ... struct sk_buff *skb = kmalloc( sizeof( struct sk_buff ), ... ); // заполнение данных *skb чтением из портов сетевого адаптера netif_rx( skb ); return IRQ_HANDLED; }
Все эти действия выполняются не в самом обработчике верхней половины прерываний от сетевого адаптера, а в обработчике отложенного прерывания NET_RX_SOFTIRQ (см. ранее) для этой линии. Последним действием является передача заполненного сокетного буфера вызову netif_rx(), который и запустит процесс движения его (буфера) вверх по структуре сетевого стека.
Этим обеспечивается движение сокетного буфера вверх по стеку. Движение вниз (при отправке в сеть) обеспечивается по цепочке.
4. При инициализации сетевого интерфейса (это момент, который уже был назван в п.2), создаётся таблица операций сетевого интерфейса, одно из полей которой ndo_start_xmit определяет функцию передачи пакета в сеть:
struct net_device_ops ndo = { .ndo_open = my_open, .ndo_stop = my_close, .ndo_start_xmit = stub_start_xmit, };
5. При вызове stub_start_xmit() должна обеспечить аппаратную передачу полученного сокета в сеть, после чего уничтожает (возвращает в пул) буфер сокета:
static int stub_start_xmit( struct sk_buff *skb, struct net_device *dev ) { // ... аппартное обслуживание передачи dev_kfree_skb( skb ); return 0; }
Реально чаще уничтожение отправляемого буфера будет происходить не при инициализации операции, а при её (успешном) завершении, что отслеживается по той же линии IRQ, упоминавшейся выше.
Часто задаваемый вопрос: а где же в этом процессе место, где реально создаётся информация, помещаемая в буфер, или где потребляется информация из принимаемых буферов? Ответ: не ищите такого места в пределах сетевого стека ядра — любая информация для отправки в сеть, или потребляемая из сети, возникает в поле зрения только на прикладных уровнях, в приложениях пространства пользователя, таких, например, как ping, ssh, telnet и великое множество других. Интерфейс из этого прикладного уровня в стек протоколов ядра обеспечивается известным API сокетов прикладного уровня.
Предыдущий раздел: | Оглавление | Следующий раздел: |
Драйверы: сетевой интерфейс | Протокол сетевого уровня |