読者です 読者をやめる 読者になる 読者になる

create_pid_namespace()の処理を読む。

linux kernel

最近はfork/clone時のプロセス番号の発行あたりを読んできたので、今回はPID名前空間の生成部分を読んでみます。

処理を行うのはcreate_pid_namespace()です。clone(2)の場合、ここに至る流れはこうなっています。

do_fork()
 -> copy_process()
   -> copy_namespaces()
     -> create_new_namespaces()
       -> copy_pid_ns()
         -> create_pid_namespace()

PID名前空間が作成されるのは、PID名前空間を既存の名前空間から分離するときです。なので、copy_namespaces()にきて、CLONE_NEWPIPDフラグが立っているのでNSProxyの参照数を増やすのではなくてcreate_new_namespaces()でNSProxyの新規作成に入ります。そして、pid名前空間を処理するcopy_pid_ns()を呼んで、CLONE_NEWPIDフラグが立っているのでPID名前空間の参照数を増やさずにcreate_pid_namespace()を呼んで名前空間の新規作成に入るという感じです。

最初に既存のpid名前空間の階層を1つ足した値をlevelに設定します。そして、pid名前空間の階層数が限度を超えたらエラーとなります。

 82 static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns,
 83         struct pid_namespace *parent_pid_ns)
 84 {
 85         struct pid_namespace *ns;
 86         unsigned int level = parent_pid_ns->level + 1;
 87         int i;
 88         int err;
 89
 90         if (level > MAX_PID_NS_LEVEL) {
 91                 err = -EINVAL;
 92                 goto out;
 93         }
 94

MAX_PID_NS_LEVELはcreate_pid_namespace()のすぐ上で定義されていて、32段階まで可能となっています。

 79 /* MAX_PID_NS_LEVEL is needed for limiting size of 'struct pid' */
 80 #define MAX_PID_NS_LEVEL 32

ここは単にpid_namespace構造体のメモリを確保するだけです。

 95         err = -ENOMEM;
 96         ns = kmem_cache_zalloc(pid_ns_cachep, GFP_KERNEL);
 97         if (ns == NULL)
 98                 goto out;
 99 

pid_namespace構造体はこのような構造体です。

 24 struct pid_namespace {
 25         struct kref kref;
 26         struct pidmap pidmap[PIDMAP_ENTRIES];
 27         struct rcu_head rcu;
 28         int last_pid;
 29         unsigned int nr_hashed;
 30         struct task_struct *child_reaper;
 31         struct kmem_cache *pid_cachep;
 32         unsigned int level;
 33         struct pid_namespace *parent;
 34 #ifdef CONFIG_PROC_FS
 35         struct vfsmount *proc_mnt;
 36         struct dentry *proc_self;
 37         struct dentry *proc_thread_self;
 38 #endif
 39 #ifdef CONFIG_BSD_PROCESS_ACCT
 40         struct fs_pin *bacct;
 41 #endif
 42         struct user_namespace *user_ns;
 43         struct work_struct proc_work;
 44         kgid_t pid_gid;
 45         int hide_pid;
 46         int reboot;     /* group exit code if this pidns was rebooted */
 47         struct ns_common ns;
 48 };

次はpidを管理するビットマップに使う領域のメモリ確保です。サイズは1PAGE分です。

100         ns->pidmap[0].page = kzalloc(PAGE_SIZE, GFP_KERNEL);
101         if (!ns->pidmap[0].page)
102                 goto out_free;
103

pidmapはpidmap構造体で空いている数を管理するnr_freeと、pageの2変数があります。

 13 struct pidmap {
 14        atomic_t nr_free;
 15        void *page;
 16 };

ここでpid構造体用にslabキャッシュを作ります。この中の処理は前回のfork/clone時に返すpidの設定(2) - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモで軽く見たところです。ここで渡したレベルが、、、

104         ns->pid_cachep = create_pid_cachep(level + 1);
105         if (ns->pid_cachep == NULL)
106                 goto out_free_map;
107 

このように使われているわけですね。

 53         cachep = kmem_cache_create(pcache->name,
 54                         sizeof(struct pid) + (nr_ids - 1) * sizeof(struct upid),
 55                         0, SLAB_HWCACHE_ALIGN, NULL);

次の処理はns_alloc_inum()で/proc/$$/ns/pidファイルに使用するinode番号を取得します。そして、proc_ns_operations構造体にpid名前空間のオペレーションを定義した構造体を設定します。これを使うのはsetns(2)で名前空間の移動を行うときです。

108         err = ns_alloc_inum(&ns->ns);
109         if (err)
110                 goto out_free_map;
111         ns->ns.ops = &pidns_operations;
112 

pidns_operations構造体は以下のように初期化されています。

391 const struct proc_ns_operations pidns_operations = {
392         .name           = "pid",
393         .type           = CLONE_NEWPID,
394         .get            = pidns_get,
395         .put            = pidns_put,
396         .install        = pidns_install,
397 };

残りは初期化とエラー時のgotoラベルくらいなので読み飛ばします。最後に作成したpid_namespace構造体を返して終了です。

113         kref_init(&ns->kref);
114         ns->level = level;
115         ns->parent = get_pid_ns(parent_pid_ns);
116         ns->user_ns = get_user_ns(user_ns);
117         ns->nr_hashed = PIDNS_HASH_ADDING;
118         INIT_WORK(&ns->proc_work, proc_cleanup_work);
119 
120         set_bit(0, ns->pidmap[0].page);
121         atomic_set(&ns->pidmap[0].nr_free, BITS_PER_PAGE - 1);
122 
123         for (i = 1; i < PIDMAP_ENTRIES; i++)
124                 atomic_set(&ns->pidmap[i].nr_free, BITS_PER_PAGE);
125 
126         return ns;
127 
128 out_free_map:
129         kfree(ns->pidmap[0].page);
130 out_free:
131         kmem_cache_free(pid_ns_cachep, ns);
132 out:
133         return ERR_PTR(err);
134 }

これでわかったビットコイン

これでわかったビットコイン