Видео ролики бесплатно онлайн

Смотреть русское видео

Официальный сайт ejnews 24/7/365

Смотреть видео бесплатно

dementiy 08.02.2010 00:55

CodingМеханизм создания процессов в Linux. Продолжение.

В прошлый раз было рассмотрено, как происходит создание нового процесса в системе, но не было рассказано про выделение памяти процессу и под сам процесс.

Итак мы остановились на функции copy_process(), которая как было сказано выполняет фактическую работу по созданию нового процесса. В copy_process() происходит вызов функции dup_task_struct(), которая создает стек ядра и выделяет память под структуры task_struct и thread_info:

kernel/fork.c:
1
2
3
4
5
6
7
8
9
static struct task_struct *copy_process(...) 
{
...
p = dup_task_struct(current);
...
if ((retval = copy_mm(clone_flags, p)))
goto bad_fork_cleanup_signal;
...
}


В dup_task_struct() передается указатель (current — макрос, который определен в файле arch/x86/include/asm/current.h) на текущий (вызвавший, то есть «родитель») процесс. Ниже приведен наиболее важный код из dup_task_struct():

kernel/fork.c:
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static struct task_struct *dup_task_struct(struct task_struct *orig) 
{
struct task_struct *tsk;
struct thread_info *ti;
int err;
prepare_to_copy(orig);
tsk = alloc_task_struct();
if (!tsk)
return NULL;
ti = alloc_thread_info(tsk);
if (!ti) {
free_task_struct(tsk);
return NULL;
}
err = arch_dup_task_struct(tsk, orig);
if (err)
goto out;
tsk->stack = ti;
...
setup_thread_stack(tsk, orig);
...
return tsk;
...
}


prepare_to_copy() отвечает за сохранение регистров процессора в структуре thread_info «родителя», затем они буду скопированы в структуру «ребенка».
alloc_task_struct() - макрос, который возвращает дескриптор процесса для нового процесса и сохраняет его адрес в локальной переменной tsk:

kernel/fork.c:
1
# define alloc_task_struct()   kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)


task_struct_cahep — указатель на структуру struct kmem_cache, то есть кэш объектов task_struct, из которого будет выделяться объект. GFP_KERNEL (GFP — Get Free Page) — это флаг, который означает, что следует зарезервировать блок памяти, выделяя страницы по мере обращения к ним.
Функкция kmem_cache_alloc() представляет для нас наибольший интерес. Дело в том, что в Linux используется механизм управления памятью под названием slab allocator (кусочный или слябовый распределитель, в разных источниках имеет разное название). Но с версии ядра 2.6.22 появилась новая система управления памятью slub allocator, который оптимизирован для SMP-систем, то есть многопроцессорных. По умолчанию, начиная с ядер версии 2.6.23, выбирается именно slub allocator, вот что пишут при конфигурировании ядра:

Для SLAB:
«The regular slab allocator that is established and known to work well in all environments. It organizes cache hot objects in per cpu and per node queues.»
Для SLUB:
«SLUB is a slab allocator that minimizes cache line usage instead of managing queues of cached objects (SLAB approach). Per cpu caching is realized using slabs of objects instead of queues of objects. SLUB can use memory efficiently and has enhanced diagnostics. SLUB is the default choice for a slab allocator.»

При ручной сборке ядра имеется возможность выбрать allocator см. рис.1.


Рис.1. Выбор allocator'а

Итак мы определились с тем, что будем рассматривать интерфейсы slub allocator'а. Продолжим рассмотрение kmem_cache_alloc(). kmem_cache_alloc() экспортируется ядром, что позволяет нам его использовать при написании модулей ядра, в тоже время это обертка для функции slab_alloc():

mm/slub.c:
1
2
3
4
5
void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags)
{
return slab_alloc(s, gfpflags, -1, __builtin_return_address(0));
}
EXPORT_SYMBOL(kmem_cache_alloc);


mm/slub.c:
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static __always_inline void *slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, void *addr)
{
void **object;
struct kmem_cache_cpu *c;
...
c = get_cpu_slab(s, smp_processor_id());
...
if (unlikely(!c->freelist || !node_match(c, node)))
object = __slab_alloc(s, gfpflags, node, addr, c);
else {
object = c->freelist;
c-&gt;freelist = object<c->;
stat(c, ALLOC_FASTPATH);
}
...
return object;
} </c->


Итак, что происходит в slab_alloс()? Узнаем к какому процессору принадлежит кэш объектов task_struct (get_cpu_slab()). Далее есть два пути: медленный и быстрый. Медленный: если нет больше свободных объектов, то выбирается следующий свободный сляб или же, если больше нет свободных слябов - создается новый сляб, из которого происходит выделение объекта, за это отвечает функция __slab_alloc(). В противном случае (быстрый путь), что более вероятно, новый объект получаем из списка свободных объектов:
object = c->freelist (на первый свободный объект в списке указывает переменная freelist). При необходимости (как например отладка) память под выделенный объект заполняется нулями (если установлен флаг __GFP_ZERO).

