Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Lektsii_OC

.pdf
Скачиваний:
28
Добавлен:
19.05.2015
Размер:
1.39 Mб
Скачать

все ресурсы машины, конкурируют с друг другом. В общем случае процессы принадлежат разным пользователям, разделяющим один компьютер, и ОС берет на себя роль арбитра в спорах процессов за ресурсы.

При мультипрограммировании повышается пропускная способность системы, но отдельный процесс никогда не может быть выполнен быстрее, чем если бы он выполнялся в однопрограммном режиме (всякое разделение ресурсов замедляет работу одного из участников за счет дополнительных затрат времени на ожидание освобождения ресурса). Однако задача, решаемая в рамках одного процесса, может обладать внутренним параллелизмом, который в принципе позволяет ускорить ее решение. Например, в ходе выполнения задачи происходит обращение к внешнему устройству, и на время этой операции можно не блокировать полностью

выполнение процесса, а продолжить вычисления по другой "ветви" процесса.

Для этих целей современные ОС предлагают использовать сравнительно новый механизм многонитевой обработки (multithreading). При этом вводится новое понятие "нить" (thread), а понятие "процесс" в значительной степени меняет смысл.

Мультипрограммирование теперь реализуется на уровне нитей, и задача, оформленная в виде нескольких нитей в рамках одного процесса, может быть выполнена быстрее за счет псевдопараллельного (или параллельного в мультипроцессорной системе) выполнения ее отдельных частей. Например, если электронная таблица была разработана с учетом возможностей многонитевой обработки, то пользователь может запросить пересчет своего рабочего листа и одновременно продолжать заполнять таблицу. Особенно эффективно можно

использовать многонитевость для выполнения распределенных приложений, например, многонитевый сервер может параллельно выполнять запросы сразу нескольких клиентов.

Нити, относящиеся к одному процессу, не настолько изолированы друг от друга, как процессы в традиционной многозадачной системе, между ними легко организовать тесное взаимодействие. Действительно, в отличие от процессов, которые принадлежат разным, вообще говоря, конкурирующим приложениям, все нити одного процесса всегда принадлежат одному приложению, поэтому программист, пишущий это приложение, может заранее продумать работу множества нитей процесса таким образом, чтобы они могли взаимодействовать, а не

бороться за ресурсы.

В традиционных ОС понятие "нить" тождественно понятию "процесс". В действительности часто бывает желательно иметь несколько нитей, разделяющих единое адресное пространство, но выполняющихся квазипараллельно, благодаря чему нити становятся подобными процессам (за исключением разделяемого адресного пространства).

Нити иногда называют облегченными процессами или мини-процессами. Действительно, нити во многих отношениях подобны процессам. Каждая нить выполняется строго последовательно и имеет свой собственный программный счетчик и стек. Нити, как и процессы, могут, например, порождать нити-потомки, могут переходить из состояния в состояние. Подобно традиционным процессам (то есть

процессам, состоящим из одной нити), нити могут находится в одном из следующих состояний: ВЫПОЛНЕНИЕ, ОЖИДАНИЕ и ГОТОВНОСТЬ. Пока одна нить заблокирована, другая нить того же процесса может выполняться. Нити разделяют процессор так, как это делают процессы, в соответствии с различными вариантами планирования.

Однако различные нити в рамках одного процесса не настолько независимы, как отдельные процессы. Все такие нити имеют одно и то же адресное пространство. Это означает, что они разделяют одни и те же глобальные переменные. Поскольку каждая нить может иметь доступ к каждому виртуальному адресу, одна нить может использовать стек другой нити. Между нитями нет полной защиты, потому что, вопервых, это невозможно, а во-вторых, не нужно. Все нити одного процесса всегда решают общую задачу одного пользователя, и аппарат нитей используется здесь для более быстрого решения задачи путем ее распараллеливания. При этом программисту очень важно получить в свое распоряжение удобные средства организации взаимодействия частей одной задачи. Кроме разделения адресного пространства, все нити

разделяют также набор открытых файлов, таймеров, сигналов и т.п. Нити:

Имеют собственные

Разделяют

программный счетчик,

адресное пространство,

стек,

глобальные переменные,

регистры,

открытые файлы,

нити-потомки,

таймеры,

состояние.

семафоры,

 

статистическую информацию.

