Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Iran указывает на адресное пространство и связанную с управлением памятью информацию.



3.2.7.4 active_mm

асtive_mm указывает на адресное пространство, которое чаще всего используется. Оба поля, mm и active_mm, вначале указывают на одну и ту же mm_struct.

Оценка описателя процесса подводит нас к идее типа данных, с которым процесс свя­зан на протяжении всего своего жизненного цикла. Теперь мы можем рассмотреть, что происходит на протяжении жизненного цикла процесса. Следующий раздел объясняет различные этапы и состояния процесса и построчно комментирует программу-пример для того, чтобы объяснить, что происходит в ядре.

Создание процессов: системные вызовы fork(), vfork() и cloneQ

После того как код примера откомпилирован в файл (в нашем случае исполнимый ELF1), мы можем вызвать его из командной строки. Посмотрим, что происходит после того, как мы нажимаем клавишу Return. Мы уже говорили, что любой процесс запускается другим процессом. Операционная система предоставляет функциональность, необходимую для этого в лице системных вызовов fork (), vf ork () и clone ().

Библиотека С предоставляет три функции, запускающие эти системные вызовы. Про­тотипы этих функций объявлены в < unistd. h>. Рис. 3.9 показывает, как процесс, вызы­вающий fork (), выполняет системный вызов sys__fork (). Этот рисунок показывает, как ядро производит создание процесса. Аналогично vf ork () вызывает sys_f ork () и clone () вызывает sys_clone ().

В конце концов все эти три системных вызова вызывают do_f ork () - функцию ядра, выполняющую большое количество действий, необходимых для создания процесса. Вас может удивить, почему для создания процесса существует три функции. Каждая функция создает процесс немного иначе, и существуют объективные причины, почему в разных случаях следует использовать разные функции.

Когда мы нажимаем кнопку Return в строке shell, оболочка создает новый процесс, исполняющий нашу программу с помощью вызова fork (). Поэтому, если мы вводим ко­манду Is в оболчке и нажимаем Return, псевдокод оболочки в этот момент выглядит при­мерно следующим образом:

if ( (pid = fork()) == 0 )

execve(*foo" ); else

waitpid(pid);

1 ELF - формат исполнимого файла, поддерживаемый Linux. В гл. 9 описана структура ELF-формата.



Глава 3 • Процессы: принципиальная модель выполнения


Program 1Program 2Program 3


forkQ


vforkQ


cloneQ


С Library

User Space


Kernel Space


System Call Table


 


System Call


forkQ


- sysJorkQ —|


 



cloneQ


sys_clone() —


 


> \


Vforkg


• sys_vforkQ —[


-doJorkQ


Рис. 3.9. Системный вызов создания процесса

Теперь мы можем рассмотреть эти функции и проследить их выполнение до уровня системного вызова. Несмотря на то что наша программа вызывает fork (), она может так­же легко вызывать vf ork () или clone (), которые мы тоже рассмотрим в этом разделе. Первой функцией, которую мы рассмотрим, будет fork (). Мы проследим ее через вызо­вы fork (), sys__f ork () и do__f ork (). Затем мы рассмотрим vf ork () и, наконец, clone (), тоже до вызова do_f ork ().


3.3 Создание процессов: системные вызовы forkQ, vforkQ и cloneQ



Функция fork()

Функция fork () возвращает два значения: одно родительскому и второе дочернему процессу. Если она возвращает значение дочернему процессу, fork () возвращает 0. Если она возвращает значение родительскому процессу, fork () возвращает РШ дочернего процесса. При вызове функции fork () функция помещает необходимую ин­формацию в соответствующие регистры, включая индекс в таблице системных вызовов, где находится указатель на системный вызов. Процессор, на котором мы работаем, сам определяет регистры, в которых хранится информация.

На этом этапе, если вы хотите продолжить последовательную передачу событий, по­смотрите раздел «Прерывания» в этой главе, чтобы увидеть, как вызывается sys_f ork (). Тем не менее понимать, как создается процесс, вовсе не обязательно.

Давайте теперь посмотрим на функцию sys_f ork (). Эта функция работает немно­го иначе, чем вызов функции do_f ork (). Обратите внимание, что функция sys_f ork () архитектурно зависима, потому что передаваемые в функцию параметры передаются через системные регистры.

arch/i386/kernel/process.с

asmlinkage int sys_fork(struct pt_regs regs)

{

return do__fork(SIGCHLD, regs.esp, & regs, 0, NULL, NULL);

}

Arch/ppc/kernel/process.с

int sys_fork(int pi, int p2, int p3, int p4, int p5, int p6,

struct pt_regs *regs) {

CHECK_FULL_REGS(regs);

return do_fork(SIGCHLD, regs-> grp[1], regs, 0, NULL, NULL); }

Две архитектуры получают разные параметры для системных вызовов. Структура pt_regs хранит информацию, подобную указателю стека. По соглашению на РРС указа­тель на стек содержится в gpr [ 1 ], а на х86 он содержится в %esp].

Функция vfork()

Функция vf ork () похожа на функцию fork () за исключением того, что родительский процесс блокируется до тех пор, пока дочерний не вызовет exit () или exec ().

1 Помните, что этот код выполнен в формате «AT& T», в котором регистры имеют префикс %.



Глава 3 • Процессы: принципиальная модель выполнения


sys_vfогк()

arch/i38б/kernel/process.с

asmlinkage int sys_vfork(struct pt_regs regs)

{

return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.ep, & regs, 0, NOLL, NULL); }

