NetBSD-current /sbin/initが起動されるまで(5)

forkされたプロセスがどう動くのか見てみる。

コンテキストスイッチが起きると、mi_switch()が呼ばれる。

	/* Pick new LWP to run. */
	if (newl == NULL) {
		newl = nextlwp(ci, spc);
	}

nextlwp()によってforkされたプロセスが次のプロセス(newl)に選ばれる。

細かい処理を経て、最終的にアーキテクチャ依存のコンテキスト切り替え関数が呼ばれる。

		/* Switch to the new LWP.. */
		prevlwp = cpu_switchto(l, newl, returning);

cpu_switchto()はMIPSアーキテクチャではarch/mips/mips/locore.Sにある。

	lw	a0, L_ADDR(MIPS_CURLWP)
	move	v0, s6				# Save return value
	REG_PROLOGUE
	REG_L	t0, U_PCB_CONTEXT+SF_REG_SR(a0)
	DYNAMIC_STATUS_MASK(t0,ra)		# machine dependent masking
	REG_L	ra, U_PCB_CONTEXT+SF_REG_RA(a0)
	REG_L	s0, U_PCB_CONTEXT+SF_REG_S0(a0)
	REG_L	s1, U_PCB_CONTEXT+SF_REG_S1(a0)
	REG_L	s2, U_PCB_CONTEXT+SF_REG_S2(a0)
	REG_L	s3, U_PCB_CONTEXT+SF_REG_S3(a0)
	REG_L	s4, U_PCB_CONTEXT+SF_REG_S4(a0)
	REG_L	s5, U_PCB_CONTEXT+SF_REG_S5(a0)
	REG_L	s6, U_PCB_CONTEXT+SF_REG_S6(a0)
	REG_L	s7, U_PCB_CONTEXT+SF_REG_S7(a0)
	REG_L	sp, U_PCB_CONTEXT+SF_REG_SP(a0)
	REG_L	s8, U_PCB_CONTEXT+SF_REG_S8(a0)
	REG_EPILOGUE
	mtc0	t0, MIPS_COP_0_STATUS
	COP0_SYNC
	j	ra
	nop
END(cpu_switchto)

単にコンテキストスイッチをやって、最後にはraへジャンプする。

cpu_lwp_fork()では、RAにlwp_trampolineのアドレスを設定していた。

なので、lwp_trampolineが実行されるはずだから、次はlwp_trampolineを見てみる。

/*
 * mipsN_lwp_trampoline()
 *
 * Arrange for a function to be invoked neatly, after a cpu_switch().
 * Call the service function with one argument, specified by the s0
 * and s1 respectively.  There is no need register save operation.
 */
LEAF(MIPSX(lwp_trampoline))
	addu	sp, sp, -CALLFRAME_SIZ

	# Call lwp_startup(), with args from cpu_switchto()/cpu_setfunc()
	la	t0, _C_LABEL(lwp_startup)
	move	a0, v0
	jal	ra, t0
	move	a1, s7

a0 = v0, a1 = s7を代入してlwp_startupを起動している。
cpu_lwp_fork()でs7にl2を設定していたのは確かなのだが、v0にl1を設定したのはどこでなのだろうか?

/*
 * Called by MD code when a new LWP begins execution.  Must be called
 * with the previous LWP locked (so at splsched), or if there is no
 * previous LWP, at splsched.
 */
void
lwp_startup(struct lwp *prev, struct lwp *new)
{

	KASSERT(kpreempt_disabled());
	if (prev != NULL) {
		/*
		 * Normalize the count of the spin-mutexes, it was
		 * increased in mi_switch().  Unmark the state of
		 * context switch - it is finished for previous LWP.
		 */
		curcpu()->ci_mtx_count++;
		membar_exit();
		prev->l_ctxswtch = 0;
	}
	KPREEMPT_DISABLE(new);
	spl0();
	pmap_activate(new);
	LOCKDEBUG_BARRIER(NULL, 0);
	KPREEMPT_ENABLE(new);
	if ((new->l_pflag & LP_MPSAFE) == 0) {
		KERNEL_LOCK(1, new);
	}
}