Некоторые прикладные задачи легче программировать, используя параллелизм, например задачи типа "писатель-читатель", в которых одна нить выполняет запись в буфер, а другая считывает записи из него. Поскольку они разделяют общий буфер, не стоит их делать отдельными процессами. Другой пример использования нитей - это управление сигналами, такими как прерывание с клавиатуры (del или break). Вместо обработки сигнала прерывания, одна нить назначается для постоянного ожидания поступления сигналов. Таким образом, использование нитей может сократить необходимость в прерываниях пользовательского уровня. В этих примерах не столь важно параллельное выполнение, сколь важна ясность программы.

Наконец, в мультипроцессорных системах для нитей из одного адресного пространства имеется возможность выполняться параллельно на разных процессорах. Это действительно один из главных путей реализации разделения ресурсов в таких системах. С другой стороны, правильно сконструированные программы, которые используют нити, должны работать одинаково хорошо как на однопроцессорной машине в режиме разделения времени между нитями, так и на настоящем мультипроцессоре.

Функции управления процессами:

Все функции управления процессами являются командами к диспетчеру-планировщику со стороны другого процесса. Такой процесс должен быть наделён специальными правами (запущен в специальном контексте безопасности). Базовые функции управления процессами приводятся ниже:

1) Создание процесса

Для создания процесса пользовательскими процессами выдаётся специальный системный запрос, в котором указывается требуемая информация: имя процесса, адрес начала данных процесса, адрес начала кода процесса и размер области данных.

Эта функция является системной функцией планировщика и кроме неё ни один пользовательский процесс не имеет доступа к системной информации, описывающей очереди процессов.

Два процесса, имеющих одного прародителя называются родственными. Процесс, созданный с помощью этой функции называется дочерним по отношению к тому процессу, который вызывал эту функцию. Как правило, при порождении процесса он наследует некоторую общую информацию (открытые ресурсы, файлы, глобальные переменные, таймеры, семафоры) – создаются вторые копии дескрипторов. Но некоторые частные данные, такие как стек, счётчик команд, управляющий блок создаются заново.

Функция возвращает уникальный код процесса (PID).

2) Удаление процесса

Получает всего один параметр – уникальный код удаленного процесса. При удалении процесса ОС должна освободить все занимаемые процессом ресурсы, удалить его из всех очередей и завершить все процессы, для которых удаляемый процесс является прародителем. Управление возвращается вызвавшему процессу.

Возможны две ситуации:

Вариант 1. Процесс удаляет сам себя. ОС в этом случае не может возвратить управление вызвавшему процессу, управление переходит сразу к планировщику-диспетчеру.

Вариант 2. В случае удаления процесса другим процессом последний должен обладать большим или равным приоритетом и/или быть наделённым правом удалять другие процессы (обычно права или контекст безопасности наследуется от прародителя к потомкам).

3) Задержка выполнения процессов

Функция используется для синхронизации процессов по времени или по событиям. Следует учесть, что задержка может производиться строго на определённое время (с точностью до периода системных часов). Задержка может быть инициирована самим процессом или любым другим процессом, обладающим соответствующими правами. Как правило задержанный процесс переводится в состояние ожидания (события от таймера или другого источника), и не занимает ресурсов процессора.

4) Изменение приоритетов

Процесс может менять свой приоритет в рамках класса приоритета, которому он принадлежит.

Шкала приоритетов

0

10

20

30

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

некрит.

польз.

системн.

макс.

Приоритет задается в виде пары чисел (класс, приоритет)

Под классом понимается начальное значение приоритета для данного класса. При изменении приоритета процесса в общем случае он должен быть перепланирован. Помимо самого приоритета в некоторых ОС для процесса можно изменить дисциплину планирования.

5) Останов процесса

Процесс может приостановиться как сам, так и и другим процессом, а возобновить работу может только по команде другого процесса, т.к. в состоянии ожидания он не обладает главным ресурсом – центральным процессором.

6) Возобновление работы после останова

Управление процессами в программной среде Win32.

1) создание процесса (CreateProcess)

Функция создаёт новый процесс и его основную нить. Новый процесс запускается из указанного исполняемого файла в контексте безопасности запускающего процесса. Для запуска процесса в контексте безопасности другого пользователя используются функции CreateProcessAsUser или

