5

私はいくつかのカーネル コードを研究しており、データ構造がどのように相互にリンクされているかを理解しようとしています。スケジューラがどのように機能するか、および PID とは何かについての基本的な考え方を知っています。しかし、このコンテキストで名前空間が何であるかはわかりません。また、それらすべてがどのように連携するかを理解することもできません。

いくつかの説明 (O'Reilly の「Understanding the Linux Kernel」の一部を含む) を読み、1 つのプロセスが終了して ID が再割り当てされたため、同じ PID が 2 つのプロセスに到達した可能性があることを理解しました。しかし、これがどのように行われるのかわかりません。

そう:

  1. このコンテキストでの名前空間とは何ですか?
  2. task_structとの関係はpid_namespace?(私はすでにそれが関係していると考えていましたがpid_t、方法がわかりません)

参考文献:

4

1 に答える 1

12

おそらく、これらのリンクが役立つかもしれません:

  1. 運用中の PID 名前空間
  2. PID 名前空間の簡単な紹介 (これはシステム管理者からのものです)

2 番目のリンクをたどると、名前空間がリソースを分離する優れた方法であることが明らかになります。Linux を含むどの OS でも、プロセスは存在する最も重要なリソースの 1 つです。彼自身の言葉で

はい、それだけです。この名前空間を使用すると、PID の番号付けを再開して、独自の「1」プロセスを取得できます。これは、プロセス識別子ツリーの「chroot」と見なすことができます。日々の仕事で pid を処理する必要があり、4 桁の数字に悩まされている場合に非常に便利です…</p>

したがって、独自のプライベート プロセス ツリーを作成し、それを特定のユーザーや特定のタスクに割り当てます。このツリー内では、プロセスは PID がこの「コンテナ」の外部のものと競合することを心配する必要はありません。したがって、このツリーを別の「ルート」ユーザーに完全に引き渡すのと同じくらい良いことです。そのすばらしい仲間は、すばらしい例を挙げて物事を説明する素晴らしい仕事をしてくれたので、ここでは繰り返しません。

カーネルに関する限り、私はあなたが始めるためのいくつかの指針を与えることができます. 私はここの専門家ではありませんが、これがある程度役立つことを願っています。

この LWN記事では、PID の古い見方と新しい見方について説明しています。それ自身の言葉で:

タスクが持つ可能性のあるすべての PID については、 に説明がありstruct pidます。この構造には、ID 値、この ID を持つタスクのリスト、参照カウンター、および検索を高速化するためにハッシュ テーブルに格納されるハッシュ リスト ノードが含まれます。タスクのリストについてもう少し説明します。基本的に、タスクには、プロセス ID (PID)、プロセス グループ ID (PGID)、およびセッション ID (SID) の 3 つの PID があります。PGID と SID はタスク間で共有される場合があります。たとえば、2 つ以上のタスクが同じグループに属し、各グループ ID が複数のタスクをアドレス指定する場合などです。PID 名前空間により、この構造は柔軟になります。現在、各 PID には複数の値があり、それぞれが 1 つの名前空間で有効です。つまり、ある名前空間ではタスクの PID が 1024 で、別の名前空間では 256 である可能性があります。したがって、前者struct pidは変更されます。これが方法です struct pidPID 名前空間を導入する前は、次のように見えました。

struct pid {
 atomic_t count;                          /* reference counter */
 int nr;                                  /* the pid value */
 struct hlist_node pid_chain;             /* hash chain */
 struct hlist_head tasks[PIDTYPE_MAX];    /* lists of tasks */
 struct rcu_head rcu;                     /* RCU helper */
};

そして、これが今の様子です:

struct upid {
   int nr;                            /* moved from struct pid */
   struct pid_namespace *ns;          /* the namespace this value
                                       * is visible in */
   struct hlist_node pid_chain;       /* moved from struct pid */
};

struct pid {
   atomic_t count;
   struct hlist_head tasks[PIDTYPE_MAX];
   struct rcu_head rcu;
   int level;                     /* the number of upids */
   struct upid numbers[0];
};

ご覧のとおり、struct upid は PID 値を表しています。これはハッシュに格納され、PID 値を持っています。struct pidをPID に、またはその逆に 変換するにはtask_pid_nr()pid_nr_ns()find_task_by_vpid()、 などの一連のヘルパーを使用できます。

少し古くなっていますが、この情報はあなたが始めるのに十分公平です. ここで言及する必要があるもう 1 つの重要な構造があります。ですstruct nsproxy。この構造は、名前空間が関連付けられているプロセスと比較して、すべての名前空間の焦点です。このプロセスの子プロセスが使用する PID 名前空間へのポインタが含まれています。現在のプロセスの PID 名前空間は、task_active_pid_ns.

struct task_structには、適切に呼ばれる名前空間プロキシ ポインタがあり、nsproxyこのプロセスのstruct nsproxy構造を指します。task_struct新しいプロセスを作成するために必要な手順をたどると、struct nsproxyとの間の関係を見つけることができますstruct pid

Linux の新しいプロセスは常に既存のプロセスからフォークされ、後でexecve(または exec ファミリーの同様の関数) を使用してイメージが置き換えられます。したがって、の一部としてdo_forkcopy_processが呼び出されます。

親プロセスのコピーの一部として、次の重要なことが起こります。

  1. task_structを使用して最初に複製されdup_task_structます。
  2. 親プロセスの名前空間も を使用してコピーされcopy_namespacesます。これにより、子の新しいnsproxy構造も作成され、nsproxy ポインターはこの新しく作成された構造を指します。
  3. 非 INIT プロセス (元のグローバル PID別名ブート時に生成された最初のプロセス) の場合、PID構造体が割り当てられます。alloc_pidこれにより、新しく作成されたプロセスに新しい PID 構造体が実際に割り当てられforkます。この関数の短いスニペット:

    nr = alloc_pidmap(tmp);
    if(nr<0)
       goto out_free;
    pid->numbers[i].nr = nr;
    pid->numbers[i].ns = tmp;
    

これupidにより、構造体に新しい PID と現在属している名前空間が与えられ、構造体にデータが入力されます。

さらにcopy process関数の一部として、この新しく割り当てられた PID は、対応するtask_structvia 関数にリンクされます。pid_nrつまり、そのグローバル ID (INIT 名前空間から見た元の PID nr) が のフィールドpidに格納されtask_structます。

の最終段階で、とこの新しい構造の間に、フィールドを介して 関数 を介してcopy_processリンクが確立されます。task_structpidpid_linktask_structattach_pid

まだまだたくさんありますが、これで少なくとも有利なスタートが切れることを願っています。

注: 私は最新の (現時点での) カーネル バージョンを参照しています。3.17.2.

于 2014-11-10T06:02:17.510 に答える