【发布时间】:2020-04-24 02:33:39
【问题描述】:
我目前正在学习 linux 内核源代码 (v0.11)。以下是init/main.c的main()函数:
void main(void)
{
...
move_to_user_mode();
if (!fork()) { /* we count on this going ok */
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 似乎指向与内核模式相同的堆栈。即 p0 的用户堆栈 = p0 的内核堆栈。这是真的吗?
当 p0 调用fork 时,它会调用copy_process,这会将其用户模式ss:esp 复制到p1 的tss->ss 和tss->esp。那么 p1 会与 p0 共享相同的用户模式堆栈吗?如果是这样,p1 的用户堆栈 = p0 的用户堆栈 = p0 的内核堆栈。这会引起什么问题吗?
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;
...
}
附: p0 的内核栈低于 LOW_MEMORY,表示不支持 COW。
【问题讨论】:
-
ss:esp指向与内核模式相同的堆栈 相同的虚拟地址,是的。而且由于没有将 CR3 更改为一组不同的页表,是的,那是相同的堆栈内存。iret将触发 TSS 任务切换的东西,尽管这不会为您更改 CR3。所以我认为这基本上是从内核到用户模式的这个任务,就像宏名称所表明的那样。大概是内存映射,所以用户空间可以访问它。 -
谢谢@Peter。但是如果用户态堆栈与内核态堆栈共享相同的内存,这可能会导致内存使用冲突。假设进程处于用户模式并且在其堆栈中有一些有用的东西(例如,它在函数的堆栈框架中),那么它使用
int 80陷入内核模式,它会推送ss、esp、@987654340 @、cs和eip到内核堆栈。如果用户态栈=内核态栈,int 80推送的值会和用户态推送的值重叠。 -
可能。但是任务
0在用户空间中有什么作用吗? -
它调用
fork()生成p1,然后无限循环调用pause()。这两个系统调用是不使用用户堆栈的内联函数。所以这对 p0 来说可能不是问题。但是fork()之后,好像p1的用户栈=p0的用户栈=p0的内核栈。我知道这是针对 COW 的,但 p0 的堆栈位于内核的数据段中,并且位于不支持 COW 的 LOW_MEMORY 之下。而且 p1 确实有很多工作要做。
标签: c linux assembly linux-kernel x86