CreateProcessWithLogonW.

В заголовочном файле Windows.h функция CreateProcess описывается следующим образом:

BOOL CreateProcess(

LPCTSTR lpApplicationName, LPTSTR lpCommandLine,

LPSECURITY_ATTRIBUTES lpProcessAttributes,

LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,

DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo,

LPPROCESS_INFORMATION lpProcessInformation

);

Параметры функции:

LpApplicationName – указатель на строку с завершающим нулём, в которой описывается имя запускаемого модуля. Указный модуль может быть Windows-приложением, или модулем другого типа (например MS-DOS или OS/2) если подсистема доступна на данном компьютере. Строка может содержать полный путь и имя файла модуля или частичный путь и имя модуля. Функция не используется для поиска пути. Параметр может быть нулевым. В этом случае

1. Создание процесса void main( VOID ) {STARTUPINFO si;

PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si);

ZeroMemory( &pi, sizeof(pi) ); if( !CreateProcess(

NULL,"MyProcess.exe",NULL,NULL,FALSE,0,NULL,NULL,&si,&pi )) {ErrorExit( "CreateProcess failed." );}

WaitForSingleObject( pi.hProcess, INFINITE ); CloseHandle( pi.hProcess );

CloseHandle( pi.hThread );}

Методы взаимодействия процессов Создание дочернего процесса с перенаправленным вводом и выводом

#include <stdio.h> #include <windows.h> #define BUFSIZE 4096

HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup, hInputFile, hSaveStdin, hSaveStdout;

BOOL CreateChildProcess(VOID); VOID WriteToPipe(VOID); VOID ReadFromPipe(VOID); VOID ErrorExit(LPTSTR);

VOID ErrMsg(LPTSTR, BOOL);

DWORD main(int argc, char *argv[]) {SECURITY_ATTRIBUTES saAttr; BOOL fSuccess;

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL;

hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);

if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) ErrorExit("Stdout pipe creation failed\n");

if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) ErrorExit("Redirecting STDOUT failed");

fSuccess = DuplicateHandle(GetCurrentProcess(),hChildStdoutRd,GetCurrentProcess(),&hChi ldStdoutRdDup,0,FALSE,DUPLICATE_SAME_ACCESS);

if( !fSuccess ) ErrorExit("DuplicateHandle failed"); CloseHandle(hChildStdoutRd);

hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);

if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) ErrorExit("Stdin pipe creation failed\n");

if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) ErrorExit("Redirecting Stdin failed");

fSuccess = DuplicateHandle(GetCurrentProcess(),hChildStdinWr,GetCurrentProcess(),&hChil dStdinWrDup,0,FALSE,DUPLICATE_SAME_ACCESS);

if (!fSuccess) ErrorExit("DuplicateHandle failed"); CloseHandle(hChildStdinWr);

if (! CreateChildProcess()) ErrorExit("Create process failed"); if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) ErrorExit("Re-redirecting Stdin failed\n");

if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) ErrorExit("Re-redirecting Stdout failed\n");

if (argc > 1)

hInputFile = CreateFile(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); else

hInputFile = hSaveStdin;

if (hInputFile == INVALID_HANDLE_VALUE) ErrorExit("no input file\n");

WriteToPipe();

ReadFromPipe();

return 0;}

BOOL CreateChildProcess() {PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo;

ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); siStartInfo.cb = sizeof(STARTUPINFO);

return CreateProcess(NULL,"child.exe",NULL,NULL,TRUE,0,NULL,NULL,&siStartI nfo,&piProcInfo);}

VOID WriteToPipe(VOID) {DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE];

for (;;) {

if (! ReadFile(hInputFile, chBuf, BUFSIZE, &dwRead, NULL) || dwRead == 0) break;

if (! WriteFile(hChildStdinWrDup, chBuf, dwRead, &dwWritten, NULL)) break;

}

if (! CloseHandle(hChildStdinWrDup)) ErrorExit("Close pipe failed\n"); }

VOID ReadFromPipe(VOID) {DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE];

HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); if (!CloseHandle(hChildStdoutWr))

ErrorExit("Closing handle failed"); for (;;)

{if( !ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL) || dwRead == 0) break;

if (! WriteFile(hSaveStdout, chBuf, dwRead, &dwWritten, NULL)) break; }}