Arch/ppc/kernel/process.с

int sys_vfork(int pi, int p2, int p3, int p4, int p5, int p6,

struct pt_regs *regs) {

CHECK_FULL_REGS(regs);

return do_fork (CLONE__VFORK | CLONE_VM | SIGCHLD, regs-> gpr[1], regs, 0, NULL, NULL); }

Единственная разница между вызовами do_f ork () в sys_vf ork () и sys_ fork () заключается во флагах, передаваемых do_fork (). Наличие этих флагов проверяется позднее для определения того, будет ли выполнено описанное выше поведе­ние (блокирование родителя).

Функция clone()

Библиотечная функция clone () в отличие от f ork() и vf ork() получает указатель на функцию в качестве аргумента1. Дочерний процесс, который создается с помощью do_f ork (), вызывает эту функцию сразу же после своего создания.

sys_clone()

arch/i38б/kernel/process. с

asmlinkage int sys__clone (struct pt_regs regs) { unsigned long clone_flags;

unsigned long newsp;

int __ user *parent_tidptr, *child_tidptr;

clone_flags = regs.ebx;

newsp = regs.ecx;

parent_tidptr = (int _ user *)regs.edx;

child_tidptr = (int _ user *)regs.edi;

1 Библиотечная функция clone О имеет следующий прототип: int clone (int (*fn) (void *), void *child_stack, int flags, void *arg), где первый параметр - та функция, которая будет запущена сразу же после создания процесса. Примеч. науч. ред.


3.3 Создание процессов: системные вызовы fork(), vforkQ и clone()



if (Inewsp)

newsp = regs.esp; return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, & regs, 0, parent__tidptr, child_tidptr);

}

arch/ppc/kernel/process, с

int sys_clone(unsigned long clone_flags/ unsigned long usp,

int __ user *parent_tidp, void _ user *child__thread_ptr,

int __ user *child_tidp, int рб,

struct pt_regs *regs) {

CHECK_FULL_REGS(regs); if (usp == 0)

usp = regs-> gpr[1]; /* указатель на стек дочернего процесса */ return do_fork(clone_flags & ~CL0NE_IDLETASK, usp, regs, 0, parent_tidp, child_tidp); }

Как показано в табл. 3.4, единственная разница между fork (), vf ork () и clone () заключается во флагах, установленных в соответствующем вызове do_f ork ().

Таблица 3.4. Флаги, передаваемые в doJorkQ, vforkQ и cloneQ

 

  forkQ vfork() clone()
SIGCHLD X X  
CLONE_VFORK   X  
CLONE_VM   X  

И наконец, мы переходим к do_f ork (), которая выполняет настоящее создание процесса. Вспомним, что до этого мы только выполнили из родителя вызов fork (), поро­дивший системный вызов sys_f ork О, и у нас еще нет нового процесса. Наша програм­ма f оо до сих пор является исполнимым файлом на диске. В памяти она еще не запущена.

3.3.4 Функция do_fork()

Мы проследим выполнение ядром функции do_f ork () построчно и прокомментируем детали создания нового процесса.

kernel/fork.с

1167 long do_fork(unsigned long clone_flags,

1168 unsigned long stack_start,



Глава 3 • Процессы: принципиальная модель выполнения


1169 struct pt__regs *regs,

1170 unsigned long stack_size,

1171 int __ user *parent_tidptr,

1172 int __ user *child_tidptr)

1173 {

1174 struct task_struct *p;

1175 int trace = 0;

1176 long pid; 1177

1178 if (unlikely(current-> ptrace)) {

1179 trace = fork_traceflag (clone_flags);

1180 if (trace)

1181 clone_flags |= CLONE_PTRACE;

1182 }
1183

1184 p = copy_process (clone__f lags, stack_start, regs,

stack_size, parent_tidptr, child_tidptr);

Строки 1178-1183

Код начинается с проверки того, хочет ли родительский процесс сделать новый процесс трассируемым (ptraced). Трассировка имеет приоритет среди функций, имеющих дело с процессом. Эта книга объясняет назначение ptrace только на са­мом высоком уровне. Для определения, какой дочерний процесс должен быть отсле­жен, fork_traceflag() должна подтвердить значение clone__flags. Если в clone_f lags установлен флаг CLONE_VFORK и SIGCHLD не перехвачен роди­телем или если текущий процесс обладает также установленным флагом PT__TRACE_FORK, дочерний процесс отслеживается до тех пор, пока не будут вы­ставлены флаги CLONE_UNTRACED или CLONE_IDLETASK.

Строка 1184

В этой строке создается новый процесс и из регистров извлекаются необходимые значения. Функция copy_process () выполняет все необходимые действия для создания пространства процесса и заполнения полей его описателя. Тем не менее за­пуск нового процесса происходит позже. Подробности copy__process () будут более уместны при рассмотрении работы планировщика. (См. раздел «Слежение за процессами: базовые конструкции планировщика», где более подробно описано происходящее здесь.)

kernel/fork.с

1189 pid = IS_ERR(p)? PTR__ERR(p): p-> pid; 1190


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-03-25; Просмотров: 1167; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.039 с.)
Главная | Случайная страница | Обратная связь