良くわからないがロック周りの処理に見える。

	# Call the routine specified by cpu_setfunc()
	jal	ra, s0			
	move	a0, s1

cpu_lwp_fork()で指定されたfuncを実行している。
終わるとここに戻ってくる。

	#
	# Return to user (won't happen if a kernel thread)
	#
	# Make sure to disable interrupts here, as otherwise
	# we can take an interrupt *after* EXL is set, and
	# end up returning to a bogus PC since the PC is not
	# saved if EXL=1.
	#
	.set	noat
	mtc0	zero, MIPS_COP_0_STATUS		# disable int
	COP0_SYNC
	nop					# 3 op delay
	nop
	nop
	li	a0, MIPS_SR_EXL			# set exception level
	mtc0	a0, MIPS_COP_0_STATUS
	COP0_SYNC
	nop
	nop
	move	a1, sp
	addu	a1, sp, CALLFRAME_SIZ
 #	REG_L	a0, FRAME_SR(a1)
	REG_L	t0, FRAME_MULLO(a1)
	REG_L	t1, FRAME_MULHI(a1)
	REG_L	v0, FRAME_EPC(a1)
	mtlo	t0
	mthi	t1
	_MTC0	v0, MIPS_COP_0_EXC_PC
	COP0_SYNC
	nop
	move	k1, a1
#ifdef IPL_ICU_MASK
	.set at
	lw	t0, FRAME_PPL(k1)
	sw	t0, _C_LABEL(md_imask)
	jal	_C_LABEL(md_imask_update)
	nop
	.set noat
#endif
	
	REG_L	AT, FRAME_AST(k1)
	REG_L	v0, FRAME_V0(k1)
	REG_L	v1, FRAME_V1(k1)
	REG_L	a0, FRAME_A0(k1)
	REG_L	a1, FRAME_A1(k1)
	REG_L	a2, FRAME_A2(k1)
	REG_L	a3, FRAME_A3(k1)
	REG_L	t0, FRAME_T0(k1)
	REG_L	t1, FRAME_T1(k1)
	REG_L	t2, FRAME_T2(k1)
	REG_L	t3, FRAME_T3(k1)
	REG_L	ta0, FRAME_TA0(k1)
	REG_L	ta1, FRAME_TA1(k1)
	REG_L	ta2, FRAME_TA2(k1)
	REG_L	ta3, FRAME_TA3(k1)
	REG_L	s0, FRAME_S0(k1)
	REG_L	s1, FRAME_S1(k1)
	REG_L	s2, FRAME_S2(k1)
	REG_L	s3, FRAME_S3(k1)
	REG_L	s4, FRAME_S4(k1)
	REG_L	s5, FRAME_S5(k1)
	REG_L	s6, FRAME_S6(k1)
	REG_L	s7, FRAME_S7(k1)
	REG_L	t8, FRAME_T8(k1)
	REG_L	t9, FRAME_T9(k1)
	REG_L	k0, FRAME_SR(k1)
	DYNAMIC_STATUS_MASK(k0, sp)		# machine dependent masking
	REG_L	gp, FRAME_GP(k1)
	REG_L	s8, FRAME_S8(k1)
	REG_L	ra, FRAME_RA(k1)
	REG_L	sp, FRAME_SP(k1)
	mtc0	k0, MIPS_COP_0_STATUS
	COP0_SYNC
	nop
	nop
	eret
	.set	at
END(MIPSX(lwp_trampoline))

スタックに積んであるフレームをリストアしてスイッチしている。
さて、このフレームには何が乗っているのか?

cpu_lwp_fork()を読み返してみると、親プロセスのPCBをスタックフレームにコピーしている。
という事は、fork()を呼んだ時点へ戻るはず。