#写真は前に撮った大崎駅近くからの夕焼け
kernelnewbiesメーリングリストで非SMPマシンでレースコンディションを作りたいと言うスレッドがあって、その中でtask_structの扱いが話題になっていたので調べてみた。
ソースはlxrにある2.6.29を参照しつつ
入り口はsys_clone()だけどsys_clone()は見てもdo_fork()を呼ぶのがメインの仕事なので飛ばして、do_fork()から。
この中で多分ここが主役。
1398 p = copy_process(clone_flags, stack_start, regs, stack_size, 1399 child_tidptr, NULL, trace);
copy_process()に移って、982行目が主要っぽい。
currentとはお馴染みのcurrentマクロ。
982 p = dup_task_struct(current); 983 if (!p) 984 goto fork_out;
dup_task_struct()に移って、229行目のarch_dup_task_struct()がtask_structをコピーしている部分。
211static struct task_struct *dup_task_struct(struct task_struct *orig) 212{ 213 struct task_struct *tsk; 214 struct thread_info *ti; 215 int err; 216 217 prepare_to_copy(orig); 218 219 tsk = alloc_task_struct(); 220 if (!tsk) 221 return NULL; 222 223 ti = alloc_thread_info(tsk); 224 if (!ti) { 225 free_task_struct(tsk); 226 return NULL; 227 } 228 229 err = arch_dup_task_struct(tsk, orig); 230 if (err) 231 goto out; 232
lxrでの検索によると、arch_dup_task_structの実装は2個あって、一つはarch/x86/kernel/process.cでもう一つはkernel/fork.cにある。
arch/x86/kernel/process.cでの実装は・・・
22int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) 23{ 24 *dst = *src; 25 if (src->thread.xstate) { 26 dst->thread.xstate = kmem_cache_alloc(task_xstate_cachep, 27 GFP_KERNEL); 28 if (!dst->thread.xstate) 29 return -ENOMEM; 30 WARN_ON((unsigned long)dst->thread.xstate & 15); 31 memcpy(dst->thread.xstate, src->thread.xstate, xstate_size); 32 } 33 return 0; 34}
kernel/fork.cでの実装は・・・
204int __attribute__((weak)) arch_dup_task_struct(struct task_struct *dst, 205 struct task_struct *src) 206{ 207 *dst = *src; 208 return 0; 209}
いづれの実装も、元のプロセスのtask_structのポインタをコピーしていることには違いが無い。
この時点でtask_structの基本的なコピー処理ができて、その後do_fork()内でclone_flagsに応じた処理が行われる。
1106 /* copy all the process information */ 1107 if ((retval = copy_semundo(clone_flags, p))) 1108 goto bad_fork_cleanup_audit; 1109 if ((retval = copy_files(clone_flags, p))) 1110 goto bad_fork_cleanup_semundo; 1111 if ((retval = copy_fs(clone_flags, p))) 1112 goto bad_fork_cleanup_files; 1113 if ((retval = copy_sighand(clone_flags, p))) 1114 goto bad_fork_cleanup_fs; 1115 if ((retval = copy_signal(clone_flags, p))) 1116 goto bad_fork_cleanup_sighand; 1117 if ((retval = copy_mm(clone_flags, p))) 1118 goto bad_fork_cleanup_signal; 1119 if ((retval = copy_namespaces(clone_flags, p))) 1120 goto bad_fork_cleanup_mm; 1121 if ((retval = copy_io(clone_flags, p)))
これらの処理は基本的にCLONE_XXXフラグが立っていたら参照カウントを増やして、立ってなければコピーするといった処理。
例えば、copy_mm()の場合こんな感じ
655 if (clone_flags & CLONE_VM) { 656 atomic_inc(&oldmm->mm_users); 657 mm = oldmm; 658 goto good_mm; 659 } 660 661 retval = -ENOMEM; 662 mm = dup_mm(tsk); 663 if (!mm) 664 goto fail_nomem;
do_fork()ではcopy_thread()と言う関数でスレッドのコピーもしていた。この関数はアーキテクチャごとに実装がある。
x86_64だとarch/x86/kernel/process_64.c。ちなみにpは新しいtask_struct。
1123 retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
ところで、そもそもdo_forkを調べていたのはCLONE_VMフラグが立ってたらアドレススペースは共有されるよ!っていうのを調べたかっただけなのでいい加減疲れたし、ここで調査終了(´ー`)y─┛~~