【问题标题】:Stack of process 0 in Linux kernel 0.11Linux内核0.11中进程0的堆栈
【发布时间】:2020-04-24 02:33:39
【问题描述】:

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

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->sstss->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 陷入内核模式,它会推送ssesp、@987654340 @、cseip 到内核堆栈。如果用户态栈=内核态栈,int 80推送的值会和用户态推送的值重叠。
  • 可能。但是任务0 在用户空间中有什么作用吗?
  • 它调用fork()生成p1,然后无限循环调用pause()。这两个系统调用是不使用用户堆栈的内联函数。所以这对 p0 来说可能不是问题。但是fork()之后,好像p1的用户栈=p0的用户栈=p0的内核栈。我知道这是针对 COW 的,但 p0 的堆栈位于内核的数据段中,并且位于不支持 COW 的 LOW_MEMORY 之下。而且 p1 确实有很多工作要做。

标签: c linux assembly linux-kernel x86


【解决方案1】:

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要使用这个空间时,会触发一个page fault,然后针对这个空间触发COW,即内核会为p1的栈分配一个新的page。

结论:

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

  2. p0 不使用其用户堆栈。

  3. p1 想要写入堆栈时会在此堆栈空间上触发 COW。

【讨论】:

    猜你喜欢
    • 2015-06-26
    • 2016-04-05
    • 2017-09-19
    • 1970-01-01
    • 2011-12-22
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多