2006年04月

このエントリーを含むブックマーク 2006年04月30日

「勤労の義務」は、 言うまでもなく日本国憲法第27条に規定されている、 日本国民の3大義務の一つであるが、 そのそろこの勤労に対する固定観念をかえていくべきなのではないか? 来るべき共産社会へのビジョンは、 Web2.0 で初めて見えてきたわけでは決してなく、 何十年も前から様々な形で繰り返し描かれてきた。 例えば、

断絶への航海 ハヤカワ文庫 SF (586)
ジェイムズ・P・ホーガン (著), 小隅 黎

などの近未来小説などの形でも。 SF においては、StarTrek を引き合いに出すまでも無く、 未来には貨幣経済が存在しないという設定のほうが、 むしろ普通となっている。

にもかかわらず、いまだに資本主義以前の ナイーブな勤労観がはびこっているのは、 憲法に基づく洗脳教育のたまものなのだろうか?

人力検索はてな から引用:

「年収300万の自分にとって最高に面白い仕事」か「年収1000万の自分にとって最高に面白くない仕事」どちらかを一生しなくてはならないとしたらあなたはどちらを選びますか?その理由は?

この質問者にとっては、面白くても、面白くなくても、 仕事は仕事なのだろう。 そこにはストックという概念すら存在せず、 ただフローを得るための勤労という思い込みに囚われている。

まずバランスシートを義務教育で教えるべきなのだろうか?
それとも、貨幣経済を飛び越して Web2.0 を教えるべきなのだろうか?



hiroaki_sengoku at 11:19|この記事のURLComments(0)TrackBack(0)技術者の成長 
このエントリーを含むブックマーク 2006年04月29日

epoll版 stone のバグを長いこと放置していた... orz
(1) getident
(2) SSL_connect に時間がかかる場合の挙動

(1) のバグは、sd = socket() するまえに epoll_ctl(epfd, EPOLL_CTL_ADD, sd, &ev) していた、という単純バグ。なぜ epoll_ctl が失敗しなかったというと sd を初期化していなかったので、たまたま有効なディスクリプタだったのだろう。これは順序を入れ替えるだけで fix.

(2) は、少し複雑。stone は中継先に connect する際、どのタイミングで接続が確立するかで処理のパスが変わる。たとえば connect が EINPROGRESS を返すと epoll/select 待ちするが、connect を呼び出した時点で接続が確立すれば、直接 readWrite ループへ遷移する。

SSL がからむともっと複雑になる。SSL_accept が完了するまで中継先が決まらない場合もあるし、TCP接続が確立しても SSL接続がなかなか確立しない場合もある。SSLハンドシェークに時間がかかる場合、SSL_accept / SSL_connect は readWrite ループで呼び出されることになる。

