Moduł 8

Materiały do zajęć z Systemów operacyjnych prowadzonych na Wydziale Matematyki i Informatyki Uniwersytetu im. Adama Mickiewicza w Poznaniu.

« Wróć do spisu materiałów

Przydatne funkcje

Funkcja execl (ang. execute and leave) zadeklarowana w nagłówku unistd.h zastępuje bieżący proces nowym procesem. Pierwszym argumentem jest ścieżka bezwzględna do pliku wykonywalnego, kolejne zaś określają argumenty (uwaga: razem z nazwą polecenia). Z uwagi na to, że liczba argumentów nie jest z góry ustalona, ostatnim argumentem funkcji execl musi być zawsze zero (lub stała NULL). Funkcja ta, ze względu na swoje działanie, zwraca wartość (ujemną) tylko w przypadku niepowodzenia wywołania. W przeciwnym przypadku nie zwraca wartości (bo nie ma dokąd, skoro proces zostaje zastąpiony).

Skompiluj poniższy kod programu i go wykonaj:

#include <unistd.h>
#include <stdio.h>

int main(){
    execl("/bin/sleep", "sleep", "5", 0);
    printf("Czy ten napis sie wyswietli?");
    return 0;
}

Czy napis się wyświetlił? Dlaczego? Przeanalizuj wywołanie funkcji execl.

Zapoznaj się z funkcją systemową wait. Następnie skonstruuj funkcję system, która przyjmuje jako argument ścieżkę do programu, a następnie wykonuje go jako podproces. Funkcja powinna zwracać wartość zwracaną przez wywołany program lub -1, jeśli wystąpi jakikolwiek błąd.

Zobacz odpowiedź

#include <unistd.h>
#include <stdio.h>

int system(char *cmd){
    int pid;
    if((pid = fork()) >= 0){
        if(pid == 0){
            execl(cmd, cmd, 0);
            return -1;
        } else {
            int ret;
            wait(&ret);
            return ret;
        }
    } else {
            return -1;
    }
}

Funkcja exit, zadeklarowana w nagłówku stdlib.h, przyjmuje argument całkowitoliczbowy i kończy bieżący proces zwracając wskazaną wartość.

Zapoznaj się z poniższym kodem źródłowym. Zwróć uwagę na zastosowanie funkcji exit.

#include <stdlib.h>
#include <stdio.h>

int main(){
    printf("Hello world!\n");
    exit(0);
}

Zastanów się, jaką przewagę ma funkcja systemowa exit nad instrukcją return wywoływaną wewnątrz funkcji main.

W bibliotece standardowej zdefiniowana jest także funkcja _exit o działaniu zbliżonym do funkcji exit. Zadeklarowana jest ona w nagłówku unistd.h.

Jaka jest różnica pomiędzy funkcjami exit i _exit? Podpowiedź: zapoznaj się z działaniem funkcji systemowej atexit zadeklarowanej w nagłówku stdlib.h.

Napisz w języku C prosty program powłoki. Program ten powinien przyjmować na wejściu polecenia, a następnie wykonywać działania zgodne z ich treścią. Powłoka powinna:

  1. wyświetlać znak zachęty w postaci [{path}], gdzie {path} powinno być ścieżką do bieżącego katalogu roboczego,
  2. obsługiwać polecenie powłoki cd, działające analogicznie do tego z powłoki bash,
  3. obsługiwać polecenie powłoki exit, kończące działanie programu powłoki; polecenie exit powinno przyjmować jeden opcjonalny parametr, będący statusem wyjścia zwracanym do procesu rodzica,
  4. obsługiwać polecenie powłoki help, wyświetlające na ekranie informacje o autorze programu i oferowanych przez niego funkcjonalnościach,
  5. obsługiwać jedno inne, dowolnie wybrane polecenie powłoki,
  6. przyjmować polecenia odwołujące się do potencjalnych programów w bieżącym katalogu roboczym oraz umożliwiać ich wykonanie z parametrami,
  7. wypisywać komunikat błędu, gdy niemożliwe jest poprawne zinterpretowanie polecenia.

Proste wersje wybranych poleceń

Siłą systemów uniksopodobnych jest mnogość narzędzi dostępnych z poziomu konsoli – w czasie pierwszych pięciu ćwiczeń poznaliśmy ledwie kilka z nich, nie wykraczając jednocześnie poza niewielki procent możliwości, jakie one udostępniają.

Twórcy nawet tak prostych programów, jak cat czy echo, musieli mierzyć się z wieloma zagadnieniami związanymi z optymalizacją kodu. Pominiemy je, analizując kody źródłowe uproszczonych wersji wybranych narzędzi. Niektóre z nich utworzyliśmy sami już wcześniej, inne będą dla nas nowością.

Program cat

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define BUFSIZ 128

int main(int argc, char *argv[]){
    char buf[BUFSIZ];
    int f,i,n;

    if(argc <= 1)
        while ((n = read(0, buf, BUFSIZ)) > 0)
            write(1, buf, n);

    for(i=1; i<argc; i++){
        if((f = open(argv[i], O_RDONLY)) > 0){
            while ((n = read(f, buf, BUFSIZ)) > 0)
                write(1, buf, n);
            close(f);
        } else {
            perror(argv[i]);
        }
    }
    return 0;
}

Program echo

#include <stdio.h>

int main(int argc, char *argv[]){
    int i;

    for(i=1; i<argc; i++){
        printf("%s ", argv[i]);
    }
    printf("\n");
    return 0;
}

Program wc

Poniższy przykład pochodzi z książki: B. W. Kernighan, D. M. Ritchie, Język ANSI C, Wydawnictwa Naukowo-Techniczne, Warszawa 2003.

#include <stdio.h>

#define IN  1 /* inside a word */
#define OUT 0 /* outside a word */

/* count lines, words, and characters in input */
main(){
  int c, nl, nw, nc, state;

  state = OUT;
  nl = nw = nc = 0;
  while ((c = getchar()) != EOF) {
    ++nc;
    if (c == '\n')
      ++nl;
    if (c == ' ' || c == '\n' || c == '\t')
      state = OUT;
    else if (state == OUT) {
      state = IN;
      ++nw;
    }
  }

  printf("%d %d %d\n", nl, nw, nc);
}

Program od

#include <stdio.h>

int main(){
    int c, n;

    n = 1;
    while ((c = getchar()) != EOF) {
        printf("%3o", c);
        if (n++ >= 10) {
            n = 1;
            printf("\n");
        } else {
            printf(" ");
        }
    }

    if(n != 1) printf("\n");
    return 0;
}

Prawdziwe programy

Pobierz z katalogu ftp://ftp.gnu.org/gnu/coreutils/ plik coreutils-8.9.tar.gz i wypakuj jego zawartość. Przejdź do katalogu src i zapoznaj się z kodami źródłowymi trzech wybranych programów, z których dotąd korzystaliśmy.