* больше о SLUB allocator можно узнать на здесь


Возвращаясь к dup_task_struct(). Итак, выделив память для дескриптора процесса, далее выделяется память под структуру
struct thread_info (данная структура содержит информацию о процессе, которая специфична для архитектуры и определена в файле arch/x86/include/asm/thread_info.h) с помощью макроса alloc_thread_info(), который является оберткой для функции __get_free_pages():

arch/x86/include/asm/thread_info.h:
1
#define alloc_thread_info(tsk) ((struct thread_info *)__get_free_pages(THREAD_FLAGS, THREAD_ORDER))


Функция __get_free_pages() выделяет 2^THREAD_ORDER (THREAD_ORDER равен 0, если при сборке ядра была указана опция CONFIG_4KSTACKS, в противном случае он равен 1, другими словами выделять одну или две страницы памяти) смежных страниц физической памяти и возвращает логический адрес первой выделенной страницы.
И последнее, в dup_task_struct() устанавливается стек (см. рис.2):

1
2
3
tsk-&gt;stack = ti;
...
setup_thread_stack(tsk, orig);



Функция setup_thread_stack() просто копирует содержимое структуры thread_info «родителя» в структуру thread_info «ребенка» и связывает thread_info с task_struct:

include/linux/shed.h:
1
2
3
4
5
6
#define task_thread_info(task)      ((struct thread_info *)(task)->stack)
static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org)
{
*task_thread_info(p) = *task_thread_info(org);
task_thread_info(p)-&gt;task = p;
}



Рис.2. Связь между task_struct, thread_info и стеком процесса (kernel space)

Вернемся к функции copy_process(). У каждого процесса так или иначе есть свое адресное пространство, размер которого зависит от архитектуры (например для 32-х разрядных систем оно составляет 2^32, что составляет около 4 Гб). Процесс имеет доступ только к определенной области памяти (сегмент кода, сегмент данных, стек, bss и т.д.) из адресного пространства, причем на область памяти могут накладываться определенные права (запись, выполнение и т.д., см. рис.3).


Рис.3. Права (флаги) установленные в сегменте кода на секцию инструкций

Адресное пространство описывается структурой struct mm_struct, которая определена в файле include/linux/mm_types.h. Итак в функции copy_process() происходит выделение дескриптора памяти (struct mm_struct) для нового процесса. Выделение происходит посредством вызова функции copy_mm() и передаче ей флагов и дескриптора процесса.

* все области памяти для процесса можно просмотреть следующим образом: cat /proc/pid_your_process/maps


kernel/fork.c:
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
{
struct mm_struct * mm, *oldmm;
...
tsk-&gt;mm = NULL;
tsk-&gt;active_mm = NULL;
oldmm = current-&gt;mm;
if (!oldmm)
return 0;
if (clone_flags & CLONE_VM) {
atomic_inc(&oldmm-;&gt;mm_users);
mm = oldmm;
goto good_mm;
}
...
mm = dup_mm(tsk);
if (!mm)
goto fail_nomem;
good_mm:
...
tsk-&gt;mm = mm;
tsk-&gt;active_mm = mm;
return 0;
...
}


Если установлен флаг CLONE_VM, то память под адресное пространство не выделяется, а используется совместно с родителем, таким образом мы получаем поток. Если флаг не установлен, то выделение происходит с помощью функции dup_mm(), которая, к слову сказать, использует ранее рассматривавшуюся функцию kmem_cache_alloc().
Далее снова берет на себя «бразды правления» функция copy_process(), заполняя task_struct.

P.S. Первое, за содействие при составлении статьи спасибо eddi. Второе, на написание статьи наталкнул commonD, без его комментария к прошлой статье я бы и не стал разбираться с этим вопросом. И последнее, но не менее важное, в pdf по мимо самой статьи есть еще два небольших приложения.


Тэги: kernel processes
+ 14 -
Похожие Поделиться

berkus 08.02.2010 03:17 #
+ 0 -
(y)
digiwhite 08.02.2010 06:34 #
+ 0 -
Отличненько. В мемориз.
Shtsh 08.02.2010 12:40 #
+ 0 -
Да, опять хорошая статья.
commonD 09.02.2010 17:02 #
+ 0 -
Спасибо, очень интересно.

Кстати получается, что kmalloc работает по такому же принципу, исходя из /proc/slabinfo.
dementiy 09.02.2010 18:57 #
+ 0 -
Вы все верно поняли, используется тот же механизм. При желании можете посмотреть как устроен kmalloc(), он определен в include/linux/slub_def.h.

Смотреть онлайн бесплатно

Онлайн видео бесплатно


Смотреть русское с разговорами видео

Online video HD

Видео скачать на телефон

Русские фильмы бесплатно

Full HD video online

Смотреть видео онлайн

Смотреть HD видео бесплатно

School смотреть онлайн