SSL_connect が完了する前に中継元からの入出力が行われてはまずいので、readWrite ループ内の epoll/select 待ちでは中継元側のディスクリプタは監視対象からはずしているわけだが、SSL_connect が成功した時点で監視対象に含めなければならない。select 版では正常に動作するのだが、epoll 版では監視対象に含め損なっていた。

     ret = SSL_connect(ssl);
     if (ret > 0) {     /* success */
+       Pair *p = pair->pair;
        /* pair & dst is connected */
        pair->proto |= (proto_connect | proto_dirty);
+       if (p) p->proto |= proto_dirty; /* src */

接続元側 Pair の proto に、proto_dirty フラグを設定することにより、epoll でも監視対象に含めるようになる。 これで epoll 版も正常に動くようになったはず。
現在 senri で動作検証中...

$Id: stone.c,v 2.2.2.20 2006/04/28 21:50:24 hiroaki_sengoku Exp $



hiroaki_sengoku at 07:44|この記事のURLComments(0)TrackBack(0)stone 開発日記 
このエントリーを含むブックマーク 2006年04月27日

先日紹介した学生さんを対象とした会社説明会

KLab(株) CTO 仙石 (KLabセキュリティ(株) CTO を兼務) と語る、
「技術者の成長にとって一番役に立つ会社」
「技術者が自ら伸びていくことができる会社」とは?

日時: 5/9(火) 13:30〜15:00
場所: KLab(株) 本社会議室 (六本木ヒルズ森タワー 20F)

が、おかげさまで満席につき二回目が実施されるそうです。

日時: 5/15(月) 13:30〜15:00
場所: KLab(株) 本社会議室 (六本木ヒルズ森タワー 20F)

会社説明会といいつつ、要は私といろんな話題についてお話しましょう、
というノリですので、私の日記 (CTO日記もよろしく) を見て、
波長が合いそうだと思ったかた、ぜひ登録をお願いします。

5/8 追記: 上記説明会は満席につき、現在は 5/30実施の説明会への登録を受付中です



hiroaki_sengoku at 15:40|この記事のURLComments(0)TrackBack(0)技術者の成長 
このエントリーを含むブックマーク 2006年04月26日

CTO日記に書いた、 「実力主義・能力主義」が (思いの外 ;) 多くの方々に読んで頂けているようだ。 私がこだわっていることから 3つ紹介しているのであるが、 ほとんどのかたが、「技術者の上司は技術者であるべきです」に 注目しているのが興味深い。 勤務時間の10%以内であれば上司の許可を得ずに何をやってもいいという「どぶろく制度」 よりも上司が技術者であることのほうが関心が高い、というのは 現在の上司が理解がないと感じている人が多い、 ということを反映しているのだろう。

上司に自分の能力を正しく評価して欲しい、と 思うのは技術者として自然な感覚だとは思うのだが、 注意すべき点が二つほどあるように思う。

一つめは、視野狭窄に陥らないという点。
もう一つは、 現実問題としては、上司が技術者でない人が存在する、という点。

まず一つめの点であるが、 「上司に自分の能力を正しく評価して欲しい」と思うとき、 上司と自分との関係しか見ておらず、 しかもそれは自分からの視点のみであって、 相手がどう思っているかという視点が欠けているのではないか? 会社という運命共同体の中で自分がどのような位置にあり、 自分としてはどのような役割を果たすのか明確になっているだろうか? また、 上司はどのような考えで自分を評価しているのか、 その評価にはどのくらい妥当性があるのだろうか? そういう視点からみたとき、 見え方が変わってこないか自省すべきだろう。

もう一つの点、上司が技術者でない人の存在について考えてみる。 まっさきに CTO が思い浮かぶ。 確かに CTO の上司は社長であり、多くの会社で社長は技術者ではない。 しかし、よほど小さい会社でもない限り、 CTO 以外にも上司が技術者でない人は沢山いる。 そのような人達は、技術者以外の視点も持って、 上司や職位が同じレベルの他部門の人達と調整を行なわなければならない。 そういう調整能力と、部下を正しく評価し育成できる能力と、 両方兼ね備えた人材が豊富にいるのであれば問題無いが、 一般的にはかなり困難だろう。

だから、そういうポジションに技術者を登用する場合に、 調整能力を重視するのか、部下の育成能力を重視するのか、 考えなければならない。 この育成能力というのは、技術が分かっていることとはまた 別の次元だったりするので、さらに話は難しい。



hiroaki_sengoku at 19:39|この記事のURLComments(2)TrackBack(0)技術と経営 
このエントリーを含むブックマーク 2006年04月25日

一月の事件以来、すっかり影をひそめた 「勝ち組の塔」というキーワード。
六本木を遠くから眺めると、二本の塔が聳え立っている。

一方を「勝ち組の塔」
他方を「負け組の塔」

と呼んでみるのはどうだろうか。



hiroaki_sengoku at 11:59|この記事のURLComments(0)TrackBack(0)その他 
このエントリーを含むブックマーク 2006年04月24日

二日前にトラックバックを打った縁で、 やねうらお様とお会いしました。
ご足労頂きありがとうございました。> やねうらお様

同席した弊社社員は、

唐突な来社だったので、 本にサインしてもらう準備できてませんでした...orz

と嘆いております。(^^;)

この GCD日記は始めてからまだ一ヶ月ほどしかたっていませんが、 ブログの縁というのもあるんですね〜。



hiroaki_sengoku at 19:56|この記事のURLComments(0)TrackBack(0)その他 
このエントリーを含むブックマーク 2006年04月23日

他人の考え方を変えさせるのは難しい。 人は誰しも自分の考えたいように考えるわけで、 正論を述べたって相手を説得できるとは限らない。 たった一人の考え方を変えさせるのさえ困難なのだから、 まして組織の方向性を変えるなんて至難の業。

だから、組織を変えようとするのではなく、 他人を変えようとするのではなく、 まず自分を変えよう。 どんなに非の打ち所が無い人でも 反省すべき点の一つや二つあるわけで、 ふつうの人ならいくらでも反省すべき点はあるだろう。

まして周囲に不満を持っている人なら その分、反省すべき点も多いはず。 まさに人のふり見て我がふり直せ。 自分の都合のよいように、自分の考えたいように、 考えていないか省みよう。

他人の考え方を変えさせるのはそれからでも遅くない。 そもそも考え方を変えて欲しいと思うのは何のため?



hiroaki_sengoku at 20:30|この記事のURLComments(0)TrackBack(0)自己啓発 
このエントリーを含むブックマーク 2006年04月22日

開発環境の進化が、 プログラマのプログラミング能力を退化させていると思う。 私は、いわゆる統合開発環境というものを使ったことがなく、 いつでも emacs を愛用している。 しかも画面サイズは 20年来 80桁x24行のままである。

プログラミングは、メモを書きながら設計したあと、一気に書く。 設計さえきちんとできていれば、途中で手が止まることはあまりない。 食事も忘れて何時間も没頭することがよくある。 そして書き終えてたらコンパイル。 タイプミスとか変数宣言し忘れとかで出たコンパイルエラーを ひとつずつ修正。

で、コンパイルに成功したら実行させる。多くの場合、これで動く。 一通り動作確認して、期待しない動作をするところがあっても、 ほとんどの場合ソースを参照するまでもなく原因に思い当たる。 たいていの場合、デバッガを使うまでもなく、 ソースを見直すだけでどう修正すべきかも分かる。

という話をすると、奇異な目で見られてしまう。(^^;)
目視だけでデバッグと題するページで、 私と同じような感覚の人を見つけて安心した。

たいていの人は、デバッガでステップ実行させて、 実行中の変数を参照したり、 値を変えてみたりしてプログラムを修正するのだという。 たしかに頭の中でプログラムの動作を追うより、 デバッガを使って実際に動かしてみるほうが楽かもしれないが、 それでプログラミングスキルが伸びるのだろうか?

まるで、将棋を指すとき対戦用の盤面とは別に、 相手の指し手の可能性を検討する盤面を脇に置いて、 次の一手を検討しているようなものではないか。 そんなことをしていたら、 次の一手を考えるのに膨大な時間がかかってしまうし、 頭が鍛えられないので上達も難しいだろう。

プログラミングも同じ事。 頭の中に仮想的にデバッガを構築して、 無意識の思考でプログラムを実行させることができなければ、 いつになってもプログラムを見通す洞察力は身につかないだろう。



このエントリーを含むブックマーク 2006年04月21日

パケットの気持ちになって考える

むろん、パケット(といっても小包のことではなくて、IP パケットとかのことである) に 気持ちがあるわけではないが、 パケットからみたネットワークを無意識の思考で考えてみる、 という意味。

パケットの振る舞いを、意識した思考で考えていては、 あらゆる状況下での振る舞いを考えようとすると時間がかかりすぎる。 頭の中にネットワークのエミュレータを構築し、 無意識の思考でパケットをやりとりしてみればどうだろうか。

プログラムの気持ちになって考える

プログラムの視点、 ないしプログラムを実行する CPU の視点を、 無意識の思考で考えてみる。 プログラムを一ステップずつ意識した思考で追っていては、 いつになってもバグの原因に思い当たらないが、 頭の中にプログラムを思い描き、 無意識の思考で実行してみればバグの原因が「ひらめく」かもしれない。

将棋の駒の気持ちになって考える

寺尾創さまのコメントにある、 「直感で何通りか次々に解候補が無意識に浮かんでくる」というのは、 頭の中に将棋盤と駒があって、 無意識の思考で超高速に駒を動かしてあらゆる指し手を探索している、 ということなのか。 これが意識した思考だと、 あらゆる指し手を探索するには非現実的な時間がかかってしまうだろう。



hiroaki_sengoku at 18:37|この記事のURLComments(0)TrackBack(1)自己啓発 
このエントリーを含むブックマーク 2006年04月20日

相手の気持ちになって考える

相手のバックグランドを知る。 どういう生い立ちか、どういう環境で育ったか、何を学んだか。 バックグラウンドが分かれば、 物事をどうとらえ、どう考えるかが見えてくる。 あたかも自分の頭の中に、 相手の人格のエミュレータが動いているかのように。

意識した思考で、 相手の考えを一ステップづつシミュレートしていては遅すぎる。 あくまで相手の気持ちになりきることが重要。 無意識の思考で、相手の人格のエミュレーションができるようになれば、 無意識における相手の思考と、 意識した自分の思考をインタラクションさせることができるようになる。

無意識における相手の思考と、 現実の相手の思考との差をフィードバックさせて、 エミュレーションの精度を高めることができる。



hiroaki_sengoku at 15:20|この記事のURLComments(0)TrackBack(1)自己啓発 
このエントリーを含むブックマーク 2006年04月19日

GCD LAN内で Real Player コンテンツを再生しようとしたら、 GCDゲートウェイで、外向きポート554番をブロックしていたので、 とりあえず特定の接続先だけ解除する。

これでコンテンツを見られると、思って再生ボタンを押すと、 いきなりゲートウェイがネットワークから見えなくなった。 NICごと死んだのか、IP, IPv6 ともに反応なし。 二つあるNICの両方が無反応なので、 原因はドライバより上のレベルにありそう。 あいにくゲートウェイにはディスプレイカードを入れていないので、 シリアル(RS-232C)経由でログインしてみると、 iptables のログが延々と出力されている。 大半が、root ネームサーバのポート53番から。 つまり DNS 問い合わせに対する応答パケット。 ということは、PPPoE が生きている。これはマズイ。

機能しなくなったゲートウェイが PPPoE をつかんだままでは、 MASTER に昇格したもう片方のゲートウェイが PPPoE できない。 正確に言えば、PPPoE 自体はできるのだがプロバイダがどちらの ゲートウェイへパケットを送ってくれるかは運次第。 事実、ほとんどのパケットは機能しなくなったゲートウェイ側に 届いたので、GCD からインターネットへの通信が事実上不可能になった。

冗長構成を組むとき最も難しいのが、いかにして中途半端な死に方を 回避するかだが、死んだゲートウェイに PPPoE を止めさせるのも、 なかなか難しそうだ。

  1. PPPoE している機能不全状態のゲートウェイには頼らず、
  2. PPPoE 先のプロバイダ側には手が出せない、

と仮定すると、どうすればいいだろうか。 機能不全状態のゲートウェイのNIC を LAN から切り離す仕掛けを 組み込むとかだろうか? (どうやって? ^^;)



このエントリーを含むブックマーク 2006年04月18日

誰しも言うことで、なにもここで書かなくたって 誰しも百も承知だと思うが、 あまりに重要すぎるのであえて書く。

病は気から、ネガティブに考えれば必ず悪くなる。 悪くなる可能性のあることは必ず悪くなる。 ならば無理やりにでもポジティブに考えよう。 どうしてもポジティブに考えられなかったら、 まずは冷静に考えられるようになるまで考えるのをやめてしまえ。 家に帰って寝てしまえ。

冷静になって考えれば、どんな苦境に立った時だって、 大して不幸なわけじゃない。 どんなに困難な問題にぶちあたったって、 解決策が見つからなくたって、 解決策を見つけようとすれば経験値が上がる。 ポジティブに考えれば、必ずうまくいく。 失敗は成功の母、必ず次につながる。

むしろうまくいっているときほど慎重であれ。 成功は失敗の母、成功体験にとらわれれば、 いつか必ず失敗する。 勝って兜の緒を締めよ、落とし穴はいたるところにある。



hiroaki_sengoku at 19:02|この記事のURLComments(0)TrackBack(0)自己啓発 
このエントリーを含むブックマーク 2006年04月17日

大学4年/大学院2年の学生さんに、 どれだけこのページを読んでもらえてるか分からない (^^;) のですが、

KLab(株) CTO 仙石 (KLabセキュリティ(株) CTO を兼務) と語る、
「技術者の成長にとって一番役に立つ会社」
「技術者が自ら伸びていくことができる会社」とは?

と題する会社説明会が開催されるようです。

日時: 5/9(火) 13:30〜15:00
場所: KLab(株) 本社会議室 (六本木ヒルズ森タワー 20F)

会社説明会といいつつ、 要は私といろんな話題についてお話しましょう、 というノリですので、 私の日記 (CTO日記もよろしく) を見て、 波長が合いそうだと思ったかた、 ぜひ登録をお願いします。

4/27追記: 上記説明会は満席につき、現在は 5/15実施の説明会への登録を受付中です
5/8 追記: 上記説明会は満席につき、現在は 5/30実施の説明会への登録を受付中です



hiroaki_sengoku at 17:11|この記事のURLComments(0)TrackBack(0)技術者の成長 
このエントリーを含むブックマーク 2006年04月16日

昨日、getnameinfo の問題について述べたが、 念のため、glibc のソースを確認してみた。 手元に展開してあった glibc-2.3.5 を見ると、 glibc-2.3.5/inet/getnameinfo.c の 440行目で、

      case AF_LOCAL:
        strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
        break;

とある。sun_path が 0 終端されていないと、 sun_path が実際に何バイト確保されているかとは関係なく、 servlen バイトがコピーされてしまう。

getnameinfo の salen 引数のチェックは、 同じく getnameinfo.c 182行目の、

    case AF_LOCAL:
      if (addrlen < (socklen_t) (((struct sockaddr_un *) NULL)->sun_path))
        return EAI_FAMILY;
      break;

だけであった。 つまり sun_path の部分が 0 バイトでもエラーにならない。

今回の問題とは関係ないが、

(socklen_t) (((struct sockaddr_un *) NULL)->sun_path))

