2 ~~ >>ネットワークドライブアーキテクチャの基本-Linuxネットワークパケット受信プロセス

この記事を通じて、Linuxネットワークシステムが内部でどのように実装され、さまざまな部分がどのように相互作用するかについて深く理解することができます。これはあなたの仕事に大いに役立つと信じています。
1.Linuxネットワークパケット収集の概要

TCP / IPネットワーク層状モデルでは、プロトコルスタック全体が物理層、リンク層、ネットワーク層、トランスポート層、およびアプリケーション層に分割されます。
物理層はネットワークカードとネットワークケーブルに対応し、アプリケーション層は一般的なNginx、FTP、その他のアプリケーションに対応します。Linuxは、リンク層、ネットワーク層、トランスポート層の3つの層を実装しています。
Linuxカーネルの実装では、リンク層プロトコルはネットワークカードドライバーによって実装され、カーネルプロトコルスタックはネットワーク層とトランスポート層を実装します。カーネルは、ユーザープロセスがアクセスするための上位アプリケーション層へのソケットインターフェイスを提供します。Linuxの観点から見たTCP / IPネットワークの階層化モデルは次のようになります。
ここに画像の説明を挿入します
Linuxソースコードでは、ネットワークデバイスドライバーに対応するロジックはdriver / net / ethernetにあり、Intelシリーズネットワークカードのドライバーはdriver / net / ethernet / intelディレクトリにあります。プロトコルスタックモジュールコードは、カーネルディレクトリとネットディレクトリにあります。

カーネルとネットワークデバイスのドライバーは、割り込みによって処理されます。データがデバイスに到着すると、CPUの関連するピンで電圧変化がトリガーされ、データを処理するようにCPUに通知します。ネットワークモジュールの場合、処理が複雑で時間がかかるため、割り込み機能ですべての処理が完了すると、割り込み処理機能(優先度が高すぎる)がCPUを過度に占有し、CPUが占有されなくなります。他のデバイスに応答できます。たとえば、マウスやキーボードのメッセージ。そのため、Linuxの割り込み処理機能は上半分と下半分に分かれています。上半分は最も単純な作業であり、処理が高速で、CPUを解放します。その後、CPUは他の割り込みの入力を許可できます。残りの作業のほとんどは下半分に配置され、ゆっくりと落ち着いて処理できます。2.4以降のカーネルバージョンの実装の下半分はソフト割り込みであり、ksoftirqdカーネルスレッドによって処理されます。ハード割り込みとは異なり、ハード割り込みはCPUの物理ピンに電圧変化を適用しますが、ソフト割り込みはメモリ内の変数のバイナリ値を与えることによってソフト割り込みハンドラに通知します。

2、Linuxブート

Linuxドライバー、カーネルプロトコルスタック、およびその他のモジュールは、ネットワークカードデータパケットを受信するために装備される前に、多くの準備作業を行う必要があります。たとえば、ksoftirqdカーネルスレッドを事前に作成し、各プロトコルに対応する処理機能を登録し、ネットワークデバイスサブシステムを事前に初期化し、ネットワークカードを起動する必要があります。これらの準備ができて初めて、実際にデータパケットの受信を開始できます。それでは、これらの準備がどのように行われるかを見てみましょう。

2.1ksoftirqdカーネルスレッドを作成する

Linuxのソフト割り込みはすべて専用のカーネルスレッド(ksoftirqd)で実行されるため、後でパケット受信プロセスをより正確に理解できるように、これらのプロセスがどのように初期化されるかを確認する必要があります。プロセスの数は1ではなくNです。ここで、Nはマシンのコアの数と同じです。システムが初期化されると、smpboot_register_percpu_threadがkernel / smpboot.cで呼び出され、この関数がさらにspawn_ksoftirqd(kernel / softirq.cにあります)に対して実行されて、softirqdプロセスが作成されます。