VOID ErrorExit (LPTSTR lpszMessage)

{

fprintf(stderr, "%s\n", lpszMessage); ExitProcess(0);

}

//Код дочернего процесса.

#include <windows.h> #define BUFSIZE 4096 VOID main(VOID)

{CHAR chBuf[BUFSIZE]; DWORD dwRead, dwWritten; HANDLE hStdin, hStdout; BOOL fSuccess;

hStdout = GetStdHandle(STD_OUTPUT_HANDLE); hStdin = GetStdHandle(STD_INPUT_HANDLE);

if ((hStdout == INVALID_HANDLE_VALUE)||(hStdin == INVALID_HANDLE_VALUE)) ExitProcess(1);

for (;;)

{fSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL); if (! fSuccess || dwRead == 0) break;

fSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL); if (! fSuccess) break;} }

Управление процессами и их взаимодействие в POSIX 1) Создание процессов

EXECVE(2) Руководство программиста Linux

EXECVE(2)

ИМЯ

 

execve - выполнить программу

 

КРАТКАЯ СВОДКА

#include <unistd.h>

int execve (const char *filename, char *const argv [], char *const envp[]);

ОПИСАНИЕ

execve() выполняет программу, заданную параметром file-name. Программа должна быть либо двоичным исполняемым файлом, или скриптом, начинающимся со строки вида "#!интерпретатор [аргументы]". В последнем случае интерпретатор -- это правильный путь к исполняемому файлу, который не является скриптом; этот файл будет выполнен как интерпретатор [arg] filename. argv - это массив строк, аргументов новой программы. envp - это массив строк в формате key=value, которые передаются новой программе в качестве окружения (environment). Как argv, так и envp завершаются нулевым указателем. К массиву аргументов и к окружению можно обратиться из функции main(), которая объявлена как int main(int argc, char *argv[], char *envp[]). execve() не возвращает управление при успешном выполнении, а код, данные, инициализированные данные и стек вызвавшего процесса перезаписываются кодом, данными и стеком загруженной программы. Новая программа также наследует от вызвавшего процесса его идентификатор и открытые файловые дескрипторы, на которых не было флага закрыть-при-exec (close-on-exec, COE). Сигналы, ожидающие обработки, удаляются. Переопределенные обработчики сигналов возвращаются в значение по умолчанию.

Если текущая программа выполнялась под управлением ptrace, то после успешного execve() ей посылается сигнал SIGTRAP. Если на файле программы filename установлен setuid-бит, то фактический идентификатор пользователя вызывавшего процесса меняется на идентификатор владельца файла

программы. Точно так же, если на файле

программы установлен

setgid-бит, то

фактический

идентификатор группы устанавливается в группу файла программы.

 

 

Если исполняемый файл является динамически-скомпонованным файлом в формате a.out,

содержащим заглушки для вызова разделяемых

библиотек, то в начале выполнения этого файла

вызывается динамический компоновщик

ld.so(8), который загружает

библиотеки и компонует их с

исполняемым файлом.

 

 

 

 

Если исполняемый файл является динамически-скомпонованным файлом в формате ELF, то для

загрузки разделяемых библиотек используется

интерпретатор, указанные в сегменте

PT_INTERP.

Обычно это /lib/ld-linux.so.1 для программ, скомпилированных под Linux libc версии 5, или же /lib/ldlinux.so.2 для программ, скомпилированных под GNU libc версии 2.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном завершении execve() не возвращает управление, при ошибке возвращается -1, а errno устанавливается должным образом.

КОДЫ ОШИБОК

EACCES Интерпретатор файла или скрипта не является обычным файлом.

EACCES Нет прав на выполнение файла, скрипта или ELF-интерпретатора. EACCES Файловая система смонтирована с флагом noexec.

EPERM Файловая система смонтирована с флагом nosuid, пользователь не является суперпользователем, а на файле установлен бит setuid или setgid.

EPERM Процесс работает под отладчиком, пользователь не является суперпользователем, а на файле установлен бит setuid или setgid.

E2BIG Список аргументов слишком велик.

ENOEXEC Исполняемый файл в неизвестном формате, для другой архитектуры, или же встречены какие-то ошибки, препятствующие его выполнению.

EFAULT filename указывает за пределы доступного адресного пространства. ENAMETOOLONG filename слишком длинное.

ENOENT Файл filename, или интерпретатор скрипта или ELF-файла не существует, или же не найдена разделяемая библиотека, требуемая файлу или интерпретатору.

ENOMEM Недостаточно памяти в ядре.

ENOTDIR Компонент пути filename, или интерпретатору скрипта или ELF-интерпретатору не является каталогом.

EACCES Нет прав на поиск в одном из каталогов по пути к filename, или имени интерпретатора скрипта или ELF-интерпретатора.

ELOOP Слишком много символьных ссылок встречено при поиске filename, или интерпретатора скрипта или ELF-интерпретатора.

ETXTBSY Исполняемый файл открыт для записи одним или более процессами. EIO Произошла ошибка ввода-вывода.

ENFILE Достигнут системный лимит на общее количество открытых файлов. EMFILE Процесс уже открыл максимально доступное количество открытых файлов.

EINVAL Исполняемый файл в формате ELF содержит более одного сегмента PT_INTERP (то есть, в нем указано более одного интерпретатора).

EISDIR ELF-интерпретатор является каталогом. ELIBBAD ELF-интерпретатор имеет неизвестный формат.

СООТВЕТСТВИЕ СТАНДАРТАМ

SVr4, SVID, X/OPEN, BSD 4.3. POSIX не документирует поведение, связанное с #!, но в остальном совершенно совместимо. SVr4 документирует дополнительные коды ошибок EAGAIN, EINTR, ELIBACC, ENOLINK, EMULTIHOP; POSIX не документирует коды ошибок ETXTBSY, EPERM, EFAULT, ELOOP, EIO, ENFILE, EMFILE, EINVAL, EISDIR и ELIBBAD.

ЗАМЕЧАНИЯ

Процессы, выполняющиеся с setuid или setgid, не могут быть трассированы с помощью ptrace(). Первая строка (строка с #!) исполняемого скрипта не может быть длиннее 127 символов. Linux игнорирует биты setuid и setgid на скриптах.

СМОТРИ ТАКЖЕ

chmod(2), fork(2), execl(3), environ(5), ld.so(8)

НАЗВАНИЕ

fork - создает дочерний процесс

КРАТКАЯ СВОДКА

#include <sys/types.h> #include <unistd.h> pid_t fork(void);

ОПИСАНИЕ

fork создает дочерний процесс, который отличается от родительского только значениями PID (идентификатор процесса) и PPID (идентификатор родительского процесса), а также тем фактом, что счетчики использования ресурсов установлены в 0. Блокировки файлов и сигналы, ожидающие обработки, не наследуются. Под Linux fork реализован с помощью "копирования страниц при записи" (copy-on-write, COW), поэтому расходы на fork сводятся к копирования таблицы страниц родителя и созданию уникальной структуры, описывающей задачу.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном завершении родителю возвращается PID дочернего процесса, а дочернему процессу возвращается 0. При неудаче родительскому процессу возвращается -1, дочернего процесса не создается, а errno выставляется должным образом.

ОШИБКИ

EAGAIN fork не может выделить достаточно памяти для копирования таблиц страниц родителя и для выделения структуры описания дочернего процесса.

ENOMEM fork не может выделить необходимые ресурсы ядра, потому что памяти слишком мало.

СООТВЕТСТВИЕ СТАНДАРТАМ

Системный вызов fork соответствует SVr4, SVID, POSIX, X/OPEN, BSD 4.3.

Управление памятью

Память является важнейшим ресурсом, требующим тщательного управления со стороны мультипрограммной операционной системы. Распределению подлежит вся оперативная память, не занятая операционной системой. Обычно ОС располагается в самых младших адресах, однако может занимать и самые старшие адреса. Функциями ОС по управлению памятью являются: отслеживание свободной и занятой памяти, выделение памяти процессам и освобождение памяти при завершении процессов, вытеснение процессов из оперативной памяти на диск, когда размеры основной памяти не достаточны для размещения в ней всех процессов, и возвращение их в оперативную память, когда в ней освобождается место, а также настройка адресов программы на конкретную область физической памяти.

Типы адресов

Для идентификации переменных и команд используются символьные имена (метки), виртуальные адреса и физические адреса (рисунок 2.7). Символьные имена присваивает пользователь при написании программы на алгоритмическом языке или ассемблере.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]