Wywołania systemowe Uniksa: Różnice pomiędzy wersjami

[wersja nieprzejrzana][wersja nieprzejrzana]
Usunięta treść Dodana treść
Trasz (dyskusja | edycje)
strace to linuksizm.
poprawki niemerytoryczne
Linia 5:
Z punktu widzenia programu odwołania te są ukryte w [[Biblioteka standardowa języka C|bibliotece libc]] - program nie wie, czy dana funkcja dostarczana jest bezpośrednio przez jądro, czy też implementuje ją libc korzystając z innych mechanizmów jądra (np. w [[GNU/Linux|GNU/Linuksie]] <tt>[[fork]]</tt> zaimplementowany jest za pomocą <tt>clone</tt>).
 
Na x86 i innych systemach o podobnej architekturze <tt>libc</tt> (lub też czasem program bezpośrednio) komunikacja z kernelem odbywa się za pośrednictwem [[przerwanie|przerwań]] systemowych. W Linuksie funkcje systemowe są dostępne przez przerwanie <tt>0x80</tt>, argumenty są przekazywane w rejestrach w następującej kolejności: <tt>eax</tt>, <tt>ebx</tt>, <tt>ecx</tt>, <tt>edx</tt>, <tt>edi</tt>, <tt>esi</tt>, <tt>ebp</tt>. Numer funkcji systemowej jest przekazywany w <tt>eax</tt>, natomiast pozostałe argumenty zależą od rodzaju funkcji, i oczywiście nie wszystkie muszą być wykorzystane. Status operacji zwracany jest w rejestrze <tt>eax</tt>. Gdy operacja wykona się bezbłędnie, jego wartość jest równa 0, w przeciwnym razie jest to (ujemna) stała z pliku <tt>asm/errno.h</tt>[http://lxr.linux.no/linux/include/asm-generic/errno.h]. Pozostałe rejestry nie są zmieniane.
 
W przypadku innych procesorów wywołania systemowe są wykonywane przez specjalizowane instrukcje procesora - np. Pentium 4 posiada instrukcję <tt>sysenter</tt> (ang. '''''sys'''tem '''enter''''').
Linia 45:
== Otwieranie i zamykanie plików - '''<tt>open</tt>''', '''<tt>creat</tt>''' i '''<tt>close</tt>''' ==
 
Pliki otwiera się za pomocą trzyargumentowego <tt>open</tt>, którego definicja znajduje się w <tt>fcntl.h</tt>[http://lxr.linux.no/linux/include/asm-generic/fcntl.h]:
<source lang="c">
<pre>
int open(const char *pathname, int flags, mode_t mode);
</presource>
 
Pierwszy argument <tt>pathname</tt> oznacza ścieżkę do pliku.
Linia 64:
'''Opcjonalny''' trzeci - uprawnienia dla nowo utworzonych plików.
<tt>open</tt> należy do nielicznych wywołań systemowych dopuszczających pomijanie argumentu:
<source lang="c">
<pre>
int open(const char *pathname, int flags);
</presource>
 
Istnieje też specjalna postać <tt>open</tt>:
<source lang="c">
<pre>
int creat(const char *pathname, mode_t mode);
</presource>
równoważna <tt>open(pathname, O_CREAT|O_WRONLY|O_TRUNC, mode)</tt>
 
Linia 77:
W przypadku błędu zwracają <tt>-1</tt> a <tt>[[errno]]</tt> jest ustawiana na kod błędu.
 
<source lang="c">
<pre>
int close(int fd);
</presource>
zamyka otwarty deskryptor pliku. W dawnych czasach <tt>close</tt> nie zwracało kodu błędu,
więc nikt go nie sprawdzał. Współcześnie zwraca kod błędu, co z punktu widzenia architektury systemu
Linia 93:
 
Katalogi tworzy się za pomocą:
<source lang="c">
<pre>
int mkdir(const char *pathname, mode_t mode);
</presource>
gdzie <tt>pathname</tt> i <tt>mode</tt> mają znaczenie podobne jak w <tt>creat</tt>.
 
Pliki urządzeń tworzy się za pomocą:
<source lang="c">
<pre>
int mknod(const char *pathname, mode_t mode, dev_t dev);
</presource>
gdzie <tt>pathname</tt> i <tt>mode</tt> mają to samo znaczenie a <tt>dev</tt> to informacje o typie urządzenia.
 
== Zakończenie pracy - '''<tt>_exit</tt>''' ==
 
<source lang="c">
<pre>
void _exit(int status);
</presource>
służy do zakończenia pracy programu.
<tt>status</tt> zostanie zwrócony jako kod wyjścia.
Linia 114:
== Zarządzanie pamięcią - '''<tt>brk</tt>''' ==
 
<source lang="c">
<pre>
int brk(void *end_data_segment);
</presource>
 
Wywołanie <tt>brk</tt> jest bardzo ważne i widać je często w wynikach <tt>strace</tt>,
Linia 134:
=== '''<tt>read</tt>''' ===
 
<tt>read</tt> jest zdefiniowany w <tt>unistd.h</tt>[] jako:
<source lang="c">
<pre>
ssize_t read(int fd, void *buf, size_t count);
</presource>
 
Pierwszym argumentem jest otwarty deskryptor piku, drugim bufor, do którego mają się dostać zapisywane dane,
Linia 159:
 
<tt>write</tt> jest zdefiniowany w <tt>unistd.h</tt> jako:
<source lang="c">
<pre>
ssize_t write(int fd, const void *buf, size_t count);
</presource>
 
Argumenty mają takie samo znaczenie jak w <tt>read</tt> - <tt>write</tt> pisze do deskryptora <tt>fd</tt> co najwyżej <tt>count</tt> bajtów z bufora <tt>buf</tt> i zwraca liczbę zapisanych bajtów. W przypadku <tt>write</tt> liczba <tt>0</tt> jest jednak równie poprawna jak pozostałe i można próbować dalej.
Linia 173:
 
Nic nie stoi jednak na przeszkodzie, żeby kernel sam zajął się tą operacją - służą temu zdefiniowane w <tt>sys/uio.h</tt> wywołania:
<source lang="c">
<pre>
int readv(int filedes, const struct iovec *vector, size_t count);
int writev(int filedes, const struct iovec *vector, size_t count);
</presource>
 
Pierwszy argument to tradycyjnie otwarty deskryptor pliku, drugi to wskaźnik na tablicę wektorów,
trzeci zaś to ilość elementów tej tablicy. Element ma postać:
 
<source lang="c">
<pre>
struct iovec {
void *iov_base;
size_t iov_len;
};
</presource>
 
gdzie <tt>iov_base</tt> to adres a <tt>iov_len</tt> rozmiar bufora.
 
Procedura naszego serwera miała by wówczas postać:
<source lang="c">
<pre>
struct iovec io[2];
io[0].iov_base = http_headers;
Linia 198:
io[1].iov_len = file_headers_size;
writev (fd, io, 2);
</presource>
 
<tt>readv</tt> i <tt>writev</tt> pojawiły się po raz pierwszy w systemie [[4.2BSD]].
Linia 206:
 
Kolejny często występujący problem wydajności przedstawia następujący fragment kodu:
<source lang="c">
<pre>
bytes_read = read (fd1, buf, buf_size);
write (fd2, buf, bytes_read);
</presource>
 
Często występuje konieczność przerzucenia ogromnej ilości danych z jednego deskryptora do drugiego.
Jednym problemem jest podwojona liczba wywołań systemowych, ale jeszcze poważniejse jest zupełnie
bezużyteczne kopiowanie danych. Przeciwdziałać temu ma zdefiniowany w nagłówku <tt>sys/sendfile.h</tt>:
<source lang="c">
<pre>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
</presource>
 
<tt>out_fd</tt> to deskryptor wyjściowy, <tt>in_fd</tt> - wejściowy, <tt>offset</tt> to wskaźnik na
Linia 234:
 
Przykład działania:
<source lang="c">
<pre>
#include <sys/sendfile.h>
#include <unistd.h>
Linia 265:
return 0;
}
</presource>
 
Ponieważ dane nie wędrują przez pamięć procesu, można podawać "absurdalne" wartości typu (jak wyżej) 64 megabajty
Linia 284:
 
Zdefiniowane w <tt>unistd.h</tt> wywołanie:
<source lang="c">
<pre>
int access(const char *pathname, int mode);
</presource>
służy do sprawdzenia praw do pliku <tt>pathname</tt>.
 
Linia 308:
 
Przykład działania:
<source lang="c">
<pre>
#include <unistd.h>
#include <stdio.h>
Linia 342:
return 0;
}
</presource>
 
== Przechwytywanie sygnałów ==
 
Kontroler sygnałów instaluje się za pomocą zdefiniowanej w <tt>signal.h</tt> funkcji:
<source lang="c">
<pre>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
</presource>
 
Na przykład jeśli nie chcemy pozwolić na Control-C ([[SIGINT]], 2) w trakcie wpisywania danych,
możemy przechwycić sygnał:
<source lang="c">
<pre>
#include <stdio.h>
#include <signal.h>
Linia 370:
return 0;
}
</presource>
 
==Zobacz też==