2.2ネットワークサブシステムの初期化
Linuxカーネルは、subsys_initcallを呼び出すことにより、各サブシステムを初期化します。この関数への多くの呼び出しは、ソースコードディレクトリでgrepアウトできます。ここでは、net_dev_init関数に対して実行されるネットワークサブシステムの初期化について説明します。

この関数では、softnet_dataデータ構造が各CPUに適用されます。このデータ構造のpoll_listは、ドライバーがポーリング関数を登録するのを待機しています。このプロセスは、後でネットワークカードドライバーが初期化されるときに確認できます。

さらに、open_softirqは、各ソフト割り込みの処理関数を登録します。NET_TX_SOFTIRQの処理関数はnet_tx_actionであり、NET_RX_SOFTIRQの関数はnet_rx_actionです。open_softirqの追跡を続けた後、この登録方法がsoftirq_vec変数に記録されていることがわかりました。ksoftirqdスレッドが後でソフト割り込みを受信すると、この変数を使用して、各ソフト割り込みに対応する処理関数を検索します。

2.3プロトコルスタックの登録
カーネルは、ネットワーク層のipプロトコル、およびトランスポート層のtcpプロトコルとudpプロトコルを実装します。これらのプロトコルの対応する実装関数は、ip_rcv()、tcp_v4_rcv()、およびudp_rcv()です。通常のコードの記述方法とは異なり、カーネルは登録によって実装されます。Linuxカーネルのfs_initcallはsubsys_initcallに似ており、初期化モジュールのエントリポイントでもあります。fs_initcallがinet_initを呼び出した後、ネットワークプロトコルスタックの登録が開始されます。inet_initを介して、これらの関数はinet_protosおよびptype_baseデータ構造に登録されます。

ip_rcvやudp_rcvなどの関数のコードを見ると、多くのプロトコルの処理を確認できます。たとえば、ip_rcvはnetfilterおよびiptableフィルタリングを処理します。**多くのまたは非常に複雑なnetfilterまたはiptablesルールがある場合**、これらのルールはソフト割り込みのコンテキストで実行されるため、ネットワーク遅延が増加します。別の例では、udp_rcvは、ソケット受信キューがいっぱいかどうかを判別します。対応するカーネルパラメーターは、net.core.rmem_maxおよびnet.core.rmem_defaultです。興味があれば、inet_initのコードを読むことをお勧めします。

2.4 NICドライバーの初期化
すべてのドライバー(NICドライバーだけでなく)は、module_initを使用して初期化関数をカーネルに登録します。ドライバーがロードされると、カーネルはこの関数を呼び出します。たとえば、igbネットワークカードドライバーのコードは、drivers / net / ethernet / intel / igb /igb_main.cにあります。

ドライバーのpci_register_driver呼び出しが完了すると、Linuxカーネルは、igbネットワークカードドライバーのigb_driver_nameおよびigb_probe関数アドレスなどのドライバーの関連情報を認識します。ネットワークカードデバイスが認識されると、カーネルはそのドライバーのプローブメソッドを呼び出します(igb_driverのプローブメソッドはigb_probeです)。プローブ方式を駆動する目的は、デバイスを準備することです。igbネットワークカードの場合、そのigb_probeはdrivers / net / ethernet / intel / igb /igb_main.cの下にあります。

ネットワークカードドライバーは、ethtoolに必要なインターフェイスを実装し、関数アドレスの登録を完了するためにここにも登録されます。ethtoolがシステムコールを開始すると、カーネルは対応する操作のコールバック関数を見つけます。igbネットワークカードの場合、実装機能はすべてdrivers / net / ethernet / intel / igb /igb_ethtool.cの下にあります。今回はethtoolの動作原理を十分に理解できたと思いますよね?このコマンドがパケットの送受信のネットワークカードの統計を表示し、ネットワークカードの適応モードを変更し、RXキューの数とサイズを調整できる理由は、ethtoolコマンドが最終的にネットワークカードの対応するメソッドを呼び出すためです。 ethtool自体ではなく、ドライバーがこの超能力を持っています。