という書き方は参考になる。 stone.c では

(((struct sockaddr_un*)sa)->sun_path - (char*)sa)

と書いていたのだが、 NULL を使えば簡潔に書ける、 というのは目から鱗だった。



hiroaki_sengoku at 08:17|この記事のURLComments(0)TrackBack(0)stone 開発日記 
このエントリーを含むブックマーク 2006年04月15日

UNIX で TCP接続を accept したとき、そのソケット sd に対して

struct sockaddr_storage ss;
struct sockaddr *from = (struct sockaddr*)&ss;
socklen_t fromlen = sizeof(ss);
getpeername(sd, from, &fromlen);

などとすれば、接続元のアドレスが from に返る。 UNIXドメインソケットを listen し accept した場合は、

from->sa_family ← AF_UNIX
fromlen ← 2

という値が返るようだ。

ところが、
この返り値を getnameinfo に与えると、 fromlen で指定したサイズを超えて UNIX ドメインソケットのファイル名を読もうとする 問題があるようだ。

たとえば次のようなテストプログラムを書いてみる:

#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/un.h>
#define STRMAX  256
main() {
    struct sockaddr *sa;
    socklen_t salen;
    struct sockaddr_un sun;
    char name[STRMAX+1];
    char serv[STRMAX+1];
    int err;
    bzero(name, STRMAX+1);
    bzero(serv, STRMAX+1);
    sa = (struct sockaddr*)&sun;
    salen = sizeof(sun);
    sun.sun_family = AF_UNIX;
    strcpy(sun.sun_path, "/tmp/sock");
    salen = sizeof(sa_family_t);
    err = getnameinfo(sa, salen, name, STRMAX, serv, STRMAX, 0);
    printf("salen: %d, err: %d, name: '%s', serv: '%s'\n",
           salen, err, name, serv);
}

salen は 2 であるので、 sun.sun_path の部分の値は無視すべきであるのに、 serv には、"/tmp/sock" の値が返ってしまう。 sun.sun_path の部分が初期化されていなければ、 不定値が返るだろう。

実際、stone では struct sockaddr_storage を初期化せずに getpeername を呼び出して長さ 2 の struct sockaddr_un を受け取り、 これを getnameinfo の引数として与えると、 dserv に不定値が返ってきてしまった。

本来ならば、getnameinfo は長さ 0 のパス名を返すか、 あるいは UNIX ドメインソケットは扱わない、 ないしは長さが異常ということで EAI_FAMILY を返すか、 どちらかであるべきではなかろうか。 少なくとも glibc-2.3.5 と、glibc-2.2.5 では、 この問題があることを確認した。

とりあえず、stone は AF_UNIX のときは getnameinfo を呼ばずに sun_path を dserv へコピーするように修正した。

$Id: stone.c,v 2.2.2.19 2006/04/14 23:35:18 hiroaki_sengoku Exp $



hiroaki_sengoku at 09:58|この記事のURLComments(0)TrackBack(0)stone 開発日記 
このエントリーを含むブックマーク 2006年04月14日

UNIX ドメインソケットならば、カーネルはどのユーザが そのソケットに connect したか知っているはずで、 きっと getsockopt(2) でユーザID が取得できるに違いない、 と思ってカーネルソースを見ていたら SO_PEERCRED を発見。

ついでに SO_PEERCRED をググってみたら、 PostgreSQL や w3m では、 既に SO_PEERCRED を使った認証をサポートしていることが判明。 負けじと stone にも実装してみる。

$Id: stone.c,v 2.2.2.18 2006/04/13 09:02:15 hiroaki_sengoku Exp $

