From de8fc0891754d6307bdd42d1bc7a70464aa2856f Mon Sep 17 00:00:00 2001 From: proninyaroslav Date: Wed, 29 Aug 2018 20:47:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F,=20=D1=81=D0=B2=D1=8F=D0=B7=D0=B0=D0=BD=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=81=20initial=5Fstack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Initialization/linux-initialization-1.md | 37 ++++++++++++++++-------- contributors.md | 1 + 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Initialization/linux-initialization-1.md b/Initialization/linux-initialization-1.md index 05027ce..7d6c258 100644 --- a/Initialization/linux-initialization-1.md +++ b/Initialization/linux-initialization-1.md @@ -357,7 +357,7 @@ popfq ```assembly GLOBAL(initial_stack) - .quad init_thread_union+THREAD_SIZE-8 + .quad init_thread_union + THREAD_SIZE - SIZEOF_PTREGS ``` Макрос `GLOBAL` нам уже знаком. Он определён в файле [arch/x86/include/asm/linkage.h](https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/arch/x86/include/asm/linkage.h) и раскрывается до `глобального` определения символа: @@ -377,7 +377,7 @@ GLOBAL(initial_stack) когда [kasan](http://lxr.free-electrons.com/source/Documentation/kasan.txt) отключён, а `PAGE_SIZE` равен `4096` байтам. Таким образом, `THREAD_SIZE` будет раскрыт до `16` килобайт и представляет собой размер стека потока. Почему `потока`? Возможно, вы уже знаете, что каждый [процесс](https://en.wikipedia.org/wiki/Process_%28computing%29) может иметь [родительские](https://en.wikipedia.org/wiki/Parent_process) и [дочерние](https://en.wikipedia.org/wiki/Child_process) процессы. На самом деле родительский и дочерний процесс различаются в стеке. Для нового процесса выделяется новый стек ядра. В ядре Linux этот стек представлен [объединением (union)](https://en.wikipedia.org/wiki/Union_type#C.2FC.2B.2B) со структурой `thread_info`. -Как мы видим, `init_thread_union` представлен [объединением](https://en.wikipedia.org/wiki/Union_type#C.2FC.2B.2B) `thread_union`. Раньше это объединение выглядело следующим образом: +`init_thread_union` представлен `thread_union` и определён в файле [include/linux/sched.h](https://github.com/torvalds/linux/blob/0500871f21b237b2bea2d9db405eadf78e5aab05/include/linux/sched.h): ```C union thread_union { @@ -390,6 +390,9 @@ union thread_union { ```C union thread_union { +#ifndef CONFIG_ARCH_TASK_STRUCT_ON_STACK + struct task_struct task; +#endif #ifndef CONFIG_THREAD_INFO_IN_TASK struct thread_info thread_info; #endif @@ -397,27 +400,37 @@ union thread_union { }; ``` -где `CONFIG_THREAD_INFO_IN_TASK` - параметр конфигурации ядра, включённый для архитектуры `x86_64`. Поскольку в этой книге мы рассматриваем только архитектуру `x86_64`, экземпляр `thread_union` будет содержать только стек, а структура `thread_info` будет помещена в `task_struct`. +где `CONFIG_THREAD_INFO_IN_TASK` - параметр конфигурации ядра, включённый для архитектуры `ia64`, а `CONFIG_THREAD_INFO_IN_TASK` - параметр конфигурации ядра, включённый для архитектуры `x86_64`. Таким образом, структура `thread_info` будет помещена в структуру `task_struct` вместо объединения `thread_union`. Поскольку в этой книге мы рассматриваем только архитектуру `x86_64`, экземпляр `thread_union` будет содержать только стек и задачу -`init_thread_union` выглядит следующим образом: +`init_thread_union` определён в файле [include/asm-generic/vmlinux.lds.h](https://github.com/torvalds/blob/a6214385005333202c8cc1744c7075a9e1a26b9a/include/asm-generic/vmlinux.lds.h) как часть макроса `INIT_TASK_DATA`: +```C +#define INIT_TASK_DATA(align) \ + ... \ + init_thread_union = .; \ + ... ``` -union thread_union init_thread_union __init_task_data = { -#ifndef CONFIG_THREAD_INFO_IN_TASK - INIT_THREAD_INFO(init_task) -#endif -}; + +Данный макрос используется в [arch/x86/kernel/vmlinux.lds.S](https://github.com/torvalds/linux/blob/c62e43202e7cf50ca24bce58b255df7bf5de69d0/arch/x86/kernel/vmlinux.lds.S) следующим образом: + +``` +.data : AT(ADDR(.data) - LOAD_OFFSET) { + ... + /* init_task */ + INIT_TASK_DATA(THREAD_SIZE) + ... +} :data ``` -который представляет собой только стек потока. Теперь мы можем понять это выражение: +Теперь мы можем понять это выражение: ```assembly GLOBAL(initial_stack) - .quad init_thread_union+THREAD_SIZE-8 + .quad init_thread_union + THREAD_SIZE - SIZEOF_PTREGS ``` -где символ `initial_stack` указывает на начало массива `thread_union.stack` + `THREAD_SIZE`, который равен 16 килобайтам и - 8 байт. Здесь нам нужно вычесть `8` байт в верхней части стека. Это необходимо для обеспечения незаконного доступа следующей страницы памяти. +где символ `initial_stack` указывает на начало массива `thread_union.stack` + `THREAD_SIZE`, который равен 16 килобайтам и - `SIZEOF_PTREGS` равный 168 байтам. Здесь нам нужно вычесть `168` байт в верхней части стека. Это необходимо для обеспечения незаконного доступа следующей страницы памяти. После настройки начального загрузочного стека, необходимо обновить [глобальную таблицу дескрипторов](https://en.wikipedia.org/wiki/Global_Descriptor_Table) с помощью инструкции `lgdt`: diff --git a/contributors.md b/contributors.md index 547f890..6e34b50 100644 --- a/contributors.md +++ b/contributors.md @@ -119,3 +119,4 @@ * [Miles Frain](https://github.com/milesfrain) * [Horace Heaven](https://github.com/horaceheaven) * [Ivan Kovnatsky](https://github.com/sevenfourk) +* [Takuya Yamamoto](https://github.com/tkyymmt)