プロセスIDの取得


プロセスIDはstruct task_struct current->pidに設定され、init_taskにリストされるプロセスを走査することでプロセスを取得する事が可能ですが、走査効率と、ネームスペース毎にプロセスIDを有している故に、 current->pid/current->nsproxy->pid_nsをキーとするハッシュテーブルにリストからstruct pidを取得し、そのstruct hlist_head tasks[]リストからプロセスを取得します。

pid/upid/pid_namespaceの下の3つのstructで管理されます。ネームスペースは親子階層となり、プロセスはそれぞれのネームスペース下で管理され、その数分プロセスIDを有する事になります。pid.levelはこのネームスペース数で、numbers[]はカレントネームスペースを配列0とし、各配列が各親ネームスペースのuidとなります。

struct pid_namespace *nsは、このプロセスIDのネームスペースで、upid.nrがプロセスIDです。struct pid_namespaceのpidmapでプロセスIDをビットマップで管理します。

struct hlist_node pid_chainはnr/nsをハッシュキーとするハッシュテーブルpid_hash[]にリストするノードです。
struct pid
{
       atomic_t count;
       unsigned int level;
       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;
};

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;
};
copy_process()で親プロセスを複写して新規プロセスを作成します。この時、alloc_pid()をコールし、struct pidを取得し、pid_nr()でpid.numbers[0].upid.nrをプロセスIDとtask_structのpidに設定します。

hlist_add_head_rcu()でpid_hashfn(upid->nr, upid->ns)をハッシュキーとして、pid_hash[]にupid->pid_chainをリストします。このハッシュテーブルからupidを取得すれば、 container_of(upid, struct pid, numbers[ネームスペースのlevel])で、struct pidが取得できます。find_pid_ns()参照
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;
}
pid_ns->last_pidは前回取得したプロセスIDで、それをインクリメントした値がプロセスIDとなります。pid_maxはCONFIG_BASE_SMALLカーネルオプションにより、0x1000 : 0x8000です。

pid_ns->pidmap[]はpage単位で管理する配列で、pid/BITS_PER_PAGEでpid値におけるpidmap[]の配列位置で、offsetがpageのビット位置です。その配列にpageが割り当てられていないなら、pageを割り当てます。

pidmap[]のoffsetからfind_next_offset()で順に、空きビット位置を取得し、取得できなければ次の配列pidmap[]から取得する事を繰り返します。
#define PIDMAP_ENTRIES         ((PID_MAX_LIMIT + 8*PAGE_SIZE - 1)/PAGE_SIZE/8)
#define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000)

int pid_max = PID_MAX_DEFAULT;

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;
}

static inline int mk_pid(struct pid_namespace *pid_ns,
               struct pidmap *map, int off)
{
       return (map - pid_ns->pidmap)*BITS_PER_PAGE + off;
}
pid_nr()でpid->numbers[0].nrをプロセスIDとして取得します。pid->numbers[]はカレントネームスペースから親ネームスペースに遡ってプロセスIDを取得しますので、配列0のnumber[]がプロセスのネームスペース下のIDとなります。
static inline pid_t pid_nr(struct pid *pid)
{
       pid_t nr = 0;
       if (pid)
               nr = pid->numbers[0].nr;
       return nr;
}
find_pid_ns()はプロセスIDおよびネームスペースからcontainer_ofマクロで、struct pidを取得します。
struct pid *find_pid_ns(int nr, struct pid_namespace *ns)
{
       struct hlist_node *elem;
       struct upid *pnr;

       hlist_for_each_entry_rcu(pnr, elem,
                       &pid_hash[pid_hashfn(nr, ns)], pid_chain)
               if (pnr->nr == nr && pnr->ns == ns)
                       return container_of(pnr, struct pid,
                                       numbers[ns->level]);

       return NULL;
}

追記

current->pidをプロセスIDとして処理するネームスペースが異なるプロセスからのシステムコールは対応できないと言うことです。

最終更新 2015/06/06 23:42:58 - north
(2011/05/15 05:25:23 作成)


検索

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