courses:unix:lab_sysprog

  • Proszę przypomnieć sobie, w jaki sposób kompiluje się programy w języku C w środowisku Unix (np. zajrzeć tutaj).
  • 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).

1. Operacje na plikach

  • 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: zob. manual do funkcji creat(2)
  • Otwarcie pliku: zob. manual do funkcji open(2)
    • Funkcja creat() jest szczególnym przypadkiem open() - na serwerze SPK mają dokładnie tę samą stronę w manualu.
  • Czytanie z pliku: zob. manual do funkcji read(2)
  • Zapis do pliku: zob. manual do funkcji write(2)
  • Zamknięcie pliku: zob. manual do funkcji close(2)

System udostępnia również 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,
  • stat(2) zwraca informacje o pliku.

2. Podstawowe operacje na katalogach

  • Katalogi implementowane są przez zwykłe pliki (co oznacza, że można na nich używać funkcji dla plików).
  • W systemie Unix występuje również szereg funkcji upraszczających pracę z katalogami, m.in.:
    • opendir(3), closedir(3),
    • scandir(3).

3. Praca z procesami

  • Środowisko pracy:
    • ogólny opis w environ(7),
    • funkcje operujące na środowisku: getenv(3), putenv(3), setenv(3).
  • Uruchamianie nowych programów w obrębie bieżącego procesu:
    • odpowiedzialna funkcja: execve(2)
    • “wrapperami” dla użytkownika jest grupa funkcji exec(3) [czym się różnią między sobą?]
    • Nie należy ich mylić z funkcją system(3), która uruchamia zewnętrzne polecenie w osobnym procesie (i zbiera wyjście)
  • Tworzenie nowych procesów:
    • Funkcja fork(2)
    • Tworzy ona proces potomny będący kopią procesu macierzystego, który dziedziczy jego środowisko pracy.

I. Operacje na plikach

  1. Proszę ściągnąć i obejrzeć poniższy program, a następnie zrealizować polecenia umieszczone w kolejnych podpunktach:
    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);
    }
    1. skompiluj program:
      gcc -Wall -ansi -pedantic f1.c -o f1
    2. uruchom program podając jako argument stworzony wcześniej plik tekstowy
    3. rozbuduj program o sprawdzanie liczby argumentów wywołania
    4. rozbuduj program o sprawdzanie rezultatu funkcji open() i read()
      • wskazówka 1: co powinien wypisywać printf() jako liczbę przeczytanych znaków?
      • wskazówka 2: dlaczego przy wypisywaniu znaków, czasami na końcu wyświetlane są “śmieci”?
  2. Proszę ściągnąć i obejrzeć poniższy program, a następnie zrealizować polecenia umieszczone w kolejnych podpunktach:
    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);
    }
    1. skompiluj program:
      gcc -Wall -ansi -pedantic f2.c -o f2
    2. uruchom program [ile i jakich argumentów należy podać? co ma robić program?]
    3. rozbuduj program o funkcje z 1. programu
    4. rozbuduj program o możliwość kopiowania pliku dowolnej długości

II. Podstawowe operacje na katalogach

  1. Proszę ściągnąć, obejrzeć, skompilować i uruchomić poniższy program (jest to prymitywny program typu ls), a następnie zrealizować polecenia umieszczone w kolejnych podpunktach:
    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;
    }
    1. rozbuduj program o precyzyjne sprawdzanie wartości zwracanych przez funkcje, a co za tym idzie podstawową diagnostykę błędów
    2. rozbuduj program o jak najwięcej funkcjonalności polecenia ls, czyli czytanie kolejnych danych zwracanych przez funkcję stat()
    3. rozbuduj program o możliwość wyświetlania zawartości podkatalogów

III. Praca z procesami

  1. Proszę ściągnąć, obejrzeć, skompilować i uruchomić poniższy program:
    p1.c
    #include <stdio.h>
    #include <unistd.h>
     
    extern char **environ;  /* można skorzystać ze zmiennej environ z unistd.h ... */
     
    int main (int argc, char **argv, char **envp) {  /* ... albo z trzeciego argumentu main() */
        int i;
     
        printf("Srodowisko procesu:\n");
        for (i = 0; envp[i] != NULL;i++)
    	printf("%s\n", envp[i]);
     
        return 0;
    }
    1. zmodyfikuj program, aby pozwalał na wypisanie i zmianę wartości wybranej zmiennej środowiskowej
  2. Proszę ściągnąć, obejrzeć, skompilować i uruchomić poniższy program:
    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;
    }
    1. jaka jest różnica pomiędzy funkcjami system() a exec()?
    2. zmodyfikuj program tak, aby działał tak samo przy użyciu innych wywołań z rodziny funkcji exec()
  3. Proszę ściągnąć, obejrzeć, skompilować i uruchomić poniższy program:
    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;
    }
    1. jak działa program? Dlaczego?
  4. Dla lepszego zrozumienia proszę obejrzeć i uruchomić kolejny program:
    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());
        return 0;
    }
    1. proszę otoczyć komentarzem wywołanie funkcji sleep(), jak to wpłynie na działanie procesów?
  5. Proszę ściągnąć, obejrzeć, skompilować i uruchomić poniższy program (i zobaczyć w jaki sposób proces macierzysty może czekać na procesy potomne):
    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());
        return 0;
    }
    1. tutaj również otocz komentarzem wywołanie funkcji sleep() - co się zmieniło? czym różni się ten program od poprzedniego?
  6. Proszę ściągnąć, obejrzeć, skompilować i uruchomić poniższy program (i zobaczyć w jaki sposób można zmienić program dla wybranych procesów):
    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());
        return 0;
    }
  7. Proszę ściągnąć, obejrzeć, skompilować i uruchomić poniższy program:
    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());
        return 0;
    }
    1. ważna obserwacja: proces potomny dziedziczy środowisko, wraz z kopiami deskryptorów plików
    2. ten program tak naprawdę nie czeka na zakończenie wszystkich swoich procesów potomnych - popraw go odpowiednio
    3. proszę przeanalizować i zmodyfikować powyższy program, np. tak, aby czytał inne fragmenty pliku, lub wykonywał równolegle inne operacje
  • courses/unix/lab_sysprog.txt
  • Last modified: 4 years ago
  • by 127.0.0.1