README から引用:

	stone [-C <file>] [-P <command>] [-Q <options>] [-N] [-d] [-p] [-n]
	      [-u <max>] [-f <n>] [-l] [-L <file>] [-a <file>] [-i <file>]
	      [-X <n>] [-T <n>] [-r]
	      [-x <port>[,<port>][-<port>]... <xhost>... --]
	      [-s <send> <expect>... --]
	      [-b [<var>=<val>]... <n> <master>:<port> <backup>:<port>]
	      [-B <host>:<port> <host1>:<port1>... --]
	      [-I <host>]
	      [-o <n>] [-g <n>] [-t <dir>] [-D] [-c <dir>]
	      [-q <SSL>] [-z <SSL>]
	      [-M install <name>] [-M remove <name>]
	      <st> [-- <st>]...

	...

	<st> は次のいずれかです。<st> は「--」で区切ることにより、複数個
	指定できます。

	...

	(4)	<host>:<port>/http <request> [<xhost>...]
	(5)	<host>:<port>/proxy <header> [<xhost>...]

	(4) は、http リクエストにのせて中継します。<request> は HTTP 1.0 
	で規定されるリクエストです。リクエスト文字列中、「\」はエスケー
	プ文字であり、次のような置き換えが行なわれます。

		\n	改行 (0x0A)
		\r	復帰 (0x0D)
		\t	タブ (0x09)
		\\	\    (0x5C)
		\a	接続元の IP アドレス
		\A	「接続元の IP アドレス」:「ポート番号」
		\d	接続先の IP アドレス
		\D	「接続先の IP アドレス」:「ポート番号」(透過プロキシ用)
		\u	接続元のユーザID (番号)
		\U	接続元のユーザ名
		\g	接続元のグループID (番号)
		\G	接続元のグループ名
			\u \U \g \G は UNIX ドメインソケットの場合のみ
		\0	SSL 証明書のシリアル番号
		\1 - \9	SSL オプションの re<n>= で指定した正規表現中、
			( ... ) 内の正規表現にマッチした文字列
		\?1<then>\:<else>\/
			もし \1 (\2 - \9 も同様) の文字列が、空文字列で
			なければ <then>、空文字列であれば <else>

	(5) は、http リクエストヘッダの先頭に <header> を追加して中継し
	ます。(4) と同様のエスケープを使うことができます。


hiroaki_sengoku at 06:58|この記事のURLComments(0)TrackBack(1)stone 開発日記 
このエントリーを含むブックマーク 2006年04月13日

CTO日記のほうで 「VPN-Warp入門編」を始めた。 VPN-Warpというのは、port forwarder で、「relay agent」プログラムを動かしているマシンへ、「リレーサーバ」経由で TCP トンネルを掘ることができる。 つまり、イントラの中だろうが固定IPがついてない NAT の中だろうが、ダイアルアップするノートPCだろうが、インターネットへアクセスすることさえできるならば、relay agent を動かすだけで外部からアクセスできるトンネルが設置可能。

ssh の -R オプションと、-L オプションを組み合わせて同様のことが実現可能であるが、大きな違いは、ssh とちがってアカウントを作る必要がないこと。SSLクライアント証明書を発行するだけなので、異なるセキュリティポリシーのトンネルを何本でも手軽に設置できる。実際、KLab のインフラの各所で活用している。



このエントリーを含むブックマーク 2006年04月12日

ネットが切れる不安という日記を見つけた。

引っ越し先には光ファイバーが入っていないのだ(汗) 明日、ADSLが大丈夫かどうか確認して、なるべく間隔があかないように手続きをしないと。 今の時代、一日でもネットに接続できないと、なんだか不安になってしまう。

私も 6年前に引越したのだが、 ネットが切れる不安におそわれた。 なんとか間隔があかないように苦労したのを覚えている。 顛末は日経Linuxの連載の枕 (PDF版) に書いた。

引っ越し作業中も社宅でサーバーを動かし続ける必要があるわけですが,部屋中に電話線やらEthernetケーブルが張り巡らせてある状態で引っ越し作業ができるはずはないので,サーバー,ルーター,TA(Terminal Adapter),電話等のネットワーク関係を部屋の片隅の半畳程度の部分に再構築し,作業中絶対にこの部分には近付かないよう*3 引っ越し業者の方にお願いすることにしました

当時はまだ ADSL のサービスが始まっていなかったので、OCN エコノミーを利用していた。だから TA。



このエントリーを含むブックマーク 2006年04月11日

同時に考えよう」で無意識の思考について私自身の経験について述べたのであるが、驚いたことにそういう仮説が実際にあって、実験も行われているらしい。

Don't think too much, It's allright」(医学都市伝説)で紹介されている論文によると、

多くの判断要素のある事柄の選択については、無意識的思考が勝るという作業仮説から研究を行った。仮説は「集中なき熟考」と名付けられ、それを元に消費選択についての四つの実験が行われた。これらは実験室だけでなく、街角の商店でも行われ、複雑な判断要素のある商品購入の際には、意識を集中させた熟考によって、かえって悪い結果を得られることが確かめられた」。

だそうだ。この仮説が「都市伝説」なのかどうかよくわからない (^^;) が、

著者たちはこれらの結果から、どこに住み、どんな仕事をするかというような、多くの判断基準が錯綜する事柄について選択をする場合、それらの条件をいったん胸に納め、それに集中することなく「無意識下」で暖めておくことが、もっともよい決断結果が得る道だと主張している。

という主張は、私の感覚ととてもよくマッチする。判断基準から選択を行う思考の場合だけでなく、初めて見る数学の問題を解くとか、さらには全く新しいアイディアを思いつく、などといったような創造的な思考をも無意識下に行える、と私としては主張したいところであるが、実験で「創造的な思考を行えたか」検証するのは難しそうだ。



hiroaki_sengoku at 06:44|この記事のURLComments(1)TrackBack(1)自己啓発 
このエントリーを含むブックマーク 2006年04月10日

取引先が秘密保持契約(NDA)のもと開示した秘密や、お客様のデータや、経営情報などについては、あからさまに「秘密」という感じがするので、それが守秘義務の範囲に含まれることは誰の目にも明らかだろう。では、ノウハウはどうだろうか?

本当に件のノウハウなんか「しゃべるな」と言われたから(=明確に秘するべき事と定義されたから)言わないだけで、別にそれを知ったからといってPerl のディストリビューションができるわけでも、俺もディストリビューションを作ってやろうと思わせるもんでもないし、マジで「コロンブスの卵」のノウハウ程度のもの。 (秘密にすべきことは明確にされるべき)

なにを「秘するべき事と定義」するかは各社の考え次第だから、私がとやかく言うことではないが、少なくとも弊社(KLab ないし KLabセキュリティ) では、アイディア自体は秘密とは考えない。

ベンチャー起業の鉄則
アイデアなんて二束三文だ
(ベンチャー起業の秘訣!無料アイデア付き)

と社長自らが言っているし、 私も以前考案したDB サーバのセキュリティ向上策をはじめとして、 実地の運用ノウハウを公開していきたいと思っている。 「アイデアは人に話すことで熟成していく」 (ベンチャー起業の秘訣!無料アイデア付き)と考えるからだ。

そもそもノウハウは、アイディア自体にあるのではなく、 そのアイディアを具現化できる「人」にこそあるのだと思う。



hiroaki_sengoku at 07:38|この記事のURLComments(0)TrackBack(0)技術と経営 
このエントリーを含むブックマーク 2006年04月09日

向いていることを仕事にしよう。 まさに、好きこそものの上手なれ、 嫌々やってる人と、好きでやっている人とでは、どんどん差がつく。 一生のうちのかなりの時間を仕事に費やすのだから好きなことをすべき。

好きなことやっててメシが食えれば苦労はしない、という人が いるかも知れない。しかし、 かなりニッチな分野でも、トップクラスならば飯の種には困らないもの。 ベンチャーと同じ。 市場規模が小さくても、他社に負けない競争力を持っていれば 商売になる。 市場規模がいくら大きくても、他社の後追いなら 価格競争に巻き込まれてジリ貧になる。 まさに、鶏口となるも牛後となるなかれ。

