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.
#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:
- wyświetlać znak zachęty w postaci
[{path}]
, gdzie{path}
powinno być ścieżką do bieżącego katalogu roboczego, - obsługiwać polecenie powłoki
cd
, działające analogicznie do tego z powłokibash
, - obsługiwać polecenie powłoki
exit
, kończące działanie programu powłoki; polecenieexit
powinno przyjmować jeden opcjonalny parametr, będący statusem wyjścia zwracanym do procesu rodzica, - obsługiwać polecenie powłoki
help
, wyświetlające na ekranie informacje o autorze programu i oferowanych przez niego funkcjonalnościach, - obsługiwać jedno inne, dowolnie wybrane polecenie powłoki,
- przyjmować polecenia odwołujące się do potencjalnych programów w bieżącym katalogu roboczym oraz umożliwiać ich wykonanie z parametrami,
- 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.