Список використаної літератури

1. Help по функціях API – файл WIN32.HLP, який знаходиться в мережі FКІТ за адресою \\nt-backup\Predmetu\SPZ\HELP_WIN32APIENG\

2. Microsoft Windows Help - Win32 Programmers Reference

3. Технічна документація Microsoft - MSDN
Додаток 1

 Програма друку назви системної валюти

(вказується користувачем при інсталяції ОС).

 

.386

.model flat, stdcall

include c:\masm32\include\windows.inc

include c:\masm32\include\kernel32.inc

include c:\masm32\include\advapi32.inc

include c:\masm32\include\user32.inc

include c:\masm32\include\masm32.inc

includelib c:\masm32\lib\kernel32.lib

includelib c:\masm32\lib\user32.lib

includelib c:\masm32\lib\masm32.lib

includelib c:\masm32\lib\advapi32.lib

 

.data

 

Subkey db "Control Panel\International\",0

hkey dd?

val_n db "sCurrency",0

 

val db 255 dup (0)

val_l dd 255

 

.code

start:

invoke RegOpenKeyEx,HKEY_CURRENT_USER, addr Subkey, 0, KEY_EXECUTE, addr hkey

invoke RegQueryValueEx, hkey, addr val_n, 0, NULL, addr val, addr val_l

invoke MessageBox,0, addr val, addr val, 0

invoke ExitProcess,0

 

end start

 




Лабораторна робота №6

 

Тема: Ствоення обчислювальних процесів та потоків в ОС Linux.

Мета: Навчитися програмним шляхом породжувати обчислювальні процеси та потоки в UNIX подібних операційних системах.

 

Основні теоретичні відомості

 

Породження процесів

 

Звичайно, новий процес в Unix-подібних операційних системах створюється за допомогою системного виклику fork():

 

#include <sys/types.h>

#include <unistd.h>

 

pid_t fork(void);

 

Функція fork() створює новий процес, який відрізняється від батьківського лише своїм номером в системі. Тобто відбувається копіювання всього адресного простору та таблиці відкритих файлових дескрипторів батьківського процесу. При успішному виконанні функція fork() повертає номер нового процесу в системі, інакше повертається -1 і глобальна змінна errno встановлюється у відповідне значення. Детальніше про можливі значення errno можна дізнатися з on-line допомоги по функції fork() командою man.

Далі необхідно асоціювати новостворений процес з конкретною задачею, що робиться за допомогою системного виклику execve():

 

#include <unistd.h>

 

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

 

Функція execve() виконує програму filename, з аргументами командної стрічки argv та масивом змінних оточення envp. При успішному виклику функція execve() не повертається, інакше повертається -1 і встановлюється глобальна змінна errno. Детальніше про можливі коди помилки - в on-line документації по execve().

Таким чином наступний фрагмент коду реалізує послідовність дій по створенню нового процесу:

 

 


...

/* батьківський процес */

...

pid_t child;

 

if ((child = fork()) > 0)

{

...

/* код породженого процесу */

  

if ((execve("нова_програма", 0, 0)) < 0)

{

   perror("execve ");

   exit(1);

}

} else if (child < 0)

{

perror("fork ");

exit(1);

}

 

/*

батьківський процес продовжує своє виконання тут

*/

...

 

Ось приклад програми, що запускає новий процес і передає йому його номер в системі, повернений функцією fork(). Породжений процес в свою чергу в нескінченному циклі виводить свій номер на пристрій stdout. Батьківський процес після створення нового процесу також у нескінченному циклі виводить на stdout стрічку "parent\n". Таким чином можна бачити, коли виконується породжений процес, а коли батьківський.

 

Код батьківського процесу:

 

/* parent.c */

 

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

 

#define DELAY 100000000

 

int main(int argc, char **argv)

{

int i;

pid_t child_1;

char **argv_c;

  

argv_c = (char **)calloc(1, sizeof(char **));

if (! argv_c)

{

   perror("calloc ");

   exit(1);

}

argv_c[0] = (char *)calloc(1, 256);

if (! argv_c[0])

{

   perror("calloc ");

   exit(1);

}

if ((child_1 = fork()) > 0)

{

   sprintf(argv_c[0], "%d", child_1);

 

   if (execve("child", argv_c, 0) < 0)

   {

        perror("execve:");

        exit(1);

   }

} else

if (child_1 < 0)

{

   perror("fork:");

   exit(1);

}

 

free(argv_c[0]);

free(argv_c);

 

for (;;)

{

   printf("parent\n");

 

 

   for (i = 0; i < DELAY; i++)

       ;

}

  

return 0;

}

 

Код породженого процесу:

 

/* child.c */

#include <stdio.h>

#include <stdlib.h>

 

#define DELAY 100000000

 

int main(int argc, char *argv[])

{

int i;

 

for (;;)

{

   fprintf(stdout, "%s%s%s","child ", argv[0], "\n");

   for (i = 0; i < DELAY; i++)

       ;

}

return 0;

}

 

Компіляція parent.c та child.c відбувається наступним чином:

 

$ gcc -o parent parent.c

$ gcc -o child child.c

$./parent

parent

child 1866

child 1866

parent

child 1866

parent

parent

child 1866

child 1866

parent

 

Потрібно зазначити, що викликати функцію execve() одразу після fork() не завжди обов'язково. Це робиться лише тоді, коли ми хочемо з нашої програми запустити на виконання нову задачу. Але якщо потрібно провести деякі обчислення або операції, і ми не хочемо, щоб вони блокували головний потік виконання програми, то код цих обчислень або операцій можна вставити одразу після успішного виклику fork(), і вони будуть виконуватись в новому процесі аж до виклику функції exit() або _ exit().

 


Породження потоків

 

В традиційній моделі UNIX систем, якщо процес потребує, щоб деяка його частина була реалізована через іншу функціональну одиницю, то він викликає функцію fork() для створення нового процесу і переказує відповідну частину завдання цьому новому процесові. В такий спосіб працює більшість мережевих серверів в UNIX системах.

Протягом багатьох років такий принцип роботи досить вдало виправдовував себе, але виникають певні проблеми, пов'язані з викликом функції fork():

- виклик fork() є “дорогою” операцією. Весь вміст адресного простору батьківського процесу копіюється в адресний простір нового процесу разом з дескрипторами відкритих файлів та обробниками сигналів. Звичайно, реалізація fork() відбувається за принципом копіювання при-записі (copy-on-write, COW), у відповідності з яким копіювання сторінок пам’яті батьківського процесу в адресний простір породженого процесу відбувається лише за умови, коли породжений процес намагається змінити дані відповідного батьківського процесу. Така техніка дозволяє дещо підвищити ефективність системи, адже в більшості випадків після створення нового процесу батьківський процес одразу викликає exec().

- проблематичність при реалізації механізмів міжпроцесової взаємодії: якщо комунікація від батьківського процесу до породженого відбувається досить легко, то в зворотньому випадку виникають деякі труднощі.

Одним із способів породження потоків в ОС Linux є використання бібліотеки LinuxTreads, яка є реалізацією стандарту POSIX на обчислювальні потоки.

Однією з ідей стосовно потоків є те, що функція, яка створює новий потік, в якості параметра приймає вказівник на функцію, з якої починається новий потік обчислень.

 

Для запуску нового потоку використовується функція pthread_create():

 

#include <pthread.h>

 

int pthread_create(pthread_t * thread, pthread_attr_t *

attr, void * (*start_routine)(void *), void * arg); 

 

pthread_t * thread - OUT - ідентифікатор новоствореного потоку;

pthread_attr_t * attr - IN - атрибути нового потоку;

void * (*start_routine)(void *) - IN - адреса функції, з якої почнеться новий потік;

void * arg - IN - адреса єдиного аргументу, що можна передати функції start_routine;

 

При успішному виконанні по адресі thread повертається ідентифікатор нового потоку і 0 у процес, що викликається; інакше у цей процес повертається ненульовий код помилки. Детальніше про код помилки у on-line документації по pthread_create.

Після того, як створено новий потік, виникає необхідність вказати викликаючому процесу не завершувати своє виконання, поки не завершиться цей новий потік. Для цього використовується функція pthread_join():

 

 #include <pthread.h>

 

 int pthread_join(pthread_t th, void **thread_return);

 

pthread_t th - IN - ідентифікатор потоку, на чиє закінчення потрібно зачекати

void **thread_return - OUT - адреса, по якій зберігається значення, повернуте потоком th при своєму закінченні.

При успішному виконанні по адресі thread_return зберігається значення, повернуте потоком th, і у викликаючий поток повертається 0. Інакше повертається ненульовий код помилки.

Ось приклад програми, що запускає новий обчислювальний потік, який у нескінченному циклі виводить в stdout свій ідентифікатор в системі.

 

#include <stdio.h>

#include <pthread.h>

 

#define DELAY 10000000

 

void *foo(void *);

 

int main(int argc, char **argv)

{

pthread_t tdi;

 

pthread_create(&tid, NULL, foo, 0);

pthread_join(tid, 0);

return 0;

}

void *foo(void *arg)

{

int i;

pthread_t tid = pthread_self();

for (;;)

{

   printf("%s%d%s", "thread ", tid, "\n");

   fflush(stdout);

   for (i = 0; i < DELAY; i++)

       ;

}

}

 

Хід роботи

1. Ознайомитись з основними теоретичними відомостями даної лабораторної роботи.

2. Ознайомитись з приведеними в основних теоретичних відомостях прикладами програм.

3. Затвердити номер індивідуального завдання у викладача.

4. Виконати завдання згідно з затвердженим номером.

Зміст звіту

Звіт повинен містити:

 - титульну сторінку;

 - тему, мету роботи;

- завдання, робочий варіант тексту програми, результат виконання програми;

- тексти зовнішніх модулів, використовуваних при компілюванні;

- короткі висновки по проведеній роботі.

 


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: