Linux内核0.11中的进程0堆栈

huangapple go评论72阅读模式
英文:

Stack of process 0 in Linux kernel 0.11

问题

我目前正在学习Linux内核源代码(v0.11)。以下是init/main.c中的main()函数:

void main(void)
{
	...
	move_to_user_mode();
	if (!fork()) {		/* 我们依赖这个操作能成功执行 */
		init();
	}

move_to_user_mode函数中,进程0通过以下方式进入用户模式:

#define move_to_user_mode() \
__asm__ ("movl %%esp,%%eax\n\t" \
	"pushl $0x17\n\t" \
	"pushl %%eax\n\t" \
	"pushfl\n\t" \
	"pushl $0x0f\n\t" \
	"pushl $1f\n\t" \
	"iret\n" \
	"1:\tmovl $0x17,%%eax\n\t" \
	"movw %%ax,%%ds\n\t" \
	"movw %%ax,%%es\n\t" \
	"movw %%ax,%%fs\n\t" \
	"movw %%ax,%%gs" \
	:::"ax")

iret之后,似乎用户模式的ss:esp指向与内核模式相同的堆栈。也就是说,进程0的用户堆栈=进程0的内核堆栈。这是正确的吗?

当进程0调用fork时,它调用copy_process,将其用户模式的ss:esp复制到进程1的tss->sstss->esp中。那么进程1会与进程0共享相同的用户模式堆栈吗?如果是这样,进程1的用户堆栈=进程0的用户堆栈=进程0的内核堆栈。这会引发任何问题吗?

copy_process如下:

int copy_process(int nr, long ebp, long edi, long esi, long gs, long none,
		long ebx, long ecx, long edx,
		long fs, long es, long ds,
		long eip, long cs, long eflags, long esp, long ss)
{
	...
	p->tss.esp = esp;
	...
	p->tss.ss = ss & 0xffff;
	...
}

附带说明:进程0的内核堆栈位于LOW_MEMORY以下,这意味着不支持写时复制(COW)。

【翻译完毕】

英文:

I'm currently learning linux kernel source code (v0.11). The following is the main() function of init/main.c:

void main(void)	
{
	...
	move_to_user_mode();
	if (!fork()) {		/* we count on this going ok */
		init();
	}

where in move_to_user_mode, process 0 goes to user mode by doing this:

#define move_to_user_mode() \
__asm__ ("movl %%esp,%%eax\n\t" \
	"pushl $0x17\n\t" \
	"pushl %%eax\n\t" \
	"pushfl\n\t" \
	"pushl $0x0f\n\t" \
	"pushl $1f\n\t" \
	"iret\n" \
	"1:\tmovl $0x17,%%eax\n\t" \
	"movw %%ax,%%ds\n\t" \
	"movw %%ax,%%es\n\t" \
	"movw %%ax,%%fs\n\t" \
	"movw %%ax,%%gs" \
	:::"ax")

After iret, it seems like that the user mode ss:esp points to the same stack as it is in kernel mode. i.e. p0's user stack = p0's kernel stack. Is this true?

When p0 calls fork, it calls copy_process, which copies its user mode ss:esp to p1's tss->ss and tss->esp. So will p1 share the same user mode stack as p0? If so, p1's user stack = p0's user stack = p0's kernel stack. Will this cause any problem?

The copy_process is as following:

int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
		long ebx,long ecx,long edx,
		long fs,long es,long ds,
		long eip,long cs,long eflags,long esp,long ss)
{
	...
	p->tss.esp = esp;
	...
	p->tss.ss = ss & 0xffff;
	...
}

P.S. p0's kernel stack is below LOW_MEMORY, which means it does not support COW.

答案1

得分: 3

p0的用户栈是在kernel/sched.c中定义的user_stack,这与在move_to_user_mode之前使用的栈相同,也就是move_to_user_mode中推送的esp的值。在move_to_user_mode之后,p0不应该再使用这个空间(这就是为什么后续的forkpause是内联函数),因为当p0调用fork生成p1时,p1的用户栈也指向这个空间。这个空间在p1的页表中被设置为只读。当p1想要使用这个空间时,它会触发一个页面故障,然后触发对这个空间的COW,即内核将为p1的栈分配一个新页面。

结论:

  1. p0的用户栈 = 在fork之后的p1的用户栈。

  2. p0不使用自己的用户栈。

  3. p1在想要写入栈时会触发对这个栈空间的COW。

英文:

p0's user stack is the user_stack defined in kernel/sched.c, which is the same stack used before move_to_user_mode, and which is the value of the pushed esp in move_to_user_mode . And after move_to_user_mode, p0 should not use this space (this is why the following fork and pause are inline function) since p1's user stack also points to this space when p0 calls fork to produce p1. This space is set to read-only in p1's page table. When p1 wants to use this space, it will trigger a page fault, and then trigger COW for this space, i.e. the kernel will allocate a new page for p1's stack.

Conclusion:

  1. p0's user stack = p1's user stack right after fork.

  2. p0 does not use its user stack.

  3. p1 will trigger COW on this stack space when it wants to write to the stack.

huangapple
  • 本文由 发表于 2020年1月6日 22:37:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/59613963.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定