WYWOŁANIE SYSTEMOWE: semctl();
PROTOTYP: int semctl ( int semid, int semnum, int cmd, union semun arg );
ZWRACA: liczba dodatnia jeżeli sukces
-1 - błąd: errno = EACCESS ( brak prawa dostępu )
EFAULT ( nieprawidłowy adres wskazywant przez argument arg )
EIDRM ( zestaw został usunięty )
EINVAL ( zestaw nie istnieje lub semid nieprawidłowy )
EPERM (EUID nie ma prawa do cmd w arg)
ERANGE ( wartość semafora przekroczona )
UWAGI: Przeprowadza operacje kotrolne na zestawie semaforów
Wywołanie systemowe semctl używane jest do kontrolowania zestawu semaforów. Jest ono analogiczne do wywołania msgctl, które jest opdowiednie dla kolejek. Jeżeli porównasz listę argumentów obu wywołań zauważysz, że są one nieco odmienne. Przypomnij sobie, iż aktualnie semafory są zaimplementowane jako zestawy, rzadziej niż pojedyńcze jednostki. Do operowania semaforem potrzeba nie tylko klucz IPC, ale także numer semafora w zestawie.
Oba wywołania systemowe posiadają argument cmd używany do określenia operacji. Ostatnią różnicą jest krańcowy argument. Dla msgctl jest to kopia wewnętrznej struktury używanej przez jądro. Przypomnij sobie, że używaliśmy ją do odbierania lub ustawiania informacji o prawa dostępu i właścicielu kolejki wiadomości. Przy semaforach, dodatkowe komendy operacyjne są dozwolone co wymaga bardziej złożonej struktury. Użycie union myli wielu początkujących programistów. W celu zapobieżenia temu przeanalizujemy tę strukturę dokładnie.
Pierwszym argumentem dla semctl() jest klucz ( zwrócony przez semget). Drugi argument (semun) to numer semaforu, którym jesteśmy zainteresowani. Jest to index elementu w zestawie, pierwszy element ma index o wartości 0.
Argument cmd przedstawia komendę, która ma zostać wykonana. Jak widzisz, mamy znane komendy IPC_STAT/IPC_SET, jak również wiele dodatkowych specyficznych dla zestawów semaforów:
Ustawia wartość członka ipc_perm struktury semid_ds. Wartości pobiera z buf unii semun.
Usuwa zestaw z jądra.
Używany do pobierania wartości wszystkich semaforów z zestawu Wartości całkowite przechowywane są w tablicy unsigned short int wkazywanej przez array - członka unii.
Zwraca ilość procesów oczekujących na zasoby.
Zwraca PID procesu, który jako ostatni wywołał semop.
Zwaraca wartość pojedyńczego semaforu z zestawu.
Zwraca ilość procesów czekających na 100% użycia zasobu.
Ustawia wartości wszystkich semaforów wartościami z tablicy array zawartej w unii.
Ustawia wartość jednego semaforu z zestawu. Wartość pobierana z val zawartego w unii.
Argument arg jest elementem typu semun. Ta unia zadeklarowana jest w linux/sem.h:
/* arg dla wywołania semctl. */
union semun {
int val; /* wartość dla SETVAL */
struct semid_ds *buf; /* bufor dla IPC_STAT i IPC_SET */
ushort *array; /* tablica dla GETALL i SETALL */
struct seminfo *__buf; /* bufor dla IPC_INFO */
void *__pad;
};
Używany gdy komendą jest SETVAL. Określa wartość, którą należy zapisać w semaforze.
Używany dla komend IPC_STAT/IPC_SET. Reprezentuje kopię wewnętrznej struktury jądra używanej dla semaforów.
Wskaźnik używany dla komend GETALL/SETALL. Powinien wskazywać tablicę wartości całkowitych, która zostanie użyta do ustawiania lub pobierania wartości semaforów.
Wywołanie to jest jednym z najtrudniejszych do zrozumienia wywołań IPC Systemu V, dlatego pokażemy kilka przykładów.
Następująca funkcja zwraca wartość podanego semaforu. Ostatni argument wywołania semctl ( unia ) jest ignorowany dla komendy GETVAL:
int get_sem_val( int sid, int semnum )
{
return( semctl(sid, semnum, GETVAL, 0));
}
Załużmy, że potrzebujemy status wszystkich drukarek z naszego przykładu:
#define MAX_PRINTERS 5
printer_usage()
{
int x;
for(x=0; x<MAX_PRINTERS; x++)
printf("Drukarka %d: %d\n\r", x, get_sem_val( sid, x ));
}
Rozważ nastepującą funkcję inicjującą semafor nową wartością:
void init_semaphore( int sid, int semnum, int initval)
{
union semun semopts;
semopts.val = initval;
semctl( sid, semnum, SETVAL, semopts);
}
Zauważ, iż ostatni argument dla semctl jest kopią unii częściej niż wskaźnikiem do niej. Jeżeli już mówimy o przekazywaniu unii jako argument powolę sobie przedstawić częste błędy podczas używania tego wywołania.
W projekcie msgtool komendy IPC_STAT i IPC_SET były używane do ustawiania/pobierania praw dostępu do kolejki. Są one również zaimplementowane dla semaforów, jednak ich sposób użycia jest nieco odmienny dlatego, iż wewnętrzna struktura jest odbierana i kopiowana z unii, rzadziej niż z pojedyńczej jednostki. Czy potrafisz zlokalizować błąd w następującym kodzie ?
/* Żądane prawa dostępu powinny być przekazane jako tekst ( np.: "660" ) */
void changemode(int sid, char *mode)
{
int rc;
struct semid_ds mysemds;
/* pobierz wartości wewnętrznej struktury danych */
if((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1)
{
perror("semctl");
exit(1);
}
printf("Stare prawa dostępu: %o\n", semopts.buf->sem_perm.mode);
/* zmiana restrykcji dla semaforu*/
sscanf(mode, "%o", &semopts.buf->sem_perm.mode);
/* aktualizacja wewnętrznej struktury danych */
semctl(sid, 0, IPC_SET, semopts);
printf("Zaktualizowano...\n");
}
Kod próbuje stworzyć lokalną kopię wewnętrznej struktury dla zestawu, zmodyfikować prawa dostępu i ustawić je ( dzięki IPC_SET ) w jądrze. Niestety, pierwsze wywołanie semctl zwraca EFAULT lub zły adres do ostatniego argumentu ( unii! ). Dodatkowo, jeżeli nie sprawdziliśmy błędów dla tego wywołania możemy dostać błąd pamięci. Dlaczego?
Przypomnij sobie, iż komendy IPC_SET/IPC_STAT używają element unii - buf, jest on wskaźnikiem do zmiennej typu semid_ds. Wskaźniki są wskaźnikami, są wskaźnikami, są wskaźnikami! Element buf musi wskazywać jakąś prawidłową lokalizację aby funkcja działała prawidłowo. Oto zmieniona wersja:
void changemode(int sid, char *mode)
{
int rc;
struct semid_ds mysemds;
/* pobierz wartości wewnętrznej struktury danych */
/* wkaż naszą lokalną kopię! */
semopts.buf = &mysemds;
/* spróbujmy ponownie! */
if((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1)
{
perror("semctl");
exit(1);
}
printf("Stare prawa dostępu: %o\n", semopts.buf->sem_perm.mode);
/* zmiana restrykcji dla semaforu */
sscanf(mode, "%o", &semopts.buf->sem_perm.mode);
/* aktualizacja wewnętrznej struktury danych */
semctl(sid, 0, IPC_SET, semopts);
printf("Zaktualizowano...\n");
}