コードの読み方

7131 ワード

Cの読み方と土の読み方
Posted on
August 5, 2010
by
roczhou
Cの勉强はずっと断続的でしたが、半年前にsyslog-ngプロジェクトのため、最近の一周间はライトtpdのモジュール改造のため、C言叶を2回もかじらざるを得ませんでした.仕事以来、shellとPythonを使っていましたが、いくつかの経験をまとめることができます.
  • エントリを探します:まず、関数エントリを探します:main().ライトtpdのように複数のバイナリプログラムがあり、Makefileを一目スキャンすることができます.am、大体
  • を見つけることができます
  • 文書を読み、資料を探し、基本フレームワークを理解する:手をつけずに読み始める.cソースファイルは、まず公式ドキュメントを探したり、犬を放したりしてプログラムの基本フレームワークを理解したりすることができます.アーキテクチャ図があれば、一番いいです.たとえばsyslog-ngのドキュメントの説明は非常によく書かれており、基本的な概念を理解するのに役立ちます.特に、構造体やコールバック関数の多くを後で理解する場合は、説明ドキュメントでインスタンスマッピングを見つけることができます.また、既存の资料を探したり、犬を放したり、「xxxソース解析」というキーワードを使って検索したりすることができます.通常、他の人が书いた分析资料があります....もしなかったら...では、おめでとうございます.
  • 基礎知識準備:他人が書いたCコードを本当に読むには、基本的な文法のほかに、基本的な標準ライブラリ(少なくとも概念がある)を理解する必要があります.また、システム呼び出しについても一定の認識が必要です.その点、Steve Richardの『UNIX環境高度プログラミング』をぜひ読んでみてください.また、異なるアプリケーションタイプには、いくつかの基本的な方法があります.例えば、bashのようなコマンドライン解釈器(他のアプリケーションでもコマンド解釈があり、Pythonでcmdモジュールを提供するなど、他の言語で書かれたプログラムを含む)については、以前、簡単なbashをどのように書くかという本がありましたが、当時実践したことがあります.裏面にはcdコマンドもありませんでしたが、良い実践材料でした.今度本の名前を出してください.syslog-ng、lighttpdのようなサービスプログラムについては、通常、マルチスレッドモードかpoll()モードの2つのモードに分けられていますが、私は一般的にpollを使う傾向があります.基本的には、私も今「thread is a bad idea」と思っています.多くの性能上の問題は、Linuxオペレーティングシステムを利用して補完することができます.サービスプロセスモードは、通常、コマンドラインとプロファイルを解析してからメインサイクルに入り、poll/epollのようなシステム呼び出し(異なるシステムが具体的に異なる)を使用して、さまざまなリクエストを繰り返し処理します.この概念があれば,主サイクルにおけるpollの所在を見つけることで,リードコードの起点を見つけることができる.
  • データ構造を理解する:データ構造を理解することは重中の重さであり、最初はすべてを明らかにすることはできず、すべての*を通読するしかない.hファイルは、初歩的なデータ構造を理解し、これらの構造体と現実的なエンティティとのつながり、特にドキュメントに記載されているエンティティとのつながりを「想像」しようと努力します.この過程は私たちに関連するそれぞれを大体理解させることができる.cファイルの大まかな役割は、マークしたり、書いたりします.高度な言語には、PythonのInt、String、List、Dict、Tuple、Fileなど、便利な基本データ構造がありますが、C言語には現在、標準がないようです.これも柔軟性に必要な代価です.例えばsyslog-ngはglibライブラリのデータ構造を使用し、lighttpdはarray、keyvalue、string、buffer、splay_を自分で書いた.treeなど.プログラム本体を理解するには,これらのデータ構造はあまり役に立たず,最初は片側に置いておくことができ,大体どういう意味か知っているだけで,コードで出会ったとき,その関数名に基づいて高度な言語の方法で想像することができる.現在の多くのCプログラムは,実装時にオブジェクト向けの方法をシミュレートしており,具体的な方法は構造体において関数ポインタを大量に用いてコールバック関数を形成することである.だからこれらのデータ構造を研究する時、対象化の思想で理解を助けることができて、そして自分にいくつかの問題を聞くことができます:Q:これらのデータ構造の間の関係は何ですか?Qコードでなぜこのようなデータ構造が使用されるのかを,オブジェクト化された組立関係(composite)で理解するにはどうすればよいか.Q:これらのデータ構造をどこで、どのように使用しますか.Q:typedef-インスタンスはいつ作成されますか?
  • 整理コールバック:コードコールバック(callbacks)に対して基本的な認識があり、いくつかの重要な構造体の中のコールバック関数が大体どれらを指すかを知っている.cファイル
  • 呼び出し関係を追跡する:関数/ファイル(.c)の呼び出し関係を分析、追跡する.通常、a.gdb法:gdbの有用な命令を理解するいくつかの方法がある.「C in a Nutshell」の最後の章は経典と実用的で、参考にすることができます(Nutshellシリーズはすべて悪くないようで、「Linux in a Nutshell」「Python in a Nutshell」はすべてとても価値のある章と情報があります).gdb法の問題は,特に命令の使用が未熟な場合に比較的遅いことである.私は比較的に小さい範囲の追跡に適していると思って、つまり全体の呼び出しの流れに対して一定の認識があった後に、いくつか関心の関数あるいはホットスポットに対して、比較的に細かい追跡b.全体の上から把握して、ログ法を採用して、つまりプログラムのデバッグ(debug)モードあるいは詳しいモードを開いて、そのログの出力を見ることができます.また、そのlogメソッドを参照して、コードと一致するlog方式を使用して、自分の関心のあるいくつかの値と実行点をログc.に出力することができます.ソースコードに各関数が入るときにマクロを挿入し、そのファイルと自分の関数名、スタックの深さ(SDP++)を印刷し、出力するときに対応するマクロがあります(SDP--).この変更は、ソースコードで直接変更され、再コンパイルする必要があります.その考え方はgprof,gdbのようなシンボルテーブルをキャプチャし,関数入口に_mcount()関数を挿入することと似ている.ただ、私はまだどのように実現できるか分からないので、このような土の方法しかできません.それはPythonスクリプトを書いただけです(正確にはsyslog-ngとlighttpdについてそれぞれ修正しました).それは行に基づいて分析し、関数の出口と入り口を見つけて、gccのような文法分析器ではありません.正確には難しいので、追跡に時間をかけて校正しなければなりません.(ctagsがどうなっているのか分かりませんが、マクロで関数を定義しているものもありますが、ctagsでは見つからないものもあります)(gprofを利用するのも当然のことです.gprof 2 dot.pyとgraphivzを組み合わせて関数呼び出し関係図を生成することができますが、この図には順序は含まれておらず、統計値にすぎないので、一般的にはパフォーマンスデバッグ時にのみ使用されます)(gprofとgdbの実現、ltrace/straceを研究する時間があれば、このtraceの考え方と結びつけて何かを作ることができるかもしれません)後でこのtraceの方法と、対応するスクリプトを書く時間があります.traceの効果は大体以下の通りである:
    [Intranet root@oplive-test2 /root/lighttpd-1.4.26.roczhou/src]
    #python ctrace.lighttpd.py call -l /root/ctrace/ctrace.16 | grep -E '^ [0-9]|^debug' | less
    
    debug 2 plugins_call_handle_trigger(srv)
    debug slot in plugin: mod_proxy
     1  stat_cache.c# stat_cache_trigger_cleanup, 741
     1  fdevent.c# fdevent_poll, 175
     1  fdevent_linux_sysepoll.c# fdevent_linux_sysepoll_poll, 86
    debug 2 plugins_call_handle_trigger(srv)
    debug slot in plugin: mod_proxy
     1  stat_cache.c# stat_cache_trigger_cleanup, 741
     1  fdevent.c# fdevent_poll, 175
     1  fdevent_linux_sysepoll.c# fdevent_linux_sysepoll_poll, 86
     1  fdevent.c# fdevent_event_next_fdndx, 233
     1  fdevent_linux_sysepoll.c# fdevent_linux_sysepoll_event_next_fdndx, 116
     1  fdevent.c# fdevent_event_get_revent, 182
     1  fdevent_linux_sysepoll.c# fdevent_linux_sysepoll_event_get_revent, 91
     1  fdevent.c# fdevent_event_get_fd, 190
     1  fdevent_linux_sysepoll.c# fdevent_linux_sysepoll_event_get_fd, 106
     1  fdevent.c# fdevent_get_handler, 198
     1  fdevent.c# fdevent_get_context, 211
     1  network.c# network_server_handle_fdevent, 29
     2    connections.c# connection_accept, 1375
     3      connections.c# connections_get_new_connection, 43
     4        connections.c# connection_init, 738
     4        connections.c# connection_init, 738
     4        connections.c# connection_init, 738
     4        connections.c# connection_init, 738
     4        connections.c# connection_init, 738
     4        connections.c# connection_init, 738
     4        connections.c# connection_init, 738
    ............
     4        log.c# log_error_write, 281
     4        log.c# log_error_write, 281
    debug 5 plugins_call_handle_uri_raw(srv, con)
     4        log.c# log_error_write, 281
     4        log.c# log_error_write, 281
    debug 5 plugins_call_handle_uri_clean(srv, con)
    debug slot in plugin: mod_access
     5          mod_access.c# mod_access_patch_connection, 89
     5          log.c# log_error_write, 281
    debug slot in plugin: mod_cache
     4        log.c# log_error_write, 281
     4        stat_cache.c# hashme, 239
     4        log.c# log_error_write, 281
     4        stat_cache.c# stat_cache_get_entry, 380
     5          stat_cache.c# hashme, 239
     5          splaytree.c# splaytree_splay, 64
     4        status_counter.c# status_counter_set, 59
     5          status_counter.c# status_counter_get_counter, 18
    debug slot in plugin: mod_proxy
     4        log.c# log_error_write, 281
     4        log.c# log_error_write, 281
     4        log.c# log_error_write, 281
     4        log.c# log_error_write, 281
     4        log.c# log_error_write, 281
     4        log.c# log_error_write, 281
    debug 5 plugins_call_handle_docroot(srv, con)
    debug slot in plugin: mod_cache
    の最初の列はこの関数のスタックの深さであり、後ろにはファイルと関数名、最後に行番号(現在はまだ正確ではない)があり、インデント間の関係を通じて、関数呼び出しの順序と相互の関係を理解することができる.この追跡方式も私たちがコールバックを整理するのに役立ち、特に関係がまだはっきりしていないときと慣れていないとき(syslog-ngを読み始めたばかりのときです).
  • データストリーム:最後に、データの流れを明らかにします.要求入力->処理->結果出力のようなプロセス全体と、対応するデータ構造と、この/これらのデータ構造が通過する他のデータ構造エンティティです.たとえば、syslog-ngにはstruct*LogMessageがあり、lighttpdでは各connection*conにrequest*requestとresponse*request
  • があります.
  • 構成:構成された構造、および各異なるデータ構造エンティティの実装にどのように発散するか.

  • This entry was posted in Linux and C and tagged C, lighttpd, syslog-ng. Bookmark the permalink.
    ← Py C Ext module attr && inotify events
    gdb batch commands && cscope