その一方で、

眠る開発屋blog から引用:

ベンチャーって割と志先行になりがちだけども、 どこかでそれと距離を置かないと行き詰まるよね。 多種多様な人間を巻き込む土壌がなければバランス悪くなるもの。 ってか事業を展開させようにも志だけじゃ人が集まらない。

確かにそういう面も否定できない。 多種多様な人間を巻き込みつつ、 各人が高い志を持ち続けるには? ベクトルが異なる志を持つ人達が、 お互いを尊重して総合力を発揮するには?

志まで雇われたいの? から引用:

会社を設立するに当っては、この志というものは欠かせない。 しかしそれは創立者や役員といった「コアメンバー」で押しとどめるべきだ。 会社は従業員に雇用契約以上のものを求めてはならないし、 従業員もまた会社に雇用契約以上のものを求めるべきではない。
...
しかし、セミナーだの訓示だので、志を植え付けるがごとくは、 会社の傲慢というものだ。それでよかった時代もあったのかも知れないが、 今や社畜という生き物は不良資産扱いだ。企業不祥事の際に 産廃扱いで捨てられるのがヲチである。
会社2.0に取りかかる前に、せめて会社1.0をリリースしませんか、みなさん。

志を押しつけ、異なるベクトルを矯正しようとするのではなく、 異なるベクトルの合力を生かす方法はないのだろうか? 力をあわせる唯一の目標が「皆でお金持ちになろうね」(眠る開発屋blog) だけだったとしたら、あまりに悲しい。



hiroaki_sengoku at 08:43|この記事のURLComments(0)TrackBack(1)技術者の成長 
このエントリーを含むブックマーク 2006年04月08日

stone の最初のバージョン。 11年前 Version 1.0 を出したときは、わずか 278行だった (現在の Version 2.3a では、9100行を超えている)。

/*
 * stone.c	simple repeater
 * Copyright(C)1995 by Hiroaki Sengoku <sengoku@virgo.bekkoame.or.jp>
 * Version 1.0	Jan 28, 1995
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU Emacs; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Usage: stone <st> [-- <st>]...
 * <st> := <screen> [<hosts>...] | <host>:<port> <sport> [<hosts>...]
 *
 * (1) Any packets received by <screen> are passed to DISPLAY
 * (2) Any packets received by <sport> are passed to <host>:<port>
 * (3) as long as these packets are sent from <hosts>...
 * (4) if <hosts> are not given, any hosts are welcome.
 */

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<errno.h>
#include	<sys/types.h>
#include	<sys/time.h>
#include	<sys/socket.h>
#include	<netinet/in.h>
#include	<netdb.h>
#define	BACKLOG_MAX	5

#define XPORT		6000
#define BUFMAX		256

#define STONEMAX	(FD_SETSIZE/3)	/* max # of stones */
typedef struct {
    int sd;	/* socket descriptor to listen */
    struct sockaddr_in sin;	/* destination */
    int nhosts;			/* # of hosts */
    struct in_addr xhosts[0];	/* hosts permitted to connect */
} Stone;

/* *addrp is permitted to connect to *stonep ? */
int checkXhost(stonep,addrp)
Stone *stonep;
struct in_addr *addrp;
{
    int i;
    if( !stonep->nhosts ) return 1; /* any hosts can access */
    for( i=0; i < stonep->nhosts; i++ ) {
	if( addrp->s_addr == stonep->xhosts[i].s_addr ) return 1;
    }
    return 0;
}

/* *stonep accept connection */
void doaccept(stonep,fdsp,pair)
Stone *stonep;
fd_set *fdsp;
int *pair;
{
    struct sockaddr_in from;
    int nsd, dsd;
    int len;
    len = sizeof(from);
    nsd = accept(stonep->sd,(struct sockaddr*)&from,&len);
#ifdef DEBUG
    printf("Accept: %x port %d ...",
	   ntohl((unsigned long)from.sin_addr.s_addr),ntohs(from.sin_port));
#endif
    if( !checkXhost(stonep,&from.sin_addr) ) {
#ifdef DEBUG
	printf("denied.\n");
#endif
	if( nsd >= 0 ) close(nsd);
	return;
    }
#ifdef DEBUG
    printf("accepted.\n");
#endif
    if( nsd < 0 ) {
	if( errno == EINTR ) return;
	fprintf(stderr,"Accept error.\n");
	return;
    }
    if( (dsd=socket(PF_INET,SOCK_STREAM,0)) < 0 ) {
	fprintf(stderr,"Cannot create socket.\n");
	close(nsd);
	return;
    }
    if( connect(dsd,(struct sockaddr*)&stonep->sin,sizeof(stonep->sin)) < 0 ) {
	fprintf(stderr,"Cannot connect socket.\n");
	close(nsd);
	if( dsd >= 0 ) close(dsd);
	return;
    }
    pair[nsd] = dsd;
    pair[dsd] = nsd;
    FD_SET(nsd,fdsp);
    FD_SET(dsd,fdsp);
}

void repeater(nstones,stones)
int nstones;	/* # of stones */
Stone *stones[];
{
    int sdmax;
    int pair[FD_SETSIZE];
    fd_set fds, rfds;
    int width, i;
    char buf[BUFMAX];
    int nbyte;
    sdmax = stones[nstones-1]->sd + 1;	/* sd of last stone + 1 */
    FD_ZERO(&fds);
    for( i=0; i < nstones; i++ ) FD_SET(stones[i]->sd,&fds);
    width = ulimit(4,0);
    for( i=0; i < width; i++ ) pair[i] = -1;
    while( rfds=fds, select(width,&rfds,NULL,NULL,NULL) > 0 ) {
	for( i=0; i < width; i++ ) {
	    if( FD_ISSET(i,&rfds) ) {
		if( i < sdmax ) doaccept(stones[nstones-sdmax+i],&fds,pair);
		else if( (nbyte=read(i,buf,BUFMAX)) > 0 )
		    write(pair[i],buf,nbyte);
		else {
#ifdef DEBUG
		    printf("shutdown %d, close %d\n",pair[i],i);
#endif
		    if( pair[i] >= 0 ) {
			shutdown(pair[i],2);
			pair[pair[i]] = -1;
		    }
		    pair[i] = -1;
		    close(i);
		    FD_CLR(i,&fds);
		}
	    }
	}
    }
}

void host2addr(name,addrp,familyp)
char *name;
struct in_addr *addrp;
short *familyp;
{
    struct hostent *hp;
    if( hp=gethostbyname(name) ) {
	bcopy(hp->h_addr,(char *)addrp,hp->h_length);
	if( familyp ) *familyp = hp->h_addrtype;
    } else if( (addrp->s_addr=inet_addr(name)) != -1 ) {
	if( familyp ) *familyp = AF_INET;
    } else {
	fprintf(stderr,"Unknown host : %s\n",name);
	exit(1);
    }
}

