この事象、端末に故障があったのは問題ですが、それでアプリケーションが止まってしまうようでは実装上問題があります。では、どうすればいいのでしょうか?
当面乗り切ることさえできればいいというのであれば、簡単な方法があります。
今回、recvfrom()がパケット受信処理で待ち受けてしまうのは、ブロッキングモードで実装されていたからです。これをノンブロッキングモードで実装すれば、recvfrom()は、受信したパケットが不正な場合はすぐに処理に復帰し、待ち受けは行いません。
そこで今回は暫定措置として、実装をノンブロッキングモードに変更することで対処を行いました。その結果、システムが停止するという事象も何とか発生しなくなり、無事納品前チェックも通過、納品までこぎ着けることができました。
本当はselect()ではなくepoll()を使う方が高速ですし、監視できるファイルディスクリプタも増えてよいのですが、修正にかかるコストを考えると妥当な線でしょう。
さて、事件自体は一応の解決を見ましたが、1つ釈然としないことがあります。すなわち、recvfrom()で初めてパケットのチェックサムを検査するあの仕様は正しいのか、ということです。
普通に考えれば、なるべく早い段階、できればOSがパケットを受信した段階でエラーを検出し、不正なパケットがあればさっさと破棄してしまう挙動が正しいでしょう。実は、この方法については以前にも同様の議論がなされています(注5)。しかし、あまり早い段階でチェックサム計算を行うと全体のパフォーマンスに影響するため、変更は却下されています。
とはいえ、recvfrom()で停止してしまうこの実装にも問題があります。そこで妥協点として、select()の延長上でチェックサムを計算させるルーチンが実装されました(注6)。ただし、これはカーネル 2.6.10でマージされたので、カーネル 2.6.9ベースであるRHEL4には取り込まれていません。そのため、現状ではRHEL5にアップデートするしか対処法がないことになります。
今回、顧客には暫定対処で何とかしてもらいましたが、このままでは第2、第3の犠牲者が出てしまいます。それに今回の顧客にしたって、大本の問題が直った方がうれしいはずです。
こんなときは、今回利用したOSのディストリビューターであるRed Hatに対し、改善を提案するのがよい手です。われわれはこの問題に関して改善要望を出しており、その結果、RHEL4の次のアップデートであるRHEL 4.7には対処が取り込まれそうです(注7)。
注7:https://bugzilla.redhat.com/show_bug.cgi?id=212321。報告者のMasaki MAENOさんは、うちの団員です。このようにバグ報告によって品質が改善されていくのもOSSの面白い点ですね
今回は、実際に発生したUDPでのselect()+recvfrom()使用時の問題で、以下のような手順で問題を解決する手順を説明しました。
トラブル解析においてはほとんどの場合で、上位レイヤであるユーザーアプリケーションから低位レイヤであるハードウェアへと、解析対象を順に降ろしていく方法がお勧めです。低位レイヤから解析していく方法もあるのですが、低位レイヤから行う解析では、すべての事象が見える半面、解析対象があまりにあり過ぎて、切り分けや解析が難しくなりがちです。
ここで紹介したケースでも、原因はクライアントのハードウェアエラーから発生しましたが、そこから今回の解析結果までたどり着くのは難しい部分もあると思います。何かアプリケーションの挙動がおかしい場合は、第一歩として、アプリケーションの近くのレイヤから調査するのがよいのではないでしょうか。
Copyright © ITmedia, Inc. All Rights Reserved.