Nr grupy: 4 |
Imię i nazwisko:PAWEŁ ŻAK |
Nazwisko prowadzącego: dr. inż. Adam Mrozek |
Tytuł :SPRAWOZDANIE Z LABORATORIÓW Z PRZEDMIOTU PROGRAMOWANIE RÓWNOLEGŁE |
Data oddania sprawozdania: 09.04.2014 |
Zaliczenia/ocena: |
PR - laboratorium 1
Cel:
1. Przeprowadzenie pomiaru czasu CPU i zegarowego tworzenia procesów i wątków
systemowych Linuxa.
2. Nabycie umiejętności posługiwania się programami wykorzystującymi tworzenie
wątków i procesów.
Zrealizowane kroki:
1. Utworzenie katalogu roboczego (np. lab1), skopiowanie ze strony WWW do
katalogu roboczego pliku „fork_clone.tgz” i rozpakowanie plików – w terminalu
systemu Linux.
2. Przygotowanie pliku Makefile (na podstawie dołączonego w archiwum z
przykładami) usprawniającego kompilację programów.
3. Uzupełnienie plików źródłowych m.in. o procedury pomiaru czasu (biblioteka z
archiwum pomiar_czasu.tgz) i przeprowadzenie eksperymentu mierzącego czas
tworzenia 1000 procesów i wątków.
4. Modyfikacja kodu programów tak, aby procedura wątku i program uruchamiany (za
pomocą funkcji exec) przez nowo utworzony proces wypisywały na ekranie
indywidualnie zaprojektowane dane (np. imię i nazwisko, numer procesu lub itp.).
Pomiar czasu działania programów.
Część teoretyczna
Funkcja fork()
-jest funkcją w systemie operacyjnym Unix (i nie tylko) powodującą, że pojedynczemu programowi, któremu do jego wykonania przypisany jest pojedynczy proces w systemie operacyjnym, przypisuje się dwa procesy poprzez rozwidlenie: rodzic i dziecko - czyli tworzy nowy proces. W chwili utworzenia, proces-dziecko jest kopią procesu-rodzica (kopiowane są obszary pamięci, wartości zmiennych i część środowiska).
Funkcja clone()
-funkcja ta umozliwia w pelni wykorzystanie mocy do_fork-a. Umozliwia klonowanie, czyli wspoldzielenie, przez ojca i potomka pewnych zasobow (jednego lub wielu) - pamieci, tablicy deskryptorow, tablicy obslugi sygnalow a nawet - uwaga! - idetyfikatora. W ten sposob, trzeba przyznac - bardzo sprytny, sa wspomagane w Linuxie watki. Jednak aby funkcje mozna bylo wywolac (domyslnie jest ona niedostepna), trzeba skompilowac jadro ze zdefiniowanym symbolem CLONE_ACTUALLY_WORKS_OK.
Make FILE
-Narzędzie make służy do zarządzania kompilacją projektów składających się z wielu plików źródłowych.
Aby używać make należy napisać skrypt o nazwie Makefile lub makefile, w którym opisane są: zależności pomiędzy plikami źródłowymi i plikami wynikowymi a także sposób tworzenia plików wynikowych z plików źródłowych.
Następnie przy pomocy polecenia make kompilujemy projekt.
Make usprawnia kompilację, gdyż samodzielnie decyduje, które z plików źródłowych mają być przekompilowane (sprawdzając daty ostatniej modyfikacji).
PR - laboratorium 2
Cel:
- opanowanie podstaw wykorzystywania bibliotek wątków „pthread”,
- synchronizacja wątków
Zrealizowane kroki:
1. Utworzenie katalogu roboczego (np. lab2)
2. Opracowanie (na podstawie wiadomości z wykładu lub skryptu) programu
uruchamiającego na dziesięciu wątkach funkcję wyświetlającą komunikat rozpoczęcia,
zakończenia oraz identyfikator wątku.
Przykładowy wydruk:
Jestem wątkiem numer 1
Kończę wątek numer 1
…
Czy wątki się uruchamiają i kończą po kolei? Dlaczego tak/nie?
Program PTH1.c
3. Opracowanie programu tworzącego dwa wątki: jeden inkrementuje zmienną globalną do
1000000, a drugi ją dekrementuje tyle samo razy.
Jaką wartość ma na końcu ta zmienna i dlaczego? Program należy uruchomić kilka razy.
Rozwiąż problem by wartość zmiennej globalnej wyniosła 0.
W przypadku korzystania z maszyny wirtualnej program uruchomić w systemie jedno- i
kilkurdzeniowym. Jakie będą wyniki i dlaczego?
Program PTH2.c
4. Zaimplementowanie algorytmu realizującego funkcję bariery tak, aby wątki uruchamiane
przez program z p.2 kończyły się dopiero po uruchomieniu wszystkich wątków. Ze
względu na tematykę laboratorium wskazane jest użycie funkcji z biblioteki ptherads (np.
pthread_cond_wait, pthread_cond_broadcast)
Przykładowy wydruk:
Jestem wątkiem numer 1
Jestem wątkiem numer 2
Kończę wątek numer 1
Kończę wątek numer 2
Program PTH3.c
WNIOSKI:
LABORATORIUM nr.1:
Obydwie funkcje to jest fork() i clone() mogą być wkorzystane do tworzenia nowych procesów. Funkcja clone jest takim rozszerzeniem funkcji fork() i oprócz możliwości funkcji fork() przy zastosowaniu odpowiednich flag w argumentach funkcji clone() jest w stanie stworzyć nowy wątek.
Z otrzymanych wyników wywnioskować można, że funkcja clone jest szybsza od funkcji clone o jakieś 12% co przy programach których wykonanie zajmuje bardzo długi okres daje zauważalną różnicę.
LABORATORIUM nr.2:
Mutexy są w stanie zapobiegać problemowi wyścigu dzięki temu wątki dostają indywidualny dostęp do zmiennych znajdujących się w sekcji krytycznej.
Wątki tworzą sie nie pokolei i programista nie ma bezpośredniego wpływu na kolejność uruchamiania wątków.
Funkcja pthread_cond_wait() jest w stanie zatrzymać wykonywanie wątku. Natiomiast funkcja pthread_cond_broadcast wznawia zatrzymane wątki. Wszystkie te funkcję stosowane są aby jak najlepiej synchronizować wątki.
-Kody programów:
LABORATORIUM nr.1:
FORK.C
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include "pomiar_czasu/pomiar_czasu.h"
int zmienna_globalna=0;
main(){
char * tablica[]={0};
int pid, wynik, i;
inicjuj_czas();
for(i=0;i<1000;i++){
pid = fork();
if(pid == 0){
zmienna_globalna++;
wynik=execv("./program",tablica);
if(wynik==-1)
printf("Proces potomny nie wykonal programu\n");
exit(0);
} else {
wait(NULL); } }
drukuj_czas(); }
CLONE.C
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/sched.h>
#include "pomiar_czasu/pomiar_czasu.h"
int zmienna_globalna=0;
#define ROZMIAR_STOSU 1024*64
int funkcja_watku( void* argument )
{
zmienna_globalna++;
int wynik;
char* tab[] = {0};
wynik=execv("./program",tab);
if(wynik==-1)
printf("Proces potomny nie wykonal programu\n");
return 0;
}
main()
{
void *stos;
pid_t pid;
int i;
inicjuj_czas();
stos = malloc( ROZMIAR_STOSU );
if (stos == 0) {
printf("Proces nadrzędny - blad alokacji stosu\n");
exit( 1 );
}
for(i=0;i<1000;i++){
pid = clone( &funkcja_watku, (void *) stos+ROZMIAR_STOSU, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM, 0 );
waitpid(pid, NULL, __WCLONE);
}
free( stos );
drukuj_czas();
}
MAKE FILE
# kompilator c
CCOMP = gcc
# konsolidator
LOADER = gcc
# opcje optymalizacji:
# wersja do debugowania
OPT = -g -DDEBUG
# wersja zoptymalizowana do mierzenia czasu
# OPT = -O3
# pliki naglowkowe
INC = -I../pomiar_czasu
# biblioteki
LIB = -L../pomiar_czasu -lm
# zaleznosci i komendy
all: fork clone
fork: fork.o pomiar_czasu.o
$(LOADER) $(OPT) fork.o pomiar_czasu.o -o fork $(LIB)
clone: clone.o pomiar_czasu.o
$(LOADER) $(OPT) clone.o pomiar_czasu.o -o clone $(LIB)
# jak uzyskac plik moj_program.o ?
clone.o: clone.c
$(CCOMP) -c $(OPT) clone.c
fork.o: fork.c
$(CCOMP) -c $(OPT) fork.c
pomiar_czasu.o: pomiar_czasu.c pomiar_czasu.h
$(CCOMP) -c $(OPT) pomiar_czasu.c
clean:
rm -f *.o
LABORATORIUM nr.2:
PTH1.c
#include<stdlib.h>
#include<stdio.h>
#include<pthread.h>
#define NUM_THREADS 10
void * HelloThread(void *tID)
{ printf("Jestem watkiem nr :%i \n",(int)tID);
printf("Koncze watek nr:%i \n",(int)tID);
pthread_exit(NULL);
}
int main()
{
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
int rc,t,status;
//pthread_attr_init(&attr);
//pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
for(t = 0;t<NUM_THREADS;t++)
{
rc = pthread_create(&threads[t],NULL,HelloThread,(void*) t);
if(rc)
{
printf("ERROR %i",rc);
exit(-1);
}
}
pthread_exit(NULL);
return 0;
}
PTH2.c
#include<stdlib.h>
#include<stdio.h>
#include <pthread.h>
#define NUM_THREADS 2
int globalVar = 0;
pthread_mutex_t mutex1;
int ileWatkow;
void * AddVariable(void *tID)int i;
for(i = 0 ;i<1000000;i++)
{
pthread_mutex_lock(&mutex1);
globalVar++;
pthread_mutex_unlock(&mutex1);
}
pthread_exit(NULL);
}
void * MinusVariable(void *tID) PROGRAM Z BŁĘDEM
{
int i;
for(i = 0 ;i<1000000;i++)
{
pthread_mutex_lock(&mutex1);
globalVar--;
pthread_mutex_unlock(&mutex1);
}
pthread_exit(NULL);
}
int main()
{
pthread_t thread1;
pthread_t thread2;
int t;
t = 1;
pthread_mutex_init(&mutex1,NULL);
pthread_create(&thread1,NULL,AddVariable,(void*)t);
pthread_create(&thread2,NULL,MinusVariable,(void*)t);
/*MPI count wait - usypianie watkow
* jesli liczba uruchomionych watkow jest mniejsza od danej
*
*/
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
printf("Zmienna: %i",globalVar);
pthread_exit(NULL);
return 0;
}
PTH3.c
#include<stdlib.h>
#include<stdio.h>
#include<pthread.h>
#define NUM_THREADS 10
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int condition = 0;
void * RobWatek(void *tID);
int main()
{
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
int rc,t,status;
for(t = 0;t<NUM_THREADS;t++)
{
rc = pthread_create(&threads[t],NULL,RobWatek,(void*) t);
if(rc)
{
printf("ERROR %i",rc);
exit(-1);
}
}
sleep(1);
rc = pthread_cond_broadcast(&cond);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
pthread_exit(NULL);
return 0;
}
void * RobWatek(void *tID)
{
int rc;
pthread_mutex_lock(&mutex);
printf("Jestem watkiem nr :%i \n",(int)tID);
rc = pthread_cond_wait(&cond, &mutex);
printf("Koncze watek nr:%i \n",(int)tID);
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}