プロセスIDの取得
Rev.2を表示中。最新版はこちら。
子プロセス作成で、forkからcopy_process()で親のプロセスの複製し、引数をそのプロセスのPIDネームスペースで、alloc_pid()をコールしてpidを取得し、作成したプロセスにPIDを割り当てます。このように、pidはPIDネームスペース単位で管理されます。なお、通常処理では、引数pid=NULLでコールされます。static struct task_struct *copy_process(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *child_tidptr, struct pid *pid, int trace) { int retval; struct task_struct *p; int cgroup_callbacks_done = 0; : retval = -ENOMEM; p = dup_task_struct(current); if (!p) goto fork_out; : retval = copy_namespaces(clone_flags, p); if (retval) goto bad_fork_cleanup_mm; : if (pid != &init_struct_pid) { retval = -ENOMEM; pid = alloc_pid(p->nsproxy->pid_ns); if (!pid) goto bad_fork_cleanup_io; } : p->pid = pid_nr(pid); p->tgid = p->pid; if (clone_flags & CLONE_THREAD) p->tgid = current->tgid; : }スラブからstruct pidを確保し、ns->level分、nsのインデックスとして、alloc_pidmap()でns下でのpidを取得します。ns->levelのデフォルトは0(initプロセスのnsを継承)ですが、copy_namespaces()でCLONE_NEWNAME時、新規ネームスペースを割り当て、ns->parentに親のネームスペースを、そしてns->levelをインクリメントします。なおデフォルトは0です。
pid->tasks[PIDTYPE_PID/PIDTYPE_PGID/PIDTYPE_SID]をhlistのヘッドを初期化します。(かかる子プロセスがこのリストされます。)最後にpid_hash[]にpidとnsをキーとするハッシュテーブルに登録します。
struct pid_namespace { struct kref kref; struct pidmap pidmap[PIDMAP_ENTRIES]; int last_pid; struct task_struct *child_reaper; struct kmem_cache *pid_cachep; unsigned int level; struct pid_namespace *parent; #ifdef CONFIG_PROC_FS struct vfsmount *proc_mnt; #endif #ifdef CONFIG_BSD_PROCESS_ACCT struct bsd_acct_struct *bacct; #endif gid_t pid_gid; int hide_pid; }; struct pidmap { atomic_t nr_free; void *page; }; struct pid { atomic_t count; unsigned int level; /* lists of tasks that use this pid */ struct hlist_head tasks[PIDTYPE_MAX]; struct rcu_head rcu; struct upid numbers[1]; }; struct upid { int nr; struct pid_namespace *ns; struct hlist_node pid_chain; }; enum pid_type { PIDTYPE_PID, PIDTYPE_PGID, PIDTYPE_SID, PIDTYPE_MAX }; struct pid *alloc_pid(struct pid_namespace *ns) { struct pid *pid; enum pid_type type; int i, nr; struct pid_namespace *tmp; struct upid *upid; pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL); if (!pid) goto out; tmp = ns; for (i = ns->level; i >= 0; i--) { nr = alloc_pidmap(tmp); if (nr < 0) goto out_free; pid->numbers[i].nr = nr; pid->numbers[i].ns = tmp; tmp = tmp->parent; } get_pid_ns(ns); pid->level = ns->level; atomic_set(&pid->count, 1); for (type = 0; type < PIDTYPE_MAX; ++type) INIT_HLIST_HEAD(&pid->tasks[type]); upid = pid->numbers + ns->level; spin_lock_irq(&pidmap_lock); for ( ; upid >= pid->numbers; --upid) hlist_add_head_rcu(&upid->pid_chain, &pid_hash[pid_hashfn(upid->nr, upid->ns)]); spin_unlock_irq(&pidmap_lock); out: return pid; out_free: while (++i <= ns->level) free_pidmap(pid->numbers + i); kmem_cache_free(ns->pid_cachep, pid); pid = NULL; goto out; }alloc_pidmap()で実際のpidを決定します。pid_ns->last_pid+1をpidとすべく、pid_ns->pidmap[pid/BITS_PER_PAGE]のpageマップの対応するbitをチェックします。もし、pageマップが存在しないなら、pageを割り当てます。
そして、該当pageビットマップのオフセット位置から、pageサイズを超えたら次のpageマップへと、セットされていないビット位置(pidに相当)を取得します。
static int alloc_pidmap(struct pid_namespace *pid_ns) { int i, offset, max_scan, pid, last = pid_ns->last_pid; struct pidmap *map; pid = last + 1; if (pid >= pid_max) pid = RESERVED_PIDS; offset = pid & BITS_PER_PAGE_MASK; map = &pid_ns->pidmap[pid/BITS_PER_PAGE]; max_scan = DIV_ROUND_UP(pid_max, BITS_PER_PAGE) - !offset; for (i = 0; i <= max_scan; ++i) { if (unlikely(!map->page)) { void *page = kzalloc(PAGE_SIZE, GFP_KERNEL); spin_lock_irq(&pidmap_lock); if (!map->page) { map->page = page; page = NULL; } spin_unlock_irq(&pidmap_lock); kfree(page); if (unlikely(!map->page)) break; } if (likely(atomic_read(&map->nr_free))) { do { if (!test_and_set_bit(offset, map->page)) { atomic_dec(&map->nr_free); set_last_pid(pid_ns, last, pid); return pid; } offset = find_next_offset(map, offset); pid = mk_pid(pid_ns, map, offset); } while (offset < BITS_PER_PAGE && pid < pid_max); } if (map < &pid_ns->pidmap[(pid_max-1)/BITS_PER_PAGE]) { ++map; offset = 0; } else { map = &pid_ns->pidmap[0]; offset = RESERVED_PIDS; if (unlikely(last == offset)) break; } pid = mk_pid(pid_ns, map, offset); } return -1; }PIDMAP_ENTRIEは以下のように定義されています。CONFIG_BASE_SMALLはstruct pidのようなカーネルコアデターを小さく確保するためのコンパイルオプションです。CONFIG_BASE_SMALL=1/sizeof(long)=4/PAGE_SIZE=4Kのケースで、2097024となります。この1つづつにpageサイズ分のビットマップがアロケートされるわけで、管理できるpidの総数は2097024×4×1024×8=68715282432。スーパコンピュータのようなスカラー型ハード環境にも耐えうるような設計となっているわけです。
#define PIDMAP_ENTRIES ((PID_MAX_LIMIT + 8*PAGE_SIZE - 1)/PAGE_SIZE/8) #define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000) #define PID_MAX_LIMIT (CONFIG_BASE_SMALL ? PAGE_SIZE * 8 : \ (sizeof(long) > 4 ? 4 * 1024 * 1024 : PID_MAX_DEFAULT))