2.5ネットワークカードの起動
上記の初期化が完了すると、ネットワークカードを起動できます。ネットワークカードドライバーが初期化されたときに、ドライバーがカーネルに構造net_device_ops変数を登録したことを思い出してください。この変数には、ネットワークカードのアクティブ化、パケット送信、MACアドレス設定などのコールバック関数(関数ポインター)が含まれています。ネットワークカードが有効になっている場合(たとえば、ifconfig eth0 upを介して)、net_device_opsのigb_openメソッドが呼び出されます。

3.データ受信
3.1ハード割り込み処理
まず、データフレームがネットワークケーブルからネットワークカードに到着すると、最初に停止するのはネットワークカードの受信キューです。ネットワークカードは、割り当てられたRingBufferで使用可能なメモリ位置を探します。それを見つけた後、DMAエンジンは、ネットワークカードに関連付けられたメモリにデータをDMAします。このとき、CPUは影響を受けません。DMA操作が完了すると、ネットワークカードはCPUのようなハード割り込みを開始して、データが到着したことをCPUに通知します。

Linuxは、ハード割り込みで単純で必要なタスクのみを完了し、残りの処理のほとんどはソフト割り込みに転送されます。上記のコードからわかるように、ハード割り込み処理プロセスは非常に短いです。レジスタを記録し、CPUのpoll_listを変更してから、ソフト割り込みを発行しました。それはそれと同じくらい簡単で、ハードな中断作業は完了です。

3.2 ksoftirqdカーネルスレッドは、ソフト割り込み
NET_RX_SOFTIRQを処理し、処理関数net_rx_actionを登録します。したがって、net_rx_action関数が実行されます。

ハード割り込みでのソフト割り込みフラグの設定と、ソフト割り込みが到着するかどうかのksoftirqの判断は、すべてsmp_processor_id()に基づいています。これは、ハード割り込みがどのCPUで応答される限り、ソフト割り込みもこのCPUで処理されることを意味します。したがって、Linuxソフト割り込みのCPU消費が1つのコアに集中していることがわかった場合、アプローチは、ハード割り込みのCPUアフィニティを調整して、ハード割り込みを異なるCPUコアに分散させることです。

コア関数net_rx_actionは、そのpoll_listをトラバースしてから、ネットワークカードドライバーに登録されているポーリング関数を実行します。igbネットワークカードの場合、これはigb駆動力のigb_poll関数です。igb_pollの焦点はigb_clean_rx_irqの呼び出しです。この関数は、主にnetif_receive_skbを呼び出すnapi_skb_finish関数を呼び出し、データパケットはプロトコルスタックに送信されます。

3.3ネットワークプロトコルスタック処理
netif_receive_skb関数は、パッケージのプロトコルに基づきます。udpパッケージの場合、パッケージはip_rcv()およびudp_rcv()プロトコル処理関数に送信されて処理されます。

__netif_receive_skb_coreで、よく使っていたtcpdumpのパケットキャプチャポイントを見て、とても興奮しました。ソースコードを読む時間は本当に無駄になっていないようです。次に、__ netif_receive_skb_coreがプロトコルを取り出し、データパケットからプロトコル情報を取り出して、このプロトコルに登録されているコールバック関数のリストをトラバースします。ptype_baseは、プロトコル登録のセクションで説明したハッシュテーブルです。ip_rcv関数のアドレスは、このハッシュテーブルに格納されます。

行pt_prev-> funcは、プロトコル層によって登録された処理関数を呼び出します。ipパッケージの場合、ip_rcvと入力します(arpパッケージの場合、arp_rcvと入力します)。

3.4IPプロトコル層の処理

たとえば、プロトコル登録セクションでは、tcp_rcv()とudp_rcv()の関数アドレスがinet_protosに格納されていることがわかります。ここでは、パッケージ内のプロトコルタイプに従って配布が選択され、skbパッケージはさらに上位レベルのプロトコルであるudpおよびtcpに配布されます。

3.5UDPプロトコルレイヤー処理

プロトコル登録のセクションで、udpプロトコルの処理機能はudp_rcvであると述べました。

Linuxカーネル全体がデータパケットを受信して​​処理し、最後にデータパケットをソケットの受信キューに入れます。

4.ユーザーがソケットを読み取り、システムがrecvfromを呼び出します。

それでは、recvfromと呼ばれるユーザープロセスの後に何が起こったかを振り返ってみましょう。コードで呼び出すrecvfromはglibcライブラリ関数です。関数が実行されると、ユーザーはカーネルモードにトラップされ、Linuxによって実装されたシステムコールsys_recvfromに入ります。Linuxのsys_recvfromを理解する前に、ソケットのコアデータ構造を簡単に見てみましょう。

ソケットデータ構造のconststruct proto_opsは、プロトコルのメソッドセットに対応します。各プロトコルは、異なるメソッドのセットを実装
します。IPv4インターネットプロトコルスイートの場合、各プロトコルには対応する処理メソッドがあります。いわゆる読み取りプロセスは、sk-> sk_receive_queueにアクセスすることです。データがなく、ユーザーが待機を許可されている場合、wait_for_more_packets()が呼び出されて待機操作が実行され、ユーザープロセスがスリープ状態になります。

五数要約

ネットワークモジュールは、Linuxカーネルで最も複雑なモジュールです。単純なパケット受信プロセスには、ネットワークカードドライバー、プロトコルスタック、カーネルksoftirqdスレッドなどの多くのカーネルコンポーネント間の相互作用が含まれているようです。非常に複雑に思えますが、この記事では、カーネルパケットの受信プロセスを図でわかりやすく説明したいと思います。それでは、収集プロセス全体をつなぎ合わせましょう。

ユーザーがrecvfrom呼び出しを実行した後、ユーザープロセスはシステムコールを介してカーネルモードの作業に進みます。受信キューにデータがない場合、プロセスはスリープ状態になり、オペレーティングシステムによって一時停止されます。これは比較的単純で、残りのシーンのほとんどはLinuxカーネルの他のモジュールによって実行されます。

まず、パッケージの収集を開始する前に、Linuxは多くの準備を行う必要があります。

1. 创建ksoftirqd线程,为它设置好它自己的线程函数,后面指望着它来处理软中断呢

2. 协议栈注册,linux要实现许多协议,比如arp,icmp,ip,udp,tcp,每一个协议都会将自己的处理函数注册一下,方便包来了迅速找到对应的处理函数

3. 网卡驱动初始化,每个驱动都有一个初始化函数,内核会让驱动也初始化一下。在这个初始化过程中,把自己的DMA准备好,把NAPI的poll函数地址告诉内核

4. 启动网卡,分配RX,TX队列,注册中断对应的处理函数

上記は、カーネルがパケットを受信する準備をする前の重要な作業です。上記の準備ができたら、ハード割り込みをオンにして、データパケットの到着を待つことができます。

データが到着すると、最初にそれを迎えるのはネットワークカードです。

1. 网卡将数据帧DMA到内存的RingBuffer中,然后向CPU发起中断通知

2. CPU响应中断请求,调用网卡启动时注册的中断处理函数

3. 中断处理函数几乎没干啥,就发起了软中断请求

4. 内核线程ksoftirqd线程发现有软中断请求到来,先关闭硬中断

5. ksoftirqd线程开始调用驱动的poll函数收包

6. poll函数将收到的包送到协议栈注册的ip_rcv函数中

7. ip_rcv函数再讲包送到udp_rcv函数中(对于tcp包就送到tcp_rcv)

パケット受信プロセス全体を理解すると、Linuxがパケットを受信する際のCPUオーバーヘッドを明確に知ることができます。まず、最初のブロックは、システムコールをカーネルモードに呼び出すユーザープロセスのオーバーヘッドです。2番目のブロックは、パケットのハード割り込みに応答するCPUのCPUオーバーヘッドです。3番目のブロックは、ksoftirqdカーネルスレッドのソフト割り込みコンテキストによって使用されます。

おすすめ

転載: blog.csdn.net/weixin_38387929/article/details/113812682