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