英文:
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->ss
和tss->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不应该再使用这个空间(这就是为什么后续的fork
和pause
是内联函数),因为当p0调用fork
生成p1时,p1的用户栈也指向这个空间。这个空间在p1的页表中被设置为只读。当p1想要使用这个空间时,它会触发一个页面故障,然后触发对这个空间的COW,即内核将为p1的栈分配一个新页面。
结论:
-
p0的用户栈 = 在
fork
之后的p1的用户栈。 -
p0不使用自己的用户栈。
-
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:
-
p0's user stack = p1's user stack right after
fork
. -
p0 does not use its user stack.
-
p1 will trigger COW on this stack space when it wants to write to the stack.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论