/* make stone */
Stone *mkstone(dhost,dport,port,nhosts,hosts)
char *dhost;	/* destination hostname */
int dport;	/* destination port */
int port;	/* listening port */
int nhosts;	/* # of hosts to permit */
char *hosts[];	/* hosts to permit */
{
    Stone *stonep;
    struct sockaddr_in sin;
    int i;
    stonep = calloc(1,sizeof(Stone)+sizeof(struct in_addr)*nhosts);
    if( !stonep ) {
	fprintf(stderr,"Out of memory.\n");
	exit(1);
    }
    stonep->nhosts = nhosts;
    bzero((char *)&sin,sizeof(sin)); /* clear sin struct */
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);	/* convert to network byte order */
    host2addr(dhost,&stonep->sin.sin_addr,&stonep->sin.sin_family);
    for( i=0; i < nhosts; i++ ) {
	host2addr(hosts[i],&stonep->xhosts[i],NULL);
#ifdef DEBUG
	printf("permit %x to connecting to %x:%d\n",
	       ntohl((unsigned long)stonep->xhosts[i].s_addr),
	       ntohl((unsigned long)stonep->sin.sin_addr.s_addr),dport);
#endif
    }
    stonep->sin.sin_port = htons(dport);
    stonep->sd = socket(AF_INET,SOCK_STREAM,0);
    if( stonep->sd < 0 ) {
	fprintf(stderr,"Can't get socket.\n");
	exit(1);
    }
    if( bind(stonep->sd, (struct sockaddr*)&sin, sizeof(sin)) ) {
	fprintf(stderr,"Can't bind.\n");
	exit(1);
    }
    listen(stonep->sd,BACKLOG_MAX);
#ifdef DEBUG
    printf("stone%3d:%s:%d <- %d\n",stonep->sd,dhost,dport,port);
#endif
    return stonep;
}

help(com)
char *com;
{
    fprintf(stderr,
	    "Usage: %s <st> [-- <st>]...\n"
	    "   st: <screen> [<hosts>...]"
	    "| <host>:<port> <port> [<hosts>...]\n"
	    ,com);
    exit(1);
}

int getdist(p,portp)
char *p;
int *portp;
{
    while( *p ) {
	if( *p == ':' ) {
	    *p++ = '\0';
	    *portp = atoi(p);
	    return 1;
	}
	p++;
    }
    return 0;
}

main(argc,argv)
int argc;
char *argv[];
{
    int nstones;	/* # of stones */
    Stone *stones[STONEMAX];
    int i, j, k;
    char display[256], *p, *q;
    char *disphost, *host;
    int dispport, port, sport;
    p = getenv("DISPLAY");
    if( p ) {
	strcpy(display,p);
	getdist(display,&dispport);
	disphost = display;
	dispport += XPORT;
    } else {
	disphost = NULL;
    }
    if( argc < 2 ) help(argv[0]);
    setbuf(stdout,NULL);
    nstones = 0;
    for( i=1; i < argc; i++ ) {
	if( getdist(argv[i],&port) ) {
	    host = argv[i++];
	    if( argc <= i ) help(argv[0]);
	    sport = atoi(argv[i++]);
	} else {
	    host = disphost;
	    port = dispport;
	    sport = XPORT+atoi(argv[i++]);
	}
	j = 0;
	k = i;
	for( ; i < argc; i++, j++ ) if( !strcmp(argv[i],"--") ) break;
	stones[nstones++] = mkstone(host,port,sport,j,&argv[k]);
    }
    q = argv[argc-1] + strlen(argv[argc-1]);
    for( p=argv[1]; p < q; p++ ) *p = '\0';
    repeater(nstones,stones);
}


hiroaki_sengoku at 11:45|この記事のURLComments(0)TrackBack(0)stone 開発日記 
このエントリーを含むブックマーク 2006年04月07日

今朝未明、bフレッツのメンテナンス工事があって、 GCDの対外線が切れた。 切れたこと自体は事前に NTT からアナウンスがあったことだし 問題はないのだが、

bフレッツが切れたことにより PPPoE を行っている pppd が終了すると、 /etc/ppp/adsl-lost が実行される。 GCD の adsl-lost は次のようになっている:

#!/bin/sh
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
. /etc/rc.d/hostname
MASTER_FILE="/var/run/keepalived_vrrp.MASTER"
if [ -f $MASTER_FILE -a "$1" = "/etc/ppp/pppoe.conf" ]; then
    rm -f $MASTER_FILE
    ERROR_FILE="/var/run/keepalived_vrrp.ERROR"
    echo 2 >> $ERROR_FILE
    /command/svc -t /service/keepalived/
fi

つまり、/var/run/keepalived_vrrp.MASTER を削除したうえで keepalived を再起動する。 すると、keepalived は BACKUP 状態に移行する。 すると、

/etc/rc.d/vrrp_notify INSTANCE VI BACKUP

実行されて、vrrp_notify が adsl-stop を呼び出す。

そして、adsl-stop が adsl-connect が強制終了する。 こうして完全な BACKUP 状態になるはずだった。

ところが

今朝起きて見てみると、 ゲートウェイが両方とも BACKUP 状態になっていた。(*_*)

片方のゲートウェイは PPPoE がつながっていて、 インターネットに正常に接続しているのだが、 あいにく BACKUP 状態なので、 GCD LAN のデフォルトゲートウェイアドレスである 192.168.1.1 を 持っておらず、 GCD LAN の他のマシンからインターネットへ接続できない状態に 陥っていた。

どうしてこんな事態になってしまったのか考えるより、 まずは復旧が優先ということで、 PPPoE がつながっているのに BACKUP 状態となっている 中途半端なゲートウェイ上で adsl-connect プロセスを殺し、 keepalived を再起動させて、 完全な BACKUP 状態へ移行させた。

すると、もう片方のゲートウェイが自動的に MASTER へ昇格して、 無事正常な状態に戻った。

その後、原因を調べていると、 前述したように adsl-stop が adsl-connect を強制終了させることが できずに adsl-connect が走り続けていたことが原因と判明。 つまり、adsl-connect が走り続けたために、 BACKUP 状態であるにもかかわらず PPPoE のリトライを行っていた。 そして bフレッツのメンテナンス工事の終了にともなって PPPoE が成功、 BACKUP 状態なのに PPPoE でつながっている、という状態が発生した。 この状態では、正常にインターネットにつながっているために、 MASTER に昇格することはなく、BACKUP 状態が継続する。

一方、もう片方のゲートウェイは、MASTER に昇格しようとして PPPoE を試みても失敗してしまう。 つまり MASTER へ昇格できない。 したがって、両方のゲートウェイが BACKUP 状態のままになる。

というわけで問題は adsl-stop が adsl-connect を 終了させることができない点にありそうだと推測。 あらためて adsl-stop を読みなおすと...

    # Kill pppd, which should in turn kill pppoe
    if [ -r "$PPPD_PIDFILE" ] ; then
        PPPD_PID=`cat "$PPPD_PIDFILE"`
        $LOGGER -p daemon.notice "Killing pppd"
        echo "Killing pppd ($PPPD_PID)"
        kill $PPPD_PID > /dev/null 2>&1 || exit 1
    fi
    ....
    # Kill adsl-connect
    $LOGGER -p daemon.notice "Killing adsl-connect"
    echo "Killing adsl-connect ($PID)"
    kill $PID > /dev/null 2>&1

以前このスクリプトを読んだときは見落としていたのだが、 「kill $PPPD_PID」の後に「|| exit 1」がついている! なんだこれは!?

つまり、pppd プロセスが動いていなければ exit してしまうので、 adsl-connect を終了させないではないか! 誰だ、こんな頭悪いコードを書いた奴は。

adsl-connect は pppd が失敗すると、 リトライするまえに 5秒 sleep する。 つまりこの 5秒間は pppd プロセスが無い。 だから、運悪く sleep 中に adsl-stop を実行すると、 adsl-connect が強制終了されずに残ってしまう。

