|
|
pl:dydaktyka:unix:lab_prog_system [2016/12/15 09:05] kkutt [Ćwiczenie: Podstawy pracy z procesami] |
pl:dydaktyka:unix:lab_prog_system [2019/06/27 15:50] |
====== Elementy programowania systemowego w środowisku Unix ====== | |
| |
<WRAP center round info 60%> | |
Osoby obecne na zajęciach w poniedziałek 12.12.2016 o godz. 8:00, na których nie działało laboratorium, mogą wysyłać rozwiązania zadań na maila [[kkutt@agh.edu.pl]]. Liczba plusów za przesłanie N zadań to N-1. Maksymalnie można otrzymać **4 plusy** | |
--- //[[kkutt@agh.edu.pl|Krzysztof Kutt]] 2016/12/12 11:52// | |
</WRAP> | |
| |
| |
<code> | |
better !pout !cry | |
better watchout | |
lpr why | |
santa claus town | |
| |
cat /etc/passwd >list | |
ncheck list | |
ncheck list | |
cat list | grep naughty >nogiftlist | |
cat list | grep nice >giftlist | |
santa claus town | |
| |
who | grep sleeping | |
who | grep awake | |
who | egrep 'bad|good' | |
for (goodness sake) { | |
be good | |
} | |
| |
/* Nadesłał: Jakub Pelczar, IS 2012 */ | |
</code> | |
| |
===== DO PRZYGOTOWANIA ===== | |
* Proszę przypomnieć sobie, w jaki sposób kompiluje się programy w języku C w środowisku Unix \\ (np. [[http://home.agh.edu.pl/~gjn/dydaktyka/UGLX/node10.html|Kompilacja programów w środowisku Unix]]). | |
* Proszę przejrzeć manual do funkcji systemowych: **''open(2)''**, **''creat(2)''**, **''read(2)''**, **''write(2)''**, **''stat(2)''**, **''close(2)''**, \\ jak również manuale do funkcji: **''getenv(3)''**, **''putenv(3)''**, **''setenv(3)''** oraz do zmiennych: **''errno(3)''**, **''environ(7)''**. | |
===== WPROWADZENIE ===== | |
| |
| |
==== Temat: Podstawowe operacje na plikach ==== | |
| |
| |
Uwaga! przed korzystaniem z podręcznika można przełączyć go na język polski: | |
| |
export LANG=pl_PL | |
| |
lub angielski | |
| |
export LANG=en_EN | |
| |
Uwaga! sformułowanie typu ,,przeczytać w manualu opis command(x)'' oznacza, że należy wpisać: | |
| |
man x command | |
| |
np.: ,,przejrzeć podręcznik do funkcji //creat(2)//'': | |
| |
man 2 creat | |
| |
| |
=== Deskryptory plików === | |
| |
W systemie Unix dostęp do danych realizowany jest przez pliki. Dostęp procesów do samych plików jest realizowany przez deskryptory plików. Każdy proces ma pulę 20 deskryptorów (0-19), które mogą być przypisane do plików, potoków, itp. Deskryptory są używane we wszystkich funkcjach operujących na plikach. Deskryptor jest reprezentowany przez typ ''int''. | |
| |
| |
=== Tworzenie pliku === | |
| |
Proszę przejrzeć podręcznik do funkcji //creat(2)//. Jak wywołuje się funkcję, co funkcja zwraca? | |
| |
| |
=== Otwarcie pliku === | |
| |
Funkcja //creat()// jest szczególnym przypadkiem //open()//. | |
| |
Proszę przejrzeć podręcznik do funkcji //open(2)//. Jak wywołuje się funkcję, co funkcja zwraca? | |
| |
| |
=== Czytanie z pliku === | |
| |
Proszę przejrzeć podręcznik do funkcji //read(2)//. Jak wywołuje się funkcję, co funkcja zwraca? | |
| |
| |
=== Zapis do pliku === | |
| |
Proszę przejrzeć podręcznik do funkcji //write(2)//. Jak wywołuje się funkcję, co funkcja zwraca? | |
| |
| |
=== Zamknięcie pliku === | |
| |
Proszę przejrzeć podręcznik do funkcji //close(2)//. Jak wywołuje się funkcję, co funkcja zwraca? | |
| |
==== Temat: Zaawansowane operacje na plikach ==== | |
| |
| |
System udostępnia kilka funkcji oferujących zaawansowane operacje na plikach. | |
| |
Do zarządzania prawami dostępu służą np.: //chmod(2)//, //chown(2)//. | |
| |
Funkcje //access(2)//, //lseek(2)//, czy //link(2)// zwiększają możliwości operowania na plikach. | |
| |
| |
=== Informacje o pliku === | |
| |
Proszę przejrzeć podręcznik do funkcji //stat(2)//. Jak wywołuje się funkcję, co funkcja zwraca? | |
| |
==== Temat: Podstawowe operacje na katalogach ==== | |
| |
| |
Katalogi implementowane są przez zwykłe pliki. Tym niemniej w systemie Unix występuje szereg funkcji upraszczających pracę z katalogami. | |
| |
Proszę przejrzeć podręcznik do funkcji //opendir(3)//, //closedir(3)//, //scandir(3)//. Jak wywołuje się te funkcje? | |
| |
==== Temat: Podstawy pracy z procesami ==== | |
| |
| |
| |
=== Środowisko === | |
| |
Proszę przejrzeć podręcznik //environ(7)//. | |
| |
Proszę przejrzeć podręcznik do funkcji //getenv(3)//, //putenv(3)//, //setenv(3)//. | |
| |
| |
=== Uruchamianie programu === | |
| |
Proszę przejrzeć podręcznik do funkcji //execve(2)//. Proszę przejrzeć podręcznik do grupy funkcji //exec(3)//. Funkcje z tej grupy pozwalają na uruchamianie kodu nowego programu, w obrębie już istniejącego, bieżącego procesu. Jak wywołuje się funkcje? Czym się różnią? | |
| |
Nie należy mylić działania tych funkcji z działaniem funkcji //system(3)//, która uruchamia zewnętrzne polecenie. | |
| |
| |
=== Tworzenie nowego procesu === | |
| |
Do tworzenia nowego procesu służy funkcja //fork()//. Tworzy ona proces potomny będący kopią procesu macierzystego, która dziedziczy jego środowisko pracy. | |
| |
Proszę przejrzeć podręcznik do funkcji //fork(2)//. Jak wywołuje się funkcję, co funkcja zwraca? | |
| |
===== ĆWICZENIA ===== | |
| |
| |
==== Ćwiczenie: Podstawowe operacje na plikach ==== | |
| |
| |
W ćwiczeniu będą wykorzystywane przygotowane programy. | |
| |
- Proszę skopiować treść programów ze strony | |
| |
Proszę oglądnąć poniższy program. | |
| |
Program //f1.c//: | |
<code c f1.c> | |
#include <stdio.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
| |
#define BUFSIZE 1024 | |
| |
int main (int argc, char **argv) { | |
int f1, c; | |
char b[BUFSIZE], *n1; | |
| |
c = 10; | |
n1 = argv[1]; | |
| |
f1 = open (n1, O_RDONLY); | |
read (f1, b, c); | |
printf("%s: Przeczytano %d znaków z pliku %s: \"%s\"\n", | |
argv[0], c, n1, b); | |
close(f1); | |
| |
return(0); | |
} | |
</code> | |
| |
skompilować go: | |
| |
gcc -Wall -ansi -pedantic f1.c -o f1 | |
| |
i uruchomić, podając jako argument stworzony wcześniej plik tekstowy. | |
| |
Należy rozbudować program o: | |
| |
* sprawdzanie liczby argumentów wywołania | |
* sprawdzanie rezultatu funkcji //open()// i //read()// (co powinien wypisywać //printf()// jako liczbę przeczytanych znaków?) | |
| |
Proszę oglądnąć poniższy program. | |
| |
Program //f2.c//: | |
| |
<code c f2.c> | |
#include <stdio.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <sys/stat.h> | |
| |
#define BUFSIZE 1024 | |
| |
int main (int argc, char **argv) { | |
int f1, f2, c; | |
char b[BUFSIZE], *n1, *n2; | |
| |
c = 10; | |
n1 = argv[1]; | |
n2 = argv[2]; | |
| |
f1 = open (n1, O_RDONLY); | |
read (f1, b, c); | |
printf("%s: Przeczytano %d znaków z pliku %s: \"%s\"\n", | |
argv[0], c, n1, b); | |
| |
f2 = open (n2, O_WRONLY | O_CREAT | O_TRUNC, 0600); | |
write (f2, b, c); | |
printf("%s: Zapisano %d znaków do pliku %s: \"%s\"\n", | |
argv[0], c, n2, b); | |
| |
close(f1); | |
close(f2); | |
| |
return(0); | |
} | |
</code> | |
| |
skompilować go: | |
| |
gcc -Wall -ansi -pedantic f2.c -o f2 | |
| |
i uruchomić, podając jako argument stworzony wcześniej plik tekstowy i drugi plik, do którego zostaną przepisane dane. | |
| |
Należy rozbudować program o: | |
| |
* funkcje z 1. programu, | |
* możliwość zadania innego trybu dostępu do pliku, | |
* możliwość kopiowania pliku dowolnej długości. | |
| |
==== Ćwiczenie: Podstawowe operacje na katalogach ==== | |
| |
| |
Proszę oglądnąć, skompilować i uruchomić poniższy program. | |
| |
Program //d2.c//: | |
| |
<code c d2.c> | |
#include <stdio.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <sys/dir.h> | |
| |
#define MAX_CHAR 200 | |
| |
int main(int argc, char **argv) { | |
int t; | |
struct direct *e; | |
DIR *d; | |
struct stat s; | |
char p[MAX_CHAR]; | |
| |
d = opendir(argv[1]); | |
while ((e = readdir(d)) != 0) { | |
printf("%d %s", (int)e->d_ino, e->d_name); | |
if (strcmp(e->d_name, ".") != 0 && | |
strcmp(e->d_name, "..") != 0) | |
printf("\n"); | |
else { | |
p[0] = 0; | |
strcat(p, argv[1]); | |
strcat(p, "/"); | |
strcat(p, e->d_name); | |
t = stat(p, &s); | |
if (S_ISDIR(s.st_mode)) | |
printf("/"); | |
printf("\n"); | |
} | |
} | |
closedir(d); | |
return 0; | |
} | |
</code> | |
| |
Jest to prymitywny program typu //ls//. | |
| |
Należy rozbudować program o: | |
| |
* precyzyjne sprawdzanie wartości zwracanych przez funkcje, a co za tym idzie podstawową diagnostykę błędów, | |
* jak najwięcej możliwości typu //ls//, czyli czytanie kolejnych danych zwracanych przez funkcję //stat()//, | |
* możliwość wyświetlania zawartości podkatalogów. | |
| |
==== Ćwiczenie: Środowisko pracy procesu ==== | |
| |
Proszę oglądnąć i uruchomić poniższy program: | |
| |
Program //p1.c//: | |
| |
<code c p1.c> | |
#include <stdio.h> | |
#include <unistd.h> | |
| |
extern char **environ; | |
| |
int main (int argc, char **argv, char **envp) { | |
int i; | |
| |
printf("Srodowisko procesu:\n"); | |
for (i = 0; envp[i] != NULL;i++) | |
printf("%s\n", envp[i]); | |
| |
return(0); | |
} | |
</code> | |
| |
Proszę zmodyfikować program, aby pozwalał na wypisywanie i zmianę wartości wybranej zmiennej środowiskowej. | |
| |
==== Ćwiczenie: Podstawy pracy z procesami ==== | |
| |
| |
Proszę oglądnąć i uruchomić poniższy program: | |
| |
Program //p2.c//: | |
| |
<code c p2.c> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
| |
extern char **environ; | |
| |
int main (int argc, char **argv, char **envp) { | |
| |
printf("Poczatek procesu.\n"); | |
| |
system("echo ala ma kota"); | |
printf("Dalszy ciag kodu...\n"); | |
| |
execl("/bin/echo", "echo", "jakis napis", NULL); | |
printf("Koniec kodu...\n"); | |
| |
return(0); | |
} | |
</code> | |
| |
Jaka jest różnica pomiędzy funkcjami //system()// a //exec()//? | |
| |
Proszę zmodyfikować program tak, aby działał tak samo przy użyciu innych wywołań z rodziny funkcji //exec()//. | |
| |
Proszę oglądnąć i uruchomić poniższy program. | |
| |
Program //p3.c//: | |
| |
<code c p3.c> | |
#include <stdio.h> | |
#include <unistd.h> | |
| |
extern char **environ; | |
| |
int main (int argc, char **argv, char **envp) { | |
int p=0; | |
| |
printf("Poczatek procesu...\n"); | |
p = fork(); | |
printf("Tu jestem: %d\n", p); | |
| |
return(0); | |
} | |
</code> | |
| |
Jak działa program? Dlaczego? | |
| |
Dla lepszego zrozumienia proszę oglądnąć i uruchomić kolejny program. | |
| |
Program //p4.c//: | |
| |
<code c p4.c> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
| |
extern char **environ; | |
| |
int main (int argc, char **argv, char **envp) { | |
int p=0; | |
| |
printf("%s[%d]: Poczatek procesu glownego...\n", | |
*argv, getpid()); | |
p = fork(); | |
if (p == -1) | |
printf("%s[%d]: BLAD! Nie moge stworzyc procesu!\n", | |
*argv, getpid()); | |
else if ( p > 0) { | |
printf("%s[%d]: To dalej ja, proces glowny...\n", | |
*argv, getpid()); | |
sleep(5); | |
} | |
else if ( p == 0 ) { | |
printf("%s[%d]: Jestem procesem potomnym, moj rodzic to: [%d]...\n", | |
*argv, getpid(), getppid()); | |
exit (0); | |
} | |
| |
printf("%s[%d]: Koniec procesu glownego...\n", | |
*argv, getpid()); | |
exit (0); | |
return(0); | |
} | |
</code> | |
| |
Proszę otoczyć komentarzem wywołanie funkcji //sleep()//, jak to wpłynie na działanie procesów? | |
| |
Proszę oglądnąć i uruchomić poniższy program. | |
| |
Program //p5.c//: | |
| |
<code c p5.c> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <sys/wait.h> | |
| |
extern char **environ; | |
| |
int main (int argc, char **argv, char **envp) { | |
int p=0,p1=0; | |
| |
printf("%s[%d]: Poczatek procesu glownego...\n", | |
*argv, getpid()); | |
p = fork(); | |
if (p == -1) | |
printf("%s[%d]: BLAD! Nie moge stworzyc procesu!\n", | |
*argv, getpid()); | |
else if ( p > 0) { | |
printf("%s[%d]: To dalej ja, proces glowny...\n", | |
*argv, getpid()); | |
} | |
else if ( p == 0 ) { | |
printf("%s[%d]: Jestem procesem potomnym, moj rodzic to: [%d]...\n", | |
*argv, getpid(), getppid()); | |
sleep(5); | |
printf("%s[%d]: Koncze ze soba!\n", | |
*argv, getpid()); | |
exit (0); | |
} | |
| |
p1=wait(NULL); | |
printf("%s[%d]: Jestem bezdzietny, nie ma juz: %d :(\n", | |
*argv, getpid(), p1); | |
| |
printf("%s[%d]: Koniec procesu glownego.\n", | |
*argv, getpid()); | |
exit (0); | |
return(0); | |
} | |
</code> | |
| |
Procesy macierzyste mogą czekać na zakończenie potomnych. | |
| |
Proszę rozbudować powyższy program, wg. własnej inwencji. | |
| |
==== Ćwiczenie: Procesy i uruchamianie programów ==== | |
| |
| |
Proszę oglądnąć i uruchomić poniższy program. | |
| |
Program //p6.c//: | |
| |
<code c p6.c> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <sys/wait.h> | |
| |
extern char **environ; | |
| |
int main (int argc, char **argv, char **envp) { | |
int p=0,p1=0; | |
| |
printf("%s[%d]: Poczatek procesu glownego...\n", | |
*argv, getpid()); | |
p = fork(); | |
if (p == -1) | |
printf("%s[%d]: BLAD! Nie moge stworzyc procesu!\n", | |
*argv, getpid()); | |
else if ( p > 0) { | |
printf("%s[%d]: To dalej ja, proces glowny...\n", | |
*argv, getpid()); | |
} | |
else if ( p == 0 ) { | |
printf("%s[%d]: Jestem procesem potomnym, moj rodzic to: [%d]...\n", | |
*argv, getpid(), getppid()); | |
printf("%s[%d]: Moge byc kims innym!\n", | |
*argv, getpid()); | |
execl("/bin/echo", "echo", "moge stac sie programem ktory cos pisze!", NULL); | |
} | |
| |
p1=wait(NULL); | |
printf("%s[%d]: Jestem bezdzietny, nie ma juz: %d :(\n", | |
*argv, getpid(), p1); | |
| |
printf("%s[%d]: Koniec procesu glownego.\n", | |
*argv, getpid()); | |
exit (0); | |
return(0); | |
} | |
</code> | |
| |
==== Ćwiczenie: Procesy i zrównoleglanie pracy ==== | |
| |
Proszę oglądnąć i uruchomić poniższy program. | |
| |
Program //p7.c//: | |
| |
<code c p7.c> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <sys/wait.h> | |
#include <fcntl.h> | |
#include <sys/stat.h> | |
| |
#define BUFSIZE 1024 | |
#define CPC 5 | |
#define NC 5 | |
| |
extern char **environ; | |
| |
int main (int argc, char **argv, char **envp) { | |
int p=0, p1=0, f, n=5, c, i, j; | |
char *b, *n1; | |
| |
c = NC; | |
n1 = argv[1]; | |
| |
printf("%s[%d]: Poczatek procesu glownego...\n", | |
*argv, getpid()); | |
f = open (n1, O_RDONLY); | |
for (i=0; i<n; i++) { | |
p = fork(); | |
if (p == -1) | |
printf("%s[%d]: BLAD! Nie moge stworzyc procesu!\n", | |
*argv, getpid()); | |
else if ( p > 0) { | |
printf("%s[%d]: To dalej ja, proces glowny...\n", | |
*argv, getpid()); | |
} | |
else if ( p == 0 ) { | |
printf("%s[%d]: Jestem procesem potomnym, moj rodzic to: [%d]...\n", | |
*argv, getpid(), getppid()); | |
sleep(1); | |
lseek(f, i*CPC, SEEK_SET); | |
b = malloc(sizeof(char)*c+1); | |
j = read (f, b, c); | |
b[c+1]='\n'; | |
printf("%s: Przeczytano %d znaków, poczynajac od: %d, z pliku %s: \"%s\"\n", | |
argv[0], j, i*CPC, n1, b); | |
free(b); | |
exit(0); | |
} | |
} | |
| |
p1=wait(NULL); | |
printf("%s[%d]: Jestem bezdzietny, ostatnie dziecko to: %d :(\n", | |
*argv, getpid(), p1); | |
close(f); | |
printf("%s[%d]: Koniec procesu glownego.\n", | |
*argv, getpid()); | |
exit (0); | |
return(0); | |
} | |
</code> | |
| |
Ważna obserwacja: proces potomny dziedziczy środowisko, wraz z kopiami deskryptorów plików. | |
| |
Proszę przeanalizować i zmodyfikować powyższy program, np. tak, aby czytał inne fragmenty pliku, lub wykonywał równolegle inne operacje. | |
| |
===== BIBLIOGRAFIA ===== | |
| |
Ważne, przydatne książki: | |
| |
* Brian W. Kernighan, Dennis M. Ritchie, //Język ANSI C (org. The C Programming Language (Second Edition))//, WNT, Warszawa, 1994 i następne. | |
* Brian W. Kernighan, Rob Pike, //The UNIX Programming Environment//, Prentice Hall, 1984. | |
* Marc J. Rochkind, //Programowanie w systemie Unix dla zaawansowanych (arg. Advanced Unix Programming)//, WNT, Warszawa, 1997. | |
* W. Richard Stevens, //Advanced Programming in the UNIX Environment//, Addison-Wesley Professional, 1992. | |
* Eric S. Raymond, //The Art of UNIX Programming//, Addison-Wesley Professional, 2003. | |
| |