Стек процесса 0 в Linux ядре 0.11 - PullRequest
4 голосов
/ 06 января 2020

Я сейчас изучаю linux исходный код ядра (v0.11). Ниже приводится main() функция init/main.c:

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's 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;
    ...
}

Стек ядра PS p0 находится ниже LOW_MEMORY, что означает, что он не поддерживает COW.

1 Ответ

3 голосов
/ 07 января 2020
Пользовательский стек

p0 - это user_stack, определенный в kernel/sched.c, который является тем же стеком, который использовался до move_to_user_mode, и который является значением вставленного esp в move_to_user_mode. А после move_to_user_mode p0 не должен использовать это пространство (вот почему следующие fork и pause являются встроенными функциями), поскольку стек пользователя p1 также указывает на это пространство, когда p0 вызывает fork для получения p1. Это пространство установлено только для чтения в таблице страниц p1. Когда p1 захочет использовать это пространство, он вызовет сбой страницы, а затем запустит COW для этого пространства, то есть ядро ​​выделит новую страницу для стека p1.

Вывод:

  1. пользовательский стек p0 = пользовательский стек p1 сразу после fork.

  2. p0 не использует свой пользовательский стек.

  3. p1 запустит COW в этом стековом пространстве, когда захочет записать в стек.

...