速攻で adsl-stop から「|| exit 1」の部分を削除した。 これで安心 (だといいな)。



このエントリーを含むブックマーク 2006年04月06日

GCDバックアップ回線用のフレッツADSL では、 バックアップ回線ということで固定IPアドレスにはしていない。 どのくらい安定してつながっているか過去 10日間の統計を取ってみた。

毎分 ssh のテストログイン 14400回(=10*24*60)の結果:

エラー回数 112回
IPアドレスの切換回数 114回
IPアドレス平均持続時間 123.6分
持続時間の標準偏差(σ) 201.2分
持続時間の中央値 39分
最短持続時間 1分
最長持続時間 1385分

思ったより IPアドレスの切換が激しい。 一時間も持続しないIPアドレスが半数以上にのぼる。 10時間以上安定していたのは 4例だけ。



このエントリーを含むブックマーク 2006年04月05日

日経ソフトウェアの大森敏行氏の記事中の注釈に、

プログラマが「プログラミングに特別な能力が必要であること」を 強調したがるのには理由がある。 現在の日本のソフトウエア業界には 「プログラミングをしない人のほうが立場が上である」というケースが 往々にして見られ, そのせいでプログラムの質が上がらないという構造的な問題があるからだ。」

というくだりがある。

確かに一理あるし、 優秀なプログラマがエキスパートとして尊敬される土壌づくりは、 優れたソフトウェアを開発するために必須の条件であろう。 しかしながら、 「プログラミングをしない人のほうが立場が上」というのは 一面的な見方ではないのか。 プログラマ兼 CTO である私としては一言いいたくなる。;-)

「プログラミングをしない人」⇒「立場が上」

なのではなくて、

「プログラミングをする人」⇒「視野が狭い人が多い」
「視野が狭い」⇒「立場が下」

に (ムリヤリな) 三段論法を適用しているだけなのではないのだろうか。 自身の視野の狭さを棚に上げておいて、 「プログラマだから立場が低い」と嘆くのはいかがなものかと思う。 もちろん、 「プログラミングに特別な能力が必要」なのではなくて、 「優れたプログラムを書くには特別な能力が必要」が正しい。 「下っ端プログラマ」と「優秀なプログラマ」を同列に扱ってはいけない。

前述した大森氏の注釈は、次のように続く:

この問題に関しては「Life is beautiful」というブログの 「ソフトウェアの仕様書は料理のレシピに似ている」というエントリが参考になる。

さっそくこの中島聡氏のブログを 読んでみた。

ソフトウェアのアーキテクトが自らプログラムを書いたり、 下っ端のエンジニアの書いたコードをレビューするのは、 レストランのシェフが自ら料理をしたり、 下っ端の料理人の作ったスープの味見をするとの同じである。

とても適切な喩えのように思える。 しかしこの伝で行けば、

下っ端のプログラマ(料理人)がアーキテクト(シェフ)になれるかは 才能に依存するところが多分にあり、 適性がない人は、いくら努力しても永久に下っ端のまま、 一人月 30万円レベルから抜け出せない。 そういう人はさっさとプログラミング(料理)をあきらめて、 マネジメントをやったほうがいい。 マネジメントなら才能が無くても経験次第でそれなりのレベルにはなる。

というとても残酷ではあるが、 とても実状に近い類推が出てきてしまうのだが...

ちなみに中島氏のブログには

優秀なエンジニアとそうでないエンジニアの生産性は(誇張抜きで) 20対1ぐらいである。

という話が出てくるのだが、私はもっと差があると感じている。 超優秀なプログラマと、下っ端プログラマとでは、 生産性が(誇張抜きで) 3桁違う、 というのが私の主張。 トッププログラマの 1000分の1の生産性しかない、 そもそもプログラミングに適性がない人が 無理矢理プログラマを目指そうとするから、不幸が始まる。 しかも、プログラミングは適性のない人でも時間さえかければ (かろうじて)動くプログラムが作れてしまったりするから余計タチが悪い。

これが料理であれば適性のない人は何日かけたって美味しい料理は作れないわけで、 早い段階で才能の無さを自覚して他の職を探すものだと思うが、 下っ端プログラマにはその自覚がない。 自覚が無いからプログラマへの道をあきらめめようとしない。 仕方ないので 35歳くらいで周囲が引導を渡すわけで、 これが「プログラマ35歳定年説」の正体。

- o -

ここまでの話と全然関係ないが、大森氏の記事に

BASICというプログラミング言語の文法を知ったのは確か中学生のときだし, 高校生のときにはシャープの「MZ-80K2E」というマイコンで 簡単なゲームのプログラムくらいは書いていた。

と書いてあったのが、 実はこの記事を引用しようと思った真の理由だったりする (^^;)。 私も中学一年の終わり頃 BASIC を知り、 高校生のとき MZ-80K2E でゲームを作ったりして遊んでいた。



hiroaki_sengoku at 06:52|この記事のURLComments(3)TrackBack(0)技術者の成長 
このエントリーを含むブックマーク 2006年04月04日

人の上に立とう、といっても、威張りちらしたい、というわけではない。 相手より高い視点を持てるよう努力しよう、ということだ。

大局的な判断ができる人がリーダシップをとり、 局所的な判断しかできない人は、その指示に従うことになる。 上司が指示を出し、部下がそれに従う、というわけではない。 もちろん指揮命令系統が決められている場合は、 そこから逸脱するのは難しいかも知れないが、 職種が違う場合はどうだろう?

セールスがお客様の言葉を錦の御旗よろしく振りかざして マーケタへマーケティング戦略を指示し、 マーケタが製品のあるべき姿をプランナへ指示し、 プランナが製品仕様を決めて技術者に指示する。

こうした指示の連鎖は、よく見かける光景だが、 セールス、マーケタ、プランナ、技術者は、 本来は上下関係はないはずである。 なぜこのような指示する側とされる側に分かれてしまうのか。

で、上下関係をひっくり返してみようと思った。 技術者である私が、セールスやマーケタやプランナに指示を出してみる。

もちろん、いきなり指示を出そうと思っても、 何を指示すべきか分からないから、準備が必要である。

まず営業同行して、セールスの人達がお客様と何を話しているか観察。 ところがお客様の顔がいかにも興味なさそうなのに、 とうとうと自社製品の説明を立て板に水のごとく話している姿を目撃。 う〜ん、素人の私にだって、こういう営業方法が 論外なことくらいは分かる。 もちろん優秀な営業マンもいるのだが、 私の目から見てもダメダメな人がいるというのは発見だった。

つぎにマーケ本部の定例ミーティングに参加してみる。 最初のうちは全貌が把握できていないのでおとなしくしているが、 すぐに我慢ができなくなった。 自社のポジションがまるで理解できていない。 弊社は大企業とは違うのだ。 弊社のロゴを見たって誰も弊社のことを思い浮かべたりはしない。 それにいま攻めようとしている市場は導入期である。 新しいカテゴリを作ろうとしているのだから 成熟期の商品と同じ攻め方でいいわけがない。 もちろん優秀なマーケタもいるのだが、 マーケティングの教科書を一読だけで分かるようなことを押さえずに、 見よう見まねの SWOT 分析などをして マーケタ気取りの人がいるというのは発見だった。

プランナは、技術者に近い。技術者と同じく「作る」職種である。 仕様を考えつつどんどんプロトタイピングして実ユーザの声を聞く、 という開発手法もあるわけで、 プランナと技術者は対等の関係になることも多い。

