|
|||||||
АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция |
Процессы. Системные вызовы fork() и exec()Лабораторная работа 2 Процесс в Linux (как и в UNIX) - это программа, которая выполняется в отдельном защищенном виртуальном адресном пространстве. Когда пользователь регистрируется в системе, автоматически создается новый процесс, в котором выполняется оболочка (shell), например, /bin/bash. Если интерпретатору (shell) встречается команда, соответствующая выполняемому файлу, интерпретатор выполняет ее, начиная с точки входа (entry point). Для С-программ entry point - это функция main. Запущенная программа тоже может создать процесс, т.е. запустить какую-то программу и ее выполнение тоже начнется с функции main. Часть времени процесс выпоняется в режиме задачи (пользовательском режиме) и тогда он выпоняет собственный код, часть времени процесс выпоняется в режиме ядра и тогда он выполняет реентерабельный код операционной системы. В Linux поддерживается классическая схема мультипрограммирования. Linux поддерживает параллельное (или квазипараллельное при наличии только одного процессора) выполнение процессов пользователя. Каждый процесс выполняется в собственном защищенном виртуальном адресном пространстве, т.е. процессы защищены друг от друга и крах одного процесса никак не повлияет на другие выполняющиеся процессы и на всю систему в целом. Ядро предоставляет системные вызовы для создания новых процессов и для управления запущенными процессами. Любая программа может начать выполняться только если другой процесс ее запустит. Для создания процессов используется системный вызов fork(). В Unix и Unix-подобных ОС процесс создается только системным вызовом fork(). Системный вызов fork() создает новый процесс, который является копией процесса-предка: процесс-потомок наследует адресное пространство процесса-предка, дескрипторы всех открытых файлов и сигнальную маску. Другими словами, программа которую выполняет процесс-потомок является программой родительского процесса. Процесс потомок имеет идентичные с родителем области данных и стека. Процесс-потомок начинает работу в режиме задачи после возвращения из системного вызова fork(). Тот факт, что образы памяти, переменные, регистры и все остальное и у родительского процесса, и у дочернего идентичны, могло бы привести к невозможности различить эти процессы, однако, системный вызов fork() возвращает дочернему процессу число 0, а родительскому — отличный от нуля PID (Process IDentifier — идентификатор процесса) дочернего процесса. Оба процесса обычно проверяют возвращаемое значение и действуют соответственно: #include <unistd.h> int main(void) { { { /* здесь располагается дочерний код */ { return 0; } Если вызов fork() завершается аварийно, он возвращает -1. Обычно это происходит из-за ограничения числа дочерних процессов, которые может иметь родительский процесс (CHILD_MAX), в этом случае переменной errno будет присвоено значение EAGAIN. Если для элемента таблицы процессов недостаточно места или не хватает виртуальной памяти, переменная errno получит значение ENOMEM. В старых Unix-системах код предка копировался в адресное пространство процесса-потомка. В современных системах применяется, так называемая, оптимизация fork(): для процесса потомка создаются собственные карты трансляции адресов (таблицы страниц), но они ссылаются на адресное пространство процесса-предка (на страницы предка). При этом для страниц адресного пространства предка права доступа меняются на only- read и устанавливается флаг – copy-on-write. Если или предок или потомок попытаются изменить страницу, возникнет исключение по правам доступа. Выполняя это исключение супервизор обнаружит флаг copy-on-write и создаст копию страницы в адресном пространстве того процесса, который пытался ее изменить. Таким образом код процесса-предка не копируется полностью, а создаются только копии страниц, которые редактируются.
Рассмотрим более подробно, что же делается при выполнении вызова fork() (рис.1): 1. Резервируется пространство свопинга для данных и стека процесса-потомка; 2. Назначается идентификатор процесса PID и структура proc потомка; 3. Инициализируется структура proc потомка. Некоторые поля этой структуры копируются от процесса-родителя: идентификаторы пользователя и группы, маски сигналов и группа процессов. Часть полей инициализируется 0. Часть полей инициализируется специфическими для потомка значениями: PID потомка и его родителя, указатель на структуру proc родителя; 4. Создаются карты трансляции адресов для процесса-потомка; 5. Выделяется область u потомка и в нее копируется область u процесса-предка; 6. Изменяются ссылки области u на новые карты адресации и пространство свопинга; 7. Потомок добавляется в набор процессов, которые разделяют область кода программы, выполняемой процессом-родителем; 8. Постранично дублируются области данных и стека родителя и модифицируются карты адресации потомка; 9. Потомок получает ссылки на разделяемые ресурсы, которые он наследует: открытые файлы (потомок наследует дескрипторы) и текущий рабочий каталог; 10. Инициализируется аппаратный контекст потомка путем копирования регистров родителя; 11. Поместить процесс-потомок в очередь готовых процессов; 12. Возвращается PID в точку возврата из системного вызова в родительском процессе и 0 - в процессе-потомке.
Рис. 1 Процессы описываются структурой proc Эта структура содержит всю информацию о процессе. Информация нахoдится по адресу /usr/src/sys/sys/proc.h: /* * Description of a process. * * This structure contains the information needed to manage a thread of * control, known in UN*X as a process; it has references to substructures * containing descriptions of things that the process uses, but may share * with related processes. The process structure and the substructures * are always addressable except for those marked "(PROC ONLY)" below, * which might be addressable only on a processor on which the process * is running. */struct proc { struct proc *p_forw; /* Doubly-linked run/sleep queue. */ struct proc *p_back; struct proc *p_next; /* Linked list of active procs */ struct proc **p_prev; /* and zombies. */ /* substructures: */ struct pcred *p_cred; /* Process owner's identity. */ struct filedesc *p_fd; /* Ptr to open files structure. */ struct pstats *p_stats; /* Accounting/statistics (PROC ONLY). */ struct plimit *p_limit; /* Process limits. */ struct vmspace *p_vmspace; /* Address space. */ struct sigacts *p_sigacts; /* Signal actions, state (PROC ONLY). */ #define p_ucred p_cred->pc_ucred#define p_rlimit p_limit->pl_rlimit int p_flag; /* P_* flags. */ char p_stat; /* S* process status. */ char p_pad1[3]; pid_t p_pid; /* Process identifier. */ struct proc *p_hash; /* Hashed based on p_pid for kill+exit+... */ struct proc *p_pgrpnxt; /* Pointer to next process in process group. */ struct proc *p_pptr; /* Pointer to process structure of parent. */ struct proc *p_osptr; /* Pointer to older sibling processes. */ /* The following fields are all zeroed upon creation in fork. */#define p_startzero p_ysptr struct proc *p_ysptr; /* Pointer to younger siblings. */ struct proc *p_cptr; /* Pointer to youngest living child. */ pid_t p_oppid; /* Save parent pid during ptrace. XXX */ int p_dupfd; /* Sideways return value from fdopen. XXX */ /* scheduling */ u_int p_estcpu; /* Time averaged value of p_cpticks. */ int p_cpticks; /* Ticks of cpu time. */ fixpt_t p_pctcpu; /* %cpu for this process during p_swtime */ void *p_wchan; /* Sleep address. */ char *p_wmesg; /* Reason for sleep. */ u_int p_swtime; /* Time swapped in or out. */ u_int p_slptime; /* Time since last blocked. */ struct itimerval p_realtimer; /* Alarm timer. */ struct timeval p_rtime; /* Real time. */ u_quad_t p_uticks; /* Statclock hits in user mode. */ u_quad_t p_sticks; /* Statclock hits in system mode. */ u_quad_t p_iticks; /* Statclock hits processing intr. */ int p_traceflag; /* Kernel trace points. */ struct vnode *p_tracep; /* Trace to vnode. */ int p_siglist; /* Signals arrived but not delivered. */ struct vnode *p_textvp; /* Vnode of executable. */ char p_lock; /* Process lock (prevent swap) count. */ char p_pad2[3]; /* alignment */ /* End area that is zeroed on creation. */#define p_endzero p_startcopy /* The following fields are all copied upon creation in fork. */#define p_startcopy p_sigmask sigset_t p_sigmask; /* Current signal mask. */ sigset_t p_sigignore; /* Signals being ignored. */ sigset_t p_sigcatch; /* Signals being caught by user. */ u_char p_priority; /* Process priority. */ u_char p_usrpri; /* User-priority based on p_cpu and p_nice. */ char p_nice; /* Process "nice" value. */ char p_comm[MAXCOMLEN+1]; struct pgrp *p_pgrp; /* Pointer to process group. */ struct sysentvec *p_sysent; /* System call dispatch information. */ struct rtprio p_rtprio; /* Realtime priority. *//* End area that is copied on creation. */#define p_endcopy p_addr struct user *p_addr; /* Kernel virtual addr of u-area (PROC ONLY). */ struct mdproc p_md; /* Any machine-dependent fields. */ u_short p_xstat; /* Exit status for wait; also stop signal. */ u_short p_acflag; /* Accounting flags. */ struct rusage *p_ru; /* Exit information. XXX */};
Поиск по сайту: |
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Студалл.Орг (0.006 сек.) |