Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
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
И наконец, мы переходим к 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 } 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; Нарушение авторского права страницы