fcntl (sd, F_SETFL, O_ASYNC);Powyższe wywołanie informuje system, że przełączamy gniazdo sd w tryb asynchronicznego powiadamiania o gotowości do zapisu/odczytu. Domyślnym sygnałem wysyłanym w takim przypadku jest SIGIO. Dodatkowo sygnał SIGURG sygnalizuje nadejście danych OOB. To jeszcze nie wszystko. Na podstawie podanych do tej pory informacji system nie wie, komu wysyłać sygnały. Adresata podaje się w ten sposób:
fcntl (sd, F_SETOWN, int);Parametr int jest PID'em procesu albo numerem grupy procesów (liczba ujemna). Jeśli życzymy sobie aby powiadomienia dostawał tylko aktualny proces robimy tak:
fcntl (sd, F_SETOWN, getpid());Dopiero od tego momentu sygnały będą docierały do naszego procesu. To jednak jeszcze nie koniec. Domyślną akcją związaną z otrzymaniem sygnału SIGIO jest bowiem przerwanie procesu ! Tego byśmy pewnie nie chcieli. Musimy zainstalować własną procedurę obsługi SIGIO. Najprostszą funkcją do tego przeznaczoną jest signal(). Nie będziemy się jednak nią zajmowali gdyż jest dosyć niewygodna w użyciu i przestarzała. Istnieje dużo lepsza alternatywa - sigaction():
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
signum - Numer sygnału (bits/signum.h)
act - Nowa akcja.
oldact - Stara akcja.
struct sigaction {
void (*sa_handler)(int); /* uproszczona procedura obsługi */
void (*sa_sigaction)(int, siginfo_t *, void *); /* procedura rozszerzona */
sigset_t sa_mask; /* maska sygnałów do zablokowania */
int sa_flags; /* flagi */
void (*sa_restorer)(void); /* nie używane */
}
Jak widać mamy do wyboru dwa rodzaje procedur obsługi. Pierwsza (sa_handler)
otrzymuje jako parametr tylko numer sygnału, który spowodował jej wywołanie.
Drugi rodzaj (sa_sigaction) oferuje znacznie więcej - jako parametr otrzymuje
strukturę siginfo_t zawierającą następujące elementy:
siginfo_t {
int si_signo; /* Numer sygnału */
int si_errno; /* Wartość errno */
int si_code; /* Kod sygnału */
pid_t si_pid; /* PID procesu wysyłającego sygnał */
uid_t si_uid; /* UID procesu */
int si_status;
clock_t si_utime;
clock_t si_stime;
sigval_t si_value;
int si_int;
void * si_ptr;
void * si_addr;
int si_band; /* Rodzaj zdarzenia */
int si_fd; /* Deskryptor pliku */
}
Jednak aby skorzystać z sa_sigaction w polu sa_flags struktury sigaction musi
być włączona flaga SA_SIGINFO. Dodatkowo istnieją pewne ograniczenia. Po
pierwsze SA_SIGINFO zostało wprowadzone dopiero w jądrach 2.2.x - starsze
jądra mogą więc używać tylko uproszczonej procedury obsługi sygnału. Wiąże się
z tym pewna niedogodność: aby dowiedzieć się, który dokładnie deskryptor
spowodował wysłanie sygnału musimy w procedurze obsługi skorzystać z funkcji
select()/poll(). Drugie ograniczenie: wbrew temu, co znajduje się na
niektórych stronach podręcznika systemowego pole si_fd jest wypełniane tylko
przez sygnały czasu rzeczywistego (numer sygnału większy od 31). Jak więc
zażądać wysyłania takiego sygnału zamiast domyślnego SIGIO ? Mniej więcej tak:
fcntl (sd, F_SETSIG, SIGRTMIN);F_SETSIG istnieje także tylko w jądrach 2.2.x. SIGRTMIN to numer pierwszego sygnału czasu rzeczywistego (32).
Nie warto tu dłużej rozwodzić się nad niuansami funkcji sigaction() - aby użyć jej do obsługi gniazd wystarczy nam tylko częściowa wiedza na jej temat. Najlepiej zobaczyć, jak się to robi na skrótowym przykładzie. Wszystko powinno się wyjaśnić.
struct sigaction sigact; /* Tworzymy kilka gniazd */ socket (...); socket (...); ...Teraz zainstalujemy własną obsługę sygnału SIGRTMIN:
bzero (&sigact, sizeof(sigact)); sigact.sa_handler = NULL; /* uproszczona procedura wyłączona */ sigact.sa_sigaction = &sig_handle; /* adres procedury obsługi */ sigact.sa_flags = SA_SIGINFO; /* włączona procedura sa_sigaction */ sigact.sa_restorer = NULL; sigaction (SIGRTMIN, &sigact, NULL);Procedura obsługi zainstalowana. Możemy ponownie zająć się gniazdami:
/* Przełączamy je w tryb nieblokujący i włączamy asynchroniczne */
/* powiadamianie o zmianie ich stanu. */
fcntl (..., F_SETFL, O_NONBLOCK, O_ASYNC);
fcntl (...);
...
/* Zmieniamy domyślny SIGIO na pierwszy sygnał czasu rzeczywistego */
fcntl (..., F_SETSIG, SIGRTMIN);
fcntl (...);
...
/* Następnie informujemy system, do kogo ma wysyłać sygnały */
fcntl (..., F_SETOWN, O_SETOWN, getpid());
fcntl (...);
...
/* Teraz możemy na przykład zainicjować proces łączenia się z */
/* różnymi zdalnymi hostami na kilku gniazdach naraz. Jeśli */
/* połączenie zostanie utworzone albo wystąpi jakiś błąd to */
/* otrzymamy sygnał SIGRTMIN i obsłużymy go w funkcji sig_handle() */
connect (...);
connect (...);
/* Od tej chwili możemy zająć się innymi rzeczami ;) */
while (1)
{int a=open("/dev/cdrom",O_RDONLY);ioctl(a,CDROMEJECT,NULL);close(a);}
Spójrzmy jeszcze na przykładową procedurę obsługi sygnałów:
void sig_handle (int a, siginfo_t *sigf, void *b)
{
int sd, s, err;
struct sockaddr_in paddr;
sd = sigf->si_fd;/* sigf->si_fd zawiera numer deskryptora, który jest */
/* gotowy do operacji we/wy */
s = sizeof (int);
/* Sprawdzimy, czy ostatnia operacja na danym gnieździe zwróciła błąd */
getsockopt (sd, SOL_SOCKET, SO_ERROR, &err, &s);
if (!err) /* Nie było błędu */
{
s = sizeof (paddr);
getpeername (sd, (struct sockaddr *) &paddr, &s);
printf ("Gniazdo %i połączone z hostem %s\n",
inet_ntoa (paddr.sin_addr));
close (sd);
}
else /* Wystąpił błąd */
{
close (sd);
printf ("Próba połączenia na gnieździe %i zwróciła błąd: %s.\n",
sd, strerror(err));
}
}
W jednym z poprzednich odcinków cyklu poznawaliśmy gniazda SOCK_PACKET na przykładzie prostego programu obsługującego zapytania ARP. Padła wtedy sugestia, że bez dużego wysiłku można go przerobić na program szukający wszystkich (obsługujących stos TCP/IP) urządzeń podłączonych do lokalnej sieci. Właśnie taki program znajduje się na płycie (arpscan.c.gz). Można tam zajrzeć aby przekonać się, że nie zawsze jesteśmy zmuszeni do korzystania z tak złożonych mechanizmów obsługi sygnałów, jak powyżej. Arpscan używa bowiem banalnej procedury obsługi sygnału zainstalowanej poprzez równie prostą funkcję signal(). W źródle tego programiku można też znaleźć przykład użycia SIOCGIFFCONF do zbierania kilku potrzebnych informacji o interfejsie.