Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Модули ядра 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 сокетов прикладного уровня.


Предыдущий раздел: Оглавление Следующий раздел:
Драйверы: сетевой интерфейс   Протокол сетевого уровня