セールスやマーケタは、「売る」職種である。 技術者とは別人種と言ってもいいだろう。 だからといって「売る」職種が、「作る」職種よりも 立場が上というわけではない。 単に「売る」職種の方が広い視野を持ちやすく、 「作る」職種が、ともすると目先の問題に固執しがち というだけのこと。



hiroaki_sengoku at 08:38|この記事のURLComments(0)TrackBack(0)技術者の成長 
このエントリーを含むブックマーク 2006年04月03日

IPv6ブリッジ機能付きルーター (ブルータ) を使えば、 NAT 内の LAN でそのまま IPv6 を使えるわけで、 IPv6 の敷居は一気に下がる。

つまり、ほとんどの OS がすでに IPv6 をサポートしているので、 ルータが IPv6 を素通し (ブリッジ) しさえすれば、 LAN 内で IPv6 を意識せずに使える。

今まで通り IP を (NAT 経由したプライベートアドレスで) 使いながら、IPv6 のオイシイところ (end-to-end 通信ができる) だけを ツマミ食いできるわけで、 今度こそ (^^;) IPv6 が普及するのではないかと...

Linux でブルータを実現するには、 まずブリッジ機能をカーネルに組み込んでおく。

# brctl show br0
bridge name     bridge id               STP enabled     interfaces
br0             8000.00022Axxxxxx       no              eth0
                                                        eth1

このままだと IP までブリッジしてしまうので、 IP に対してはルータとして機能し、 IPv6 に対してだけブリッジさせるために、ebtables を使う。

# ebtables -t broute -L
Bridge table: broute

Bridge chain: BROUTING, entries: 3, policy: DROP      …… (3)
-j mark --set-mark 0x0 --mark-target CONTINUE         …… (0)
-p IPv6 -i eth0 -j mark --set-mark 0x600              …… (1)
-p IPv6 -i eth1 -j mark --set-mark 0x601              …… (2)

上記設定の意味は、

(0) まず全パケットに対し 0x0 のマークをつける。
    で、
(1) eth0 から入ってきた (LAN内から外へ出ていく)
    IPv6 パケットは、0x600 のマークをつけてブリッジする。
(2) 逆に eth1 から入ってきた (外から入ってきた)
    IPv6 パケットは、0x601 のマークをつけてブリッジする。
(3) それ以外 (IP パケットや PPPoE パケット) はブリッジしない (DROP)
    つまり IP に対してはルータとして機能する

さらに ip6tables を使ってフィルタリングする。

ip6tables -P FORWARD DROP                                       …… (*)
ip6tables -I FORWARD -j incoming -m mark --mark 0x601/0xffff    …… (2)
ip6tables -I FORWARD -j ACCEPT -m mark --mark 0x600/0xffff      …… (1)
(1) 0x600 のマークがついているパケットは、素通し (ACCEPT) する。
(2) 0x601 のマークがついているパケットは、incoming チェインへ渡す。

incoming チェインで ACCEPT されなかったパケットは、
(*) FORWARD チェインのポリシーに従って、DROP される。

ebtables でマーク付して ip6tables でそのマークに基づいて フィルタリングするという、 まわりくどい方法をとっているのは、 ebtables はあくまで L2 のフィルタなので IPv6 アドレスに基づいたフィルタリングができず、 逆に ip6tables は L3 のフィルタなので eth0/eth1 はまとめてブリッジインタフェース br0 としてしか見えず、 どちらの物理インタフェースから入ってきたパケットか 判別できないため。

ちなみに iptables だと、 L3 フィルタといいながら physdev モジュールがあるので、 ブリッジのどちら側から入ってきたか判別できたりする。 ip6tables の機能拡張に期待したいところ。



このエントリーを含むブックマーク 2006年04月02日

select でも性能低下を招かないように修正した stone のベンチマーク結果。

req/sec ms/req KB/sec
select 681.83 1.467 191.59
epoll 692.74 1.444 194.66
apache 983.60 1.017 278.36

select 版と epoll 版で、ほぼ同等の速度。もちろん、listen するポート数が多くなれば差が出てくる可能性はある。

測定条件:

$Id: stone.c,v 2.2.2.15 2006/04/02 00:03:56 hiroaki_sengoku Exp $
senri% stone -rn localhost:80 2345 >& /dev/null
asao% ab -n 1000 -c 10 http://senri:2345/health
This is ApacheBench, Version 2.0.40-dev <$Revision: 1.121.2.4 $> apache-2.0
Document Path:          /health
Document Length:        131 bytes
Concurrency Level:      10
req/sec
Requests per second [#/sec] (mean)
ms/req
Time per request [ms] (mean, across all concurrent requests)
KB/sec
Transfer rate [Kbytes/sec] received


hiroaki_sengoku at 09:16|この記事のURLComments(0)TrackBack(0)stone 開発日記 
このエントリーを含むブックマーク 2006年04月01日

epoll 化にあたって stone 2.3 のコードの全面的な見直しを 行っていたところ、性能低下を引き起こす問題点を発見。

stone 2.3 において、TCP 接続を accept し、 中継先へ connect する部分の構造は、 次のようになっている(説明のため大幅に単純化している)。

fd_set rin;

main() {
    ....
    for (;;) repeater();
}

void repeater(void) {
    ....
    rout = rin;
    ret = select(FD_SETSIZE, &rout, &wout, &eout, timeout);
    ....
    if (FD_ISSET(stone->sd, &rout)) {
        FD_CLR(stone->sd, &rin);
        ....
        ASYNC(asyncAccept, stone);
    }
}

void asyncAccept(Stone *stone) {
    ....
    nsd = accept(stonep->sd, from, &fromlen);
    FD_SET(stonep->sd, &rin);
    ....
}

つまり、select(2) によって listen しているポート (stone->sd) が accept 可能であることが判明すると、 まず rin の該当ビットをクリア(FD_CLR)し、 accept 処理を行う子スレッドを立てる(asyncAccept)。 このスレッドは accept(2) を呼び出し、 rin の該当ビットを再セット(FD_SET)する。

ところが、子スレッドと親スレッドのどちらが先に処理されるか、 という問題がある。 子スレッドが先に実行されるなら、 rin の該当ビットが再セットされた後、 親スレッドの select(2) が実行されるので問題は起きないが、 普通のスレッド実装では、 親スレッドの実行が優先されるようだ。

つまり、親スレッドの処理が先に実行されると、 rin の該当ビットがクリアされたままで、 親スレッドが select に突入してしまう。 親スレッドが select 待ちになってから、 子スレッドが rin を再セットするが、 時すでに遅し、 親スレッドが呼び出した select では、 listen しているポート (stone->sd) は 監視しない状態になってしまっている。

このため、この select が timeout して、 次回の select 待ちに入るまで、 接続を受け付けない状態が続いてしまう。 timeout は 0.1秒なので、 秒あたり高々10回の接続しか受け付けられなくなってしまう。

この問題を回避するには、 子スレッドで accept するのではなく、 親スレッドで accept すればよい。 この場合、rin の該当ビットをクリアする必要もなくなる。

で、なぜ epoll 化したことによって高速化されたかというと、 epoll の場合、rin に相当するものがカーネル空間にあるわけで、 子スレッドで EPOLL_CTL_MOD (FD_SET に相当) すると、 即それが epoll_wait 待ち (select 待ちに相当) している 親スレッドに反映するためだろう。



hiroaki_sengoku at 18:37|この記事のURLComments(0)TrackBack(0)stone 開発日記