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


最終更新 2013/08/21 15:46:32 - north
(2011/05/15 05:25:23 作成)


検索

アクセス数
3134030
最近のコメント
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
Adsense
広告情報が設定されていません。