Процессы Процесс – это исполняемый экземпляр программы и набор ресурсов, которые выделяются данной исполняемой программе. Ресурсы: • виртуальное адресное пространство; • системные ресурсы –области физической памяти, процессорное время, файлы, растровые изображения и т.д.; • модули процесса, то есть исполняемые модули, загруженные (отображенные) в его адресное пространство – основной загрузочный модуль, библиотеки динамической компоновки, драйверы устройств и т.д.; • уникальный идентификационный номер, называемый идентификатором процесса; • потоки (по крайней мере, один поток). Модель процесса: Квант времени Поток A Переклю чение процесс ора Квант времени Поток B Переклю чение процесс ора Квант времени ………… Планировщик процессов. Диспетчер процессов • Сохранение контекста текущего потока, регистров, стека и областей памяти. • Определение очередного потока. • Восстановление контекста очередного потока. t Последовательность исполнения потоков в среде с вытесняющей многозадачностью: В системе определен квант времени (порядка десятков миллисекунд) – процессорное время выделяемое одному потоку (каждому - своё). Длительность выполнения одного потока не может превышать одного кванта. Когда это время заканчивается, диспетчер процессов переключает процессор на выполнение другого потока. При этом состояние регистров, стека и областей памяти – контекст потока, сохраняется в стеке потока. Очередность потоков определяется их состоянием и приоритетом. Состояние процессов: Действие 1 Блокировка 1. 2. 3. 4. 3 4 2 Готовность Процесс заблокирован в ожидании ввода. Диспетчер выбирает другой процесс. Диспетчер выбирает данный процесс. Входные данные стали доступны. Реализацией процессов является таблица процессов, - линейный список (программно реализованный, как массив структур). Информация о процессах хранится в таблице процессов и обновляется планировщиком процессов. Некоторые поля типичной записи таблицы процессов: Регистры Счетчик команд Указатель стека Состояние процесса Приоритет Идентификатор процесса Родительский процесс Время запуска процессора Использованное время процессора Корневой каталог Рабочий каталог Дескрипторы файлов Идентификатор пользователя Создание процесса: #include <stdio.h> #include <sys/types.h> #include <unistd.h> void oldman(); void recreation(); int main(){ pid_t child_pid, parent_pid; int i=0; fprintf(stdout, "Before RECREATION %i\n", parent_pid=(int) getpid()); child_pid=fork(); while(i++<5) if(child_pid!=0) oldman(); else recreation(); return 0; } #include <sys/types.h> #include <unistd.h> void oldman(){ fprintf(stdout, "I'm not yet dead! My ID is %i\n", (int) getpid()); } void recreation(){ fprintf(stdout, "Who I am? My ID is %i\n", (int) getpid()); } С точки зрения планировщика дочерний и родительский процессы независимы: ewgenij@linux-g5md:~/2011spring/Lect2> ./2 Before RECREATION 6169 I'm not yet dead! My ID is 6169 I'm not yet dead! My ID is 6169 I'm not yet dead! My ID is 6169 Who I am? My ID is 6170 I'm not yet dead! My ID is 6169 I'm not yet dead! My ID is 6169 Who I am? My ID is 6170 Who I am? My ID is 6170 Who I am? My ID is 6170 Who I am? My ID is 6170 ewgenij@linux-g5md:~/2011spring/Lect2> ./2 Before RECREATION 6154 I'm not yet dead! My ID is 6154 I'm not yet dead! My ID is 6154 Who I am? My ID is 6155 Who I am? My ID is 6155 Who I am? My ID is 6155 Who I am? My ID is 6155 Who I am? My ID is 6155 I'm not yet dead! My ID is 6154 I'm not yet dead! My ID is 6154 I'm not yet dead! My ID is 6154 #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(){ pid_t child_pid, parent_pid; double s=0.0;; child_pid=fork(); if(child_pid!=0){ s+=3.14; fprintf(stdout, "CHILD: %i s=%g &s=%u\n", (int) getpid(),s,&s); } else{ s+=2.72; fprintf(stdout, "PARENT: %i s=%g &s=%u\n", (int) getpid(),s, &s); } return 0; } Output: PARENT: 5404 s=2.72 &s=2309295864 CHILD: 5403 s=3.14 &s=2309295864 При создании процесса с помощью системного вызова fork() копируется адресное пространство, - переменная s имеет один и тот же адрес. Однако отображение на физическую память для родительского и дочернего процесса различно, - значения переменной s различны. #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(){ pid_t child_pid; pid_t parent_pid; double s=0.0;; FILE* fp; child_pid=fork(); fp=fopen("test.dat","a+"); if(child_pid!=0){ s+=3.14; fprintf(fp, "CHILD: %i s=%g &s=%u fp=%u\n", (int) getpid(), s, &s, fp); } else{ s+=2.72; fprintf(fp, "PARENT: %i s=%g &s=%u fp=%u\n",(int) getpid(), s, &s,fp); } fclose(fp); return 0; } test.dat PARENT: 5450 s=2.72 &s=760346688 fp=6299664 CHILD: 5449 s=3.14 &s=760346688 fp=6299664 Дескрипторы файлов при копировании сохраняются. Создание процессов с помощью семейства системных вызовов exec*: #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char* argv[]){ fprintf(stdout, "Before child process creating: PARENT ID = %i\n", (int) getpid()); execvp(argv[1], argv); //execvp("ls", argv); fprintf(stdout, "Everything is ignored!\n"); return 0; } Output: ./6ex Before child process creating: PARENT ID = 5728 1 1.c 2 2.c 2.dat 3 3.c 4 4.c 5 5.c 6 6.c 6ex 6ex.c test.dat ./6ex -l *.dat Before child process creating: PARENT ID = 5741 -rw-r--r-- 1 ewgenij users 3157 2011-02-15 14:34 2.dat -rw-r--r-- 1 ewgenij users 90 2011-02-15 15:37 test.dat ./6ex ./5 Before child process creating: PARENT ID = 5923 CHILD: 5923 s=3.14 &s=3669975528 PARENT: 5924 s=2.72 &s=3669975528 Совместное использование fork и execvp: #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char* argv[]){ pid_t child_pid; child_pid=fork(); if( child_pid==0) execvp("ls", argv); fprintf(stdout,"The main program is yet running!\n"); return 0; } Родительский процесс продолжает существовать и активен: ewgenij@linux-g5md:~/2011-spring/Lect2> ./7ex -l *.png The main program is yet running! -rw-r--r-- 1 ewgenij users 119000 2011-02-15 17:27 exec1.png #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <signal.h> void oldman(){ fprintf(stdout, "I'm not yet dead! My ID is %i\n", (int) getpid()); } void recreation(){ fprintf(stdout, "Who I am? My ID is %i\n", (int) getpid()); } int main(){ pid_t child_pid, parent_pid; int i=0; fprintf(stdout, "Before RECREATION %i\n", parent_pid=(int) getpid()); child_pid=fork(); while(i++<5) if(child_pid!=0){ oldman(); if(i==3) kill(child_pid,SIGTERM); } else recreation(); return 0; } I'm not yet dead! My ID is 6526 I'm not yet dead! My ID is 6526 I'm not yet dead! My ID is 6526 Who I am? My ID is 6527 Who I am? My ID is 6527 I'm not yet dead! My ID is 6526 I'm not yet dead! My ID is 6526 Интерфейс системных вызовов MS Windows Win32 Особенности реализации языка С компании Microsot (компилятор cl). Некоторые типы данных, поддерживаемые Microsoft Windows: DWORD BOOL typedef unsigned long DWORD typedef int BOOL; BYTE typedef unsigned char BYTE; PVOID typedef void *PVOID; HANDLE typedef PVOID HANDLE; Чтобы обеспечить поддержку типов Microsoft Windows в программе, необходимо включить в нее заголовочный файл windows.h. Этот файл также содержит объявления функций интерфейса системных вызовов MS Windows Win32 API. Пример объявления функции: BOOL GetComputerName( LPTSTR lpBuffer; LPDWORD nSize; ); LPSTR LPDWORD typedef char *LPSTR typedef WORD *LPDWORD #include <windows.h> #include <stdio.h> int main(){ char Buffer[MAX_COMPUTERNAME_LENGTH+1];//[5]; int size=sizeof(Buffer); if( !GetComputerName((LPTSTR)Buffer, (LPDWORD)&size) ){ printf("System error code: %i\n",GetLastError()); return -1; } fprintf(stdout,"The computer name is %s\n",Buffer); return 0; } Аварийный выход (при задании размера буфера равным 5): C:\2011-spring\Лекции\Лекция2\Лаб2c>1 System error code: 111 Запись в таблице System Error Codes: 110….……………………………………………………1 11 ERROR_BUFFER_OVERFLOW 112………………………………………………………. Нормальное выполнение: C:\2011-spring\Лекции\Лекция2\Лаб2c>1 The computer name is EWGENIJ-PC Упражнение: Программно определить пути к системному каталогу Windows и каталогу временных файлов Windows, используя следующие функции Win32 API: UINT GetWindowsDirectory( LPTSTR lpBuffer, UINT uSize ); DWORD GetTempPath( DWORD nBufferLength, LPSTR lpBuffer ); Замечание. Примеры венгерской нотации: Префикс Тип данных u беззнаковое целое lp sz дальний указатель (long pointer) (атавизм) строка, заканчивающаяся нулевым байтом (cстрока) Создание процессов в Windows. Замечание (использование семейств функций exec и spawn в Windows-приложениях. ): В заголовочном файле process.h содержаться макросы и объявления функций exec*, spawn*, которые могут использоваться для создания процессов. Стандарт ANSI/ISO C не включает process.h, но этот заголовочный файл и библиотеки времени исполнения, содержащие реализации соответствующих функций присутстсвуют на многих платформах. В частности, эти функции поддерживаются компиляторами компаний Borland и Microsoft на платформах MS DOS и Windows 3.1. В качестве расширения process.h содержится в стандарте POSIX ( Portable Operating System Interface for Unix ). Замечание: порты Cygwin и Interix Примеры использования exec*: #include <stdio.h> #include <process.h> void main(int argc, char* argv[]){ if(argc<2) return; _execvp(argv[1],argv); printf( "\nProcess was not execed." ); exit( 0 ); } #include <stdio.h> #include <process.h> void main(){ char* argv[]={ "cmd", "/C", "dir", NULL }; _execvp(argv[0],argv); printf( "\nProcess was not execed." ); exit( 0 ); } void main(){ char* argv[]={ "notepad", NULL }; //_spawnvp(_P_OVERLAY, argv[0],argv); _spawnvp(_P_NOWAIT, argv[0],argv); printf( "\nParent process is yet running." ); exit( 0 ); } winspawn.c #include <windows.h> #include <process.h> int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ char* argv[]={ "notepad", NULL }; //_spawnvp(_P_OVERLAY, argv[0],argv); _spawnvp(_P_NOWAIT, argv[0],argv); MessageBox(NULL, "Parent process is yet running.“, "Message",MB_OK); return 0; } #define WINAPI __stdcall /*соглашение для вызова функций Win32 API*/ LPSTR HANDLE HINSTANCE typedef char *LPSTR typedef PVOID HANDLE typedef HANDLE HINSTANCE hInstance – дескриптор текущего экземпляра приложения. hPrevInstance – дескриптор предыдущего экземпляра приложения (рудимент, всегда NULL). lpCmdLine – параметры командной строки. nCmdShow – константа, задающая вид окна. Упражнение: Модифицируйте программу winspawn.c так, чтобы процесс создавался с задержкой в 5 секунд (или 5 часов). Можно использовать функцию Win32 API: VOID GetSystemTime( LPSYSTEMTIME lpSystemTime ); typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME, *LPSYSTEMTIME;