Windows system >> Windowsの知識 >  >> Linuxシステムチュートリアル >> Linuxチュートリアル >> サーバーサイドでの開発経験のまとめLinux C言語

サーバーサイドでの開発経験のまとめLinux C言語

  

数年に及ぶ開発の後、サーバーサイドで開発を行う際にはアルゴリズムとパフォーマンスの問題をいくつか考慮する必要があります。 。

私は主にLinuxでC言語を開発しているので、後者の実装はLinux OS
に基づいており、C言語で説明されています。他のプラットフォームや言語も同様に考慮する必要がありますが、実装の詳細にいくつかの違いがあるかもしれません。説明した内容はすべて32ビットシステムの開発に基づいています。

サーバープログラム開発の中核は安定しており、安定性を前提として効率性を考慮する必要があります。主なパブリックモジュールは、メモリプールとスレッドプールです。サーバープログラムは通常長時間実行され、頻繁にメモリ操作を作成および解放するため、システムのmallocおよびfreeメソッドを使用すると、システム内で大量のメモリ断片化が発生し、効率と安定性に影響します。メモリプールの主な考え方は、最初にシステムのmallocを呼び出して大容量メモリをオープンし、次にこの大容量メモリを管理することですプログラムでメモリが使用されると、メモリプールはメモリを割り当て、メモリが解放されるとメモリプールに通知するだけです。本当にメモリを解放しません。スレッドプール、原則は似ています、いくつかのタスクを同時に処理するときは、たくさんのスレッドを使う必要があります。最初にたくさんのスレッドを作成し、次に処理するタスクがあるたびにアイドルスレッドを見つけてタスクを処理します。開始します。これにより、タスクが作成されるたびにスレッドを作成することによる時間のオーバーヘッドが節約されます。

予備知識

以下は、ライブラリパッケージを使用するためのヒントです。 C言語で開発しているので、構造と効率のバランスをとるためには、カプセル化するためのいくつかのスキルが必要です。その結果、プログラムはC言語の効率を保証するのに十分モジュール化されます。

container_ofマクロ

最初にこのマクロを紹介します。このマクロはLinuxカーネルで見たもので、構造体のメンバへのポインタを通して構造体全体を派生させるために使用されます。ポインタ、例を見てみましょう。たとえば、次のような構造があります。
1:struct my_struct 2:{3:int a; 4:int b; 5:char c; 6:short d; 7:};

次のコードを見てください。 Br> 1:int 2:main()3:{4:struct my_struct mys; //構造体オブジェクトを作成する5:struct my_struct * pmys; //構造体へのポインタを宣言する6:char * pc =&( int * pb =&(mys.b); //pbは、構造体8のbメンバーを指します。pmys = container_of(pc、struct my_struct、c); /Mys.c); //pcは、構造体7のcメンバーを指します。 /pmysは実際にはmys構造体9を指しています:pmys = container_of(pb、struct my_struct、b); //pmysはまだmys構造体10を指しています://上記の2行のコードはpmys =& mys; 11:..と同じです。上記のコードは、container_ofを呼び出すことで、構造体のメンバ(pcやpbなど)へのポインタを介して構造体全体へのポインタを取得できることを示しています。このマクロは3つの引数を取ります。最初の引数は構造体のメンバへのポインタ、2番目の引数はコンストラクトの型、3番目の引数は最初の引数のメンバポインタが指すメンバの宣言です。で有名な名前です。このマクロは次のように定義されています。
1:#define offsetof(TYPE、MEMBER)((size_t)&((TYPE *)0) - > MEMBER)2:3:#ifdef WIN32 4://WIN32下型安全チェックはサポートされていません)5:#define container_of(ptr、type、member)/6:((type *)(ptr)-offsetof(type、member))7:8:#その他9://10 linux :#define container_of(ptr、type、member)({/11:const typeof(((type *)0) - > member)* _mptr =(ptr); /12:(type *)((char *) _mptr-offsetof(type、member));})13:14:#endif /* * /

上記の実装では、まずoffsetofマクロを定義し、このマクロを使用して構造体のメンバーを計算しています。この構造体の開始アドレスに対するアドレスのオフセット。 WIN32では、システム独自のオフセットマクロを使うことができます。

container_ofマクロは、実際には、構造体のメンバのアドレスから構造体の先頭を基準としたメンバのオフセットを引いた値を使用します。構造体のアドレスは、構造体へのポインタです。 linuxで実装されている場合:const typeof(((type *)0) - > member)* _mptr =(ptr);この文は、実際には型安全チェックを行っています。マクロの最初のパラメータメンバへのポインタの型は、このメンバ型へのポインタであることが保証されています。 Typeofは式の型を取得するために使用されるgcc拡張子です。私はWIN32の下でtypeofの代わりを見つけることができなかったので、私は型安全チェックをしませんでした。

container_ofマクロを使って、何ができますか?たとえば、リンクリストを作成すると、リンクリストのデータ領域は通常、教科書ではintとして扱われます。実際には複雑な構造になる可能性があり、リンクリストを作成する人として、ユーザーがどのデータをリンクリストに格納するのかわかりません。通常の慣例では、データ領域はvoid *なので、ユーザーはこのポインタを使用して任意のオブジェクトを格納できます。ユーザーが自分のデータを保存したい場合は、カスタム構造を持つオブジェクトを作成してから、このポインターを使用してその構造を指すことができます。リンクリストの各要素は、データ領域が占有するメモリに加えてvoid *を占有するため、サーバー側で開発する場合、データ量が多くなり、多くのvoid *が無駄になります。この問題は以下の方法で解決できます。

リンクリスト内の要素の構造を定義する:
1:struct link_item 2:{3:struct link_item * next; 4:};

リンクリストの操作用に提供されているメソッドは、struct link_item *に基づいています。ポインタのメソッド使用時に、ユーザーは自分のリンクリスト要素構造を宣言します。
1:struct user_link_item 2:{3:struct link_item lk_item; 4:int my_data1; 5:short my_data2; 6://... 7:}; p>この構造体はstruct link_item型のメンバーlk_itemを持ちます。リンクリストを使用するときは、常にlk_itemのアドレスをリンクリストのメソッドに渡します。データを取得することはstruct link_itemへのポインタでもありますが、container_ofマクロを使用してリンクリストメソッドによって返されたstruct link_itemへのポインタを使用してstruct user_link_itemのポインタを導出することができます。このような実装における特定の要素のメモリも外部ユーザーによって割り当てられます。

このメソッドは、C ++の継承メカニズムと似ています。継承の問題が発生したときは、この方法を検討することができます。

参照カウンタ

サーバープログラムを開発するとき、私たちはしばしばマルチスレッド並列処理です。処理されるデータにスレッドセーフな問題があります。最も単純なスレッドセーフな処理はアトミック操作です。各処理は単一の命令を使用して行われます。もう1つは、スレッド同期にスレッドロックを使用することです。データ構造に格納されているデータの場合、あるスレッドでそれを読み取ることができ、そのデータは別のスレッドで削除されます。この問題を処理するためにWIN32 COMで使用されている方法をまねることができます。参照カウンタを追加し、データにアクセスするたびにデータの参照カウンタを増分し、使用が完了したら、データの参照カウンタを減分します。参照カウンタの値が0にデクリメントされると、データは実際に削除されます。もちろん、参照カウンタのインクリメントおよびデクリメント操作はスレッドセーフである必要があり、完全にアトミック操作を使用して実装できます。

スレッドセーフである必要があるすべてのデータオブジェクトは、上記のcontainer_ofマクロの実装の継承によって実装されています。

通信プロトコルにおける可変長フィールドの構造的処理

サーバーサイド通信は通常、一連のプロトコルを設計します。契約書には通常次の形式があります。

| 4バイトは、後続のバイト数を表します。 1バイトは特定のフラグを意味します。 1バイトは特定のフラグを意味します。 2バイトは特定のフラグを表します。 nバイトは内容を表します。

上記の合意は、どうやってそれを構造体に変換するのでしょうか。以下で定義されている構造を見てください。
1://誤った構造2:struct wrong_struct 3:{4:長いサイズ; 5:文字f1; 6:文字f2; 7:短いf3; 8:文字*内容; 9:};

上記の構造は明らかに間違っており、主にコンテンツメンバーです。内容はプロトコルの最初の4バイトをカバーする文字列へのポインタです "nバイトは内容を表します"。この問題を解決する方法非常に簡単です:
1:struct right_struct 2:{3:長いサイズ; 4:文字f1; 5:文字f2; 6:短いf3; 7:文字内容[1]; 8:};

コンテンツは、要素が1つだけの文字列の配列として宣言されています。文字列配列も文字列ポインタです。内容を表すには、nバイトを参照するためにcontentメンバを使用できます。

もちろん通信プロトコルを設計する際にはバイトアライメントを考慮する必要がありますので、ここでは詳しく説明しませんので、C /C ++のデータを参考にしてください。

Copyright © Windowsの知識 All Rights Reserved