プロセス最大数はKernelではどうなっている?

昨日の続き


昨日はユーザランドで値が取得出来た。
sleep sortのアルゴリズムの問題点はプロセス数にかかっている。
なぜならsleep sortは同時にプロセスを起動しないといけないから。
もちろん工夫すれば、プロセス数が少なくても工夫は出来るけどそれはとりあえず置いておく。


さて、昨日の結果で気になる事がある。
メモリサイズ関係もほぼunlimitedになっている。
プロセスはfork()あたりで起動するが、内部的には現在のプロセスをコピーすることになる。
fork()で使用メモリ量を少なくする技術も使われているが、メモリを使う事には代わりが無い。
ということはプロセス数がunlimitedに設定されていても問題があるのは確かだ。
最悪設定なければ、メモリを食いつぶしてkernelまきこんで死ぬんだろうか?


という前置きをしてカーネルではどうなっているか調べてみた。
まずは見るべきはfork()のKernel側のソースだろう。
glibc側で処理する機能では無くkernelの機能を直接使っているはずなので放っておく。


fork()がどこかと言えば、kernel/fork.cに存在する。
関係しそうな所をあげてみよう。

void __init fork_init(unsigned long mempages)
{
#ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR
#ifndef ARCH_MIN_TASKALIGN
#define ARCH_MIN_TASKALIGN	L1_CACHE_BYTES
#endif
	/* create a slab on which task_structs can be allocated */
	task_struct_cachep =
		kmem_cache_create("task_struct", sizeof(struct task_struct),
			ARCH_MIN_TASKALIGN, SLAB_PANIC | SLAB_NOTRACK, NULL);
#endif

	/* do the arch specific task caches init */
	arch_task_cache_init();

	/*
	 * The default maximum number of threads is set to a safe
	 * value: the thread structures can take up at most half
	 * of memory.
	 */
	max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE);

	/*
	 * we need to allow at least 20 threads to boot a system
	 */
	if(max_threads < 20)
		max_threads = 20;

	init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
	init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
	init_task.signal->rlim[RLIMIT_SIGPENDING] =
		init_task.signal->rlim[RLIMIT_NPROC];
}

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)
{
	//省略
	retval = -EAGAIN;
	if (atomic_read(&p->real_cred->user->processes) >=
			task_rlimit(p, RLIMIT_NPROC)) {
		if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
		    p->real_cred->user != INIT_USER)
			goto bad_fork_free;
	}
	//省略
}

long do_fork(unsigned long clone_flags,
	      unsigned long stack_start,
	      struct pt_regs *regs,
	      unsigned long stack_size,
	      int __user *parent_tidptr,
	      int __user *child_tidptr)
{
	//省略
	p = copy_process(clone_flags, stack_start, regs, stack_size,
			 child_tidptr, NULL, trace);
	//省略
}


RLIMIT_NPROCが実際に使われている部分とそれに関係する部分を抜き出してみた。
fork_init()とcopy_process()で使われている。copy_process()はdo_fork()から呼ばれている。
task_rlimit()は単なるデータ取得のinline()関数なので放っておく。
つまり実際重要なのはfork_init()だけ。まあ内容はmax_threads/2に設定しているだけ。
結局unlimitedでもkernelではきちんと制限を儲けていると。
ちなみに、fork_init()はinit/main.cから呼ばれている。つまり、kernelが起動時の初期化関数。


で、fork()は現在はNPTLスレッド実装の一部として提供されているので(manやglibcを参照)、結局スレッド最大数を見れば良いんだとおもう。(未確認)
これは/procで確認できる。

$ cat /proc/sys/kernel/threads-max
63311

まあ、遊びでsleep sortを使う分には問題ない数です。