====== Elementy programowania systemowego w środowisku Unix ====== 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 */ ===== 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//: #include #include #include #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); } 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//: #include #include #include #include #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); } 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//: #include #include #include #include #include #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; } 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//: #include #include 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); } 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//: #include #include #include 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); } 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//: #include #include 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); } Jak działa program? Dlaczego? Dla lepszego zrozumienia proszę oglądnąć i uruchomić kolejny program. Program //p4.c//: #include #include #include 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); } 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//: #include #include #include #include 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); } 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//: #include #include #include #include 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); } ==== Ćwiczenie: Procesy i zrównoleglanie pracy ==== Proszę oglądnąć i uruchomić poniższy program. Program //p7.c//: #include #include #include #include #include #include #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 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); } 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.