Linux Kernel(2.6)の実装に関するメモ書き

sk_buff


Rev.11を表示中。最新版はこちら

1. 概要

sk_buffはパケットデータを格納するバッファ。各パケットは、それぞれsk_buffに格納されLinuxのネットワークレイヤで扱われる。

2. 基本的なデータ構造

sk_buffの構造を図2.1に示す。まずバッファの管理構造体としてstruct sk_buffがある。そして、実際にパケットデータを格納する領域が別にあり、sk_buffのhead,data,tail,endでデータの位置を管理する。また、データ格納エリアの後ろにはstruct skb_shared_infoが付いており、ここにはcloneで複製されたsk_buff間で共有するデータが格納される。


図2.1 sk_buffの構造(空のケース)

sk_buffにデータが格納されていると図2.2のようになる。sk_buffの各フィールドの意味は表2.1参照。


図2.2 sk_buffの構造(データがあるケース)


表2.1 sk_buffのデータ位置を管理するフィールド 
フィールド
説明
head
データ格納用バッファの先頭を示す
data
バッファに格納されているデータの先頭
tail
データの終端
end
データ格納用バッファの終端
このアドレスは、skb_shared_infoの開始アドレスでもある。

3. その他のデータ構造

3.1 frags配列

skb_shared_info内のfrags配列がある。この配列を使用することで、パケットデータを非連続な領域に分断して保持することができる。fragsを使用した状態のsk_buffを図3.1に示す。

ただし、このようにfragsパケットデータを分断して持つ形式はネットワークカード(とそのドライバ)がScatter/gather I/O(*1)に対応していないと使えない。Scatter/gather I/Oに対応しているとデバイスにNETIF_F_SGフラグが設定されているので、送信デバイスにこのフラグが立っている場合のみ、このような形式を使う。


図3.1 frags配列

[図3.1 補足]

frags配列の要素はskb_frag_structとなっており(ポインタではなくそのもの)、これは、分断されたデータがどこにあるかを管理する。 skb_frag_structはデータが置かれている物理ページのstruct pageを指している。そして、page_offset,sizeが物理ページ内のデータ格納位置を示している。

(*1) 物理的に非連続になっている複数のバッファのデータから1つのパケットを構築(Gather)して送信したり、逆に受信パケットを分断された複数のバッファに格納(Scatter)できる ネットワークカード。この機能により、ジャンボフレームを扱う場合に、パケットサイズ分の物理的に連続なページを確保する必要がなくなる。

Scatter/Gather I/Oに対応しているドライバでは、送信時にfrags[]をチェックして、分断されたバッファのデータをまとめて送信するようにH/Wを制御する。

3.2 frag_list

skb_shared_info内のfrag_listにはIPフラグメントされたパケットのsk_buffがチェーンされる(図3.2)。図3.2ではデータ(A)がフラグメントされた先頭パケットのデータ、データ(C)が2番目のパケットのデータとなる。送信イメージは図3.3。


図3.2 frag_list 



図3.3 フラグメントされたパケットの送信

frag_listの構築はIPv4では、ip_append_data()とip_push_pending_frames()で行われる。RAW,UDPソケットでデータ送信する時、ソケットは送信データをsk_buffに格納するためにip_append_data()を呼び出す。ip_append_data()はデータをsk_buffにデータを格納して行く際、パケット長がMTUを越えるようなら新しいsk_buff作成して、ソケットの送信キュー(sock->sk_write_queue)にチェーンしていく。sk_buffの構築が終わったら、ip_push_pending_frames()で送信キュー内のパケットをfrag_listにチェーンしてIP層の送信ルーチンに渡している。

3.3 lenとdata_len

sk_buffにはデータ長を表すフィールドとしてlenとdata_lenを持つ。

lenはsk_buff内の全データの長さを示す。図3.1、3.2でいうとデータ(A)、frags[]に保持されるデータ(B)、frag_listにチェーンされているsk_buffのデータ(C)全てを足したものになる。

data_lenはデータ(B)のサイズ。frag_listにパケットがチェーンしている場合はさらにデータ(C)のサイズを足した部分のサイズになる。つまり、lenからデータ(A)のサイズを除いた分となる。skb_headlen()関数では"len - data_len"でデータ(A)の部分のサイズを求めている。

4. sk_buffの複製(Clone)

4.1 基本的なClone

ネットワーク層で複数のソケットでデータを渡す場合や、再送のためにデータを保持しておく場合は、sk_buffを複製する。これはskb_clone()で行う。skb_clone()は図4.1に示すように引数で指定されたsk_buffを複製する。複製されるのはsk_buffのみで、データ自体は複製されない。

sk_buffを複製すると、オリジナルと複製されたsk_buffのそれぞれのclonedを1にする。また、skb_shared_info内にある参照カウンタdatarefをインクリメントする。


図4.1 sk_buffのclone

[clonedフラグ]

clonedは一度セットすると基本的にクリアされないので、複製したsk_buffが解放されても、clonedは立ったままとなる。このため、 clonedが立っていたとしても、Clone(データを共有しているsk_buff)が"存在するかもしれない"ということしかわからず、現在も存在し ているとはかぎらない。Cloneが存在するかどうかは、clonedが立っていたらさらにdatarefを参照する必要がある。値が1より大きければ、Cloneが存在することになる。1であれば自分しか参照していないのでCloneはもう存在していないことになる。Cloneが存在するかどうかはskb_cloned()マクロで調べられるが、本マクロの実装はこのようにclonedとdatarefの2段階で判定を行っている。

そもそも、datarefだけ見てしまえばCloneがいるかどうかは判定できるのだが、clonedフラグのようなものが存在しているのは、dataref参照時に必要な排他制御のコストを省くため。Cloneしたことがないsk_buffならcloned=0なので、排他制御することなく速やかにCloneがいないことを判定できる。

4.2 高速なClone

編集中

最終更新 2007/05/03 15:12:51 - kztomita
(2007/04/19 18:48:38 作成)
添付ファイル
skbuf_layout.png - kztomita
skbuf_layout2.png - kztomita
frags_array.png - kztomita
frag_list.png - kztomita
ip_fragment.png - kztomita
skbuf_clone.png - kztomita
skbuf_fclone.png - kztomita
skbuf_fclone2.png - kztomita


リンク
最近更新したページ
検索