プロセス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))







