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. 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.

  1. Proszę skopiować treść programów ze strony

Proszę oglądnąć poniższy program.

Program f1.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);
}

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:

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);
}

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:

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;
}

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:

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);
}

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:

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);
}

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:

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);
}

Jak działa program? Dlaczego?

Dla lepszego zrozumienia proszę oglądnąć i uruchomić kolejny program.

Program p4.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);
}

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:

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);
}

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:

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);
}

Ćwiczenie: Procesy i zrównoleglanie pracy

Proszę oglądnąć i uruchomić poniższy program.

Program p7.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);
}

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.
pl/dydaktyka/unix/lab_prog_system.txt · ostatnio zmienione: 2019/06/27 15:50 (edycja zewnętrzna)
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0