Σηματοφορείς




  Οι σηματοφορείς προτάθηκαν από τον E. W. Dijkstra στα τέλη της δεκαετίας του 1960. Το μοντέλο του Dijkstra παραπέμπει στους σηματοδότες που ελέγχουν μια μονή γραμμή τραίνου. Ένας συρμός για να εισέλθει στη γραμμή πρέπει να την βρεί ελεύθερη, δηλαδή ο σηματοδότης να επιτρέπει διέλευση. Μόλις ένας συρμός εισέλθει στη γραμμή, και για όσο διάστημα βρίσκεται μέσα σε αυτή, οι σηματοδότες πρέπει να δείχνουν οτι η γραμμή είναι κατηλειμμένη, δηλαδή να απαγορεύουν την είσοδο άλλων συρμών. Σε περίπτωση που υπάρχει άλλο τραίνο που θέλει να μπεί στη γραμμή θα πρέπει να περιμένει. Όταν ο συρμός βγεί από τη γραμμή, τότε οι σηματοδότες δείχνουν οτι η γραμμ είναι πάλι διαθέσιμη. Το ταρίνο σε αναμονή μπορεί να εισέλθει, κ.ο.κ.

Στην περίπτωση των υπολογιστών ο σηματοφορέας (σημαφόρος, σηματοδότης) χρησιμεύει στον έλεγχο της προσπέλασης σε κοινόχρηστους πόρους (θέσεις μνήμης, απομονωτές συσκευών Ε/Ε, κλπ) και μπορεί να είναι ένας απλός ακέραιος. Μια διεργασία (ή ένα νήμα) που θέλει να προσπελάσει ένα κοινόχρηστο πόρο πρέπει να περιμένει ώστε ο αντίστοιχος σηματοφορέας να λάβει την τιμή 0. Μόλις η διεργασία προσπελάσει τον πόρο, αυξάνει τη τιμή του σηματοφορέα κατά 1. Όταν τερματίσει, μειώνει τη τιμή του σηματοφορέα πάλι κατα 1. Υπάρχει ειδική λειτουργία ελέγχου του σηματοφορέα.

Οι σηματοφορείς μπορεί να λειτουργούν ατομικά ή σε σύνολα (πίνακες σηματοφορέων). Στην έκδοση UNIX System V μια τέτοια δομή μπορεί να είναι αρκετά 'βαριά'.  Υπάρχουν πολύ ελαφρότερες υλοποιήσεις στη πρότυπη βιβλιοθήκη των νημάτων καθώς και στο πρότυπο POSIX. 

Όπως και οι ουρές μηνυμάτων, ο σηματοφορέας πρέπει να αρχικοποιηθεί με τη συνάρτηση semget(). Ο δημιουργός ή ιδιοκτήτης του σηματοφορέα μπορεί να τροποποιήσει τις άδειες πρόσβασης ή ιδιοκτησίας με την συνάρτηση semctl(). Οι λειτουργίες του σηματοφορέα υλοποιούνται με την συνάρτηση semop()

Αρχικοποίηση

Η συνάρτηση semget() αρχικοποιεί ή προσπελαύνει ένα σηματοφορέα. Προτυποιείται ως:

int semget(key_t key, int nsems, int semflg);

Αν η κλήση επιτύχει, επιστρέφει το ID του σηματοφορέα (semid).

Το όρισμα key είναι argument is a access value associated with the semaphore ID.

Το όρισμα nsems καθορίζει τον αριθμό των στοιχείων στον πίνακα σηματοφορέων. Η κλήση αποτυγχάνει όταν το nsems είναι μεγαλύτερο από τον αριθμό των διαθέσιμων στοιχείων. Όταν ο αριθμός των διαθέσιμων στοιχείων δεν είναι γνωστός, η τιμή 0 σε αυτό το όρισμα εξασφαλίζει την επιτυχία της κλήσης.

Το όρισμα semflg καθορίζει τα αρχικά δικαιώματα πρόσβασης και δημιουργίας.

Η κώδικας που ακολουθεί επιδεικνύει τη χρήση της συνάρτησης semget().

#include <sys/types.h> 
#include <sys/ipc.h>
#include <sys/sem.h>

...
key_t key; /* key to pass to semget() */
int semflg; /* semflg to pass tosemget() */
int nsems; /* nsems to pass to semget() */
int semid; /* return value from semget() */

...

key = ...
nsems = ...
semflg = ... ...
if ((semid = semget(key, nsems, semflg)) == -1)
{
perror("semget: semget failed");
exit(1);
}
else
...

Έλεγχος

Η συνάρτηση semctl() τροποποιεί τα δικαιώματα πρόσβασης και άλλα χαρακτηριστικά του σηματοφορέα. Δηλώνεται ως εξής:

int semctl(int semid, int semnum, int cmd, union semun arg);

Το όρισμα semid πρέπει να αναφέρεται σε ID υπάρχοντος σηματοφορέα. Η τιμή του semnum ορίζει τη θέση ενός σηματοφορέα σε σχετικό πίνακα. Το όρισμα cmd λαμβάνει μια από τις παρακάτω τιμές ελέγχου:

GETVAL
-- Επιστρέφει την τιμή απλού σηματοφορέα. Σε αυτή τη περίπτωση η τιμή βρίσκεται στον ακέραιο arg.val.
SETVAL
-- Θέτει την τιμή απλού σηματοφορέα. Σε αυτή τη περίπτωση η τιμή βρίσκεται στον ακέραιο arg.val.
GETPID
-- Επιστρέφει PID της διεργασίας που προσπέλασε τελευταία το σηματοφορέα ή τον πίνακα.
GETNCNT
-- Επιστρέφει τον αριθμό των διεργασιών που αναμένουν άυξηση της τιμής του σηματοφορέα.
GETZCNT
-- Επιστρέφει τον αριθμό των διεργασιών που αναμένουν να λάβει -ο σηματοφορέας τιμή μηδέν.
GETALL
-- Επιστρέφει τις τιμές όλων των σηματοφορέων (σε πίνακα). Σε αυτή τη περίπτωση οι τιμές βρίσκονται μέσω του arg.array, που είναι δείκτης σε πίνακα unsigned short.
SETALL
-- Θέτει τις τιμές όλων των σηματοφορέων (σε πίνακα). Σε αυτή τη περίπτωση οι τιμές βρίσκονται μέσω του arg.array, που είναι δείκτης σε πίνακα unsigned short.
IPC_STAT
-- Επιστρέφει πληροφορίες για τη κατάσταση. Σε αυτή τη περίπτωση η πληροφορία βρίσκεται μέσω του arg.buf, που είναι δείκτης δε δομή τύπου semid_ds.
IPC_SET
-- Θέτει πληροφορίες για τη κατάσταση. Σε αυτή τη περίπτωση η πληροφορία βρίσκεται μέσω του arg.buf, που είναι δείκτης δε δομή τύπου semid_ds.
IPC_RMID
-- Διαγράφει το σύνολο των σηματοφορέων.

Τα όρισμα ελέγχου απαιτεί και ανάλογα δικαιώματα της καλούσας διεργασίας. Για τις επιλογές  IPC_SET ή IPC_RMID απαιτείται δικαίωμα διαχειριστή ή ιδιοκτήτη. Για τις υπόλοιπες επιλογές απαιτούνται δικαιώματα εγγραφής και ανάγνωσης. 

Το τέταρτο όρισμα union semun arg είναι προαιρετικό και χρησιμοποιείται μόνο αν το απαιτεί το όρισμα ελέγχου. Αν απαιτείται τέτοιο όρισμα πρέπει να δηλωθεί ρητά στο πρόγραμμα εφαρμογής ως:

union semun {
int val;
struct semid_ds *buf;
ushort *array;
} arg;

Ο κώδικας που ακολουθεί επιδεικνύει τη χρήση της συνάρτησης semctl().

#include <sys/types.h> 
#include <sys/ipc.h>
#include <sys/sem.h>

union semun {
int val;
struct semid_ds *buf;
ushort *array;
} arg;

int i;
int semnum = ....;
int cmd = GETALL; /* get value */

...
i = semctl(semid, semnum, cmd, arg);
if (i == -1) {
perror("semctl: semctl failed");
exit(1);
}
else
...

Λειτουργίες

Η συνάρτηση semop() εκτελεί τις λειτουργίες των σηματοφορέων. Δηλώνεται ως:
int semop(int semid, struct sembuf *sops, size_t nsops);

Το όρισμα semid πρέπει να αναφέρεται σε ID υπάρχοντος σηματοφορέα (ή πίνακα σηματοφορέων). Το όρισμα sops είναι δείκτης σε ένα πίνακα δομών τύπου sembuf, όπου η κάθε μια περιέχει τις παρακάτω πληροφορίες για τη λειτουργία ενός σηματοφορέα:

Η δομή sembuf ορίζεται στη βιβλιοθήκη sys/sem.h.

struct sembuf {
ushort_t sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};

Το όρισμα nsops καθορίζει το μήκος του πίνακα. Το μέγιστο μέγεθος του πίνακα ελέγχεται από την επιλογή SEMOPM και δηλώνει τον μέγιστο επιτρεπόμενο αριθμό λειτουργιών σε μια κλήση της συνάρτησης  semop(. Ως προεπιλογή το μέγιστο μέγεθος είναι 10. Η προς εκτέλεση λειτουργία καθορίζεται από το sem_op ως εξής:

Υπάρχουν δύο σημαίες ελέγχου που χρησιμοποιούνται με τη semop():

IPC_NOWAIT
-- Αν για κάποια λειτουργία έχει τεθεί η σημαία IPC_NOWAIT τότε σε περίπτωση αδυναμίας εκτέλεσης της λειτουργίας η συνάρτηση επιστρέφει χωρίς καμμία τροποποίηση του σηματοφορέα. Αδυναμία εκτέλεσης συμβαίνει όταν έχουμε προσπάθεια μείωσης ενός σηματοφορέα σε τιμή μικρότερη του μηδενός ή όταν έχουμε αποτυχημένο έλεγχο ενός σηματοφορέα αν έχει λάβει τη τιμή μηδέν.
SEM_UNDO
-- Επιτρέπει αναίρεση λειτουργιών στο πίνακα των σηματοφορέων κατά τον τερματισμό της διεργασίας.

Μια διεργασία για να ελέγξει ένα σηματοφορέα για την τιμή μηδέν πρέπει να έχει δικαιώματα ανάγνωσης, ενώ για να ματαβάλλει τη τιμή του πρέπει να έχει δικαιώματα εγγραφής. Η διεργασία αναστέλεται (εκτός αν έχει τεθεί η επιλογή IPC_NOWAIT), και παραμένει σε αναστολή μέχρι:

Μόνο μια διεργασία μπορεί να τροποποιεί το σηματοφορέα σε κάθε χρονική στιγμή. Σε περίπτωση που υπάρχουν πολλές διεργασίες που θέλουν να καταλάβουν το σηματοφορέα ταυτόχρονα, η σειρά κατάληψης είναι αυθαίρετη. Αν μια διεργασία εκτελεί ένα πίνακα λειτουργιών μέσω μιας κλήσης της semop() τότε δεν εκτελείται καμμία τροποποίηση μέχρι να ολοκληρωθούν με επιτυχία όλες οι λειτουργίες του πίνακα.

Αν μια διεργασία που έχει καταλάβει ένα σηματοφορέα τερματιστεί ανώμαλα και δεν αναιρέσει τις λειτουργίες της ή δεν ελευθερώσει το σηματοφορέα, τότε ο σηματοφορέας μένει κλειδωμένος στη μνήμη στη κατάσταση που τον άφησε η διεργασία. Για να αποφύγουμε κάτι τέτοιο, η σημαία SEM_UNDO επιτρέπει στην semop() να δημιουργήσει μια δομή επαναφοράς η οποία κρατά τα τελευταία έγκυρα του σηματοφορέα και επιτρέπει το σύστημα να επαναφέρει την επιστροφή του σηματοφορέα σε συνεπή κατάσταση. Σε περίπτωση που ένας πόρος ελέγχεται από ένα σηματοφορέα τότε όλες οι σχετικές λειτουργίες πρέπει να χρησιμοποιούν τη σημαία SEM_UNDO. Έτσι όλες οι διεργασίες χρησιμοποιούν την ίδια δομή επαναφοράς.

Σημείωση: Η ασυνεπής χρήση της επιλογής SEM_UNDO μπορεί να οδηγήσει σε δέσμευση υπερβολικού χώρου μνήμης που δεν πρόκειται να ελευθερωθεί παραμόνο με την επανεκκίνηση του συστήματος.

Ο κώδικας που ακολουθεί επιδεινκύει τη χρήση της συνάρτησης semop():

#include <sys/types.h> 
#include <sys/ipc.h>
#include <sys/sem.h>

...
int i;
int nsops; /* number of operations to do */
int semid; /* semid of semaphore set */
struct sembuf *sops; /* ptr to operations to perform */

...

if ((semid = semop(semid, sops, nsops)) == -1)
{
perror("semop: semop failed");
exit(1);
}
else
(void) fprintf(stderr, "semop: returned %d\n", i);
...

POSIX <semaphore.h>

Οι σηματοφορείς POSIX είναι πολύ ελαφρύτερες από τους σηματοφορείς του System V. Ο σηματοφορέας POSIX είναι πάντα απλός, όχι πίνακας έως 25 σηματοφορέων όπως τοSystem V. Οι συναρτήσεις χειρισμού των σηματοφορέων POSIX είναι:

sem_open() -- Προσπελαύνει, και προαιρετικά δημιουργεί, ένα ονοματισμένο σηματοφορέα

sem_init() -- Αρχικοποιεί ένα σηματοφορέα (εσωτερικά στο πρόγραμμα που καλεί τη συνάρτηση, δηλαδή όχι ονοματισμένο).

sem_close() -- Τερματίζει τη προσπέλαση σε ανοικτό σηματοφορέα.

sem_unlink() -- Τερματίζει τη προσπέλαση σε ανοικτό σηματοφορέα, και καταργεί το σηματοφορέα όταν όλες οι διεργασίες τερματίσουν τη προσπέλασή τους.

sem_destroy() -- Τερματίζει ένα σηματοφορέα (εσωτερικά στο πρόγραμμα που καλεί τη συνάρτηση, δηλαδή όχι ονοματισμένο).

sem_getvalue() -- Αντιγράφει τη τιμή του σηματοφορέα σε δεδομένο ακέραιο.

sem_wait(), sem_trywait() -- Αναστέλει τη διεργασία (ή επιστρέφει σφάλμα) όσο ο σηματοφορέας είναι κατηλειμμένος από άλλη διεργασία.

sem_post() -- Αύξηση τη τιμή του σηματοφορέα.

Οι λειτουργίες είναι παρόμοιες με αυτές που περιγράφηκαν παραπάνω, με τη βασική διαφορά οτι αντί για μια συνάρτηση λειτουργίας εδώ έχουμε περισσότερες εξειδικευμένες συναρτήσεις. 

semaphore.c: Επίδειξη απλής λειτουργίας

/* semaphore.c --- simple illustration of dijkstra's semaphore analogy
*
* We fork() a child process so that we have two processes running:
* Each process communicates via a semaphore.
* The respective process can only do its work (not much here)
* When it notices that the semaphore track is free when it returns to 0
* Each process must modify the semaphore accordingly
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

union semun {
int val;
struct semid_ds *buf;
ushort *array;
};

main() {
 
int i,j;
int pid;
int semid; /* semid of semaphore set */
key_t key = 1234; /* key to pass to semget() */
int semflg = IPC_CREAT | 0666; /* semflg to pass to semget() */
int nsems = 1; /* nsems to pass to semget() */
int nsops; /* number of operations to do */
struct sembuf *sops = (struct sembuf *) malloc(2*sizeof(struct sembuf));
/* ptr to operations to perform */

/* set up semaphore */

(void) fprintf(stderr, "\nsemget: Setting up seamaphore: semget(%#lx, %\%#o)\n",key, nsems, semflg);
if ((semid = semget(key, nsems, semflg)) == -1) {
perror("semget: semget failed");
exit(1);
}
else
(void) fprintf(stderr, "semget: semget succeeded: semid =\%d\n", semid);


/* get child process */

if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}

if (pid == 0) { /* child */
i = 0;

while (i < 3) {/* allow for 3 semaphore sets */

nsops = 2;

/* wait for semaphore to reach zero */
sops[0].sem_num = 0; /* We only use one track */
sops[0].sem_op = 0; /* wait for semaphore flag to become zero */
sops[0].sem_flg = SEM_UNDO; /* take off semaphore asynchronous */

sops[1].sem_num = 0;
sops[1].sem_op = 1; /* increment semaphore -- take control of track */
sops[1].sem_flg = SEM_UNDO | IPC_NOWAIT; /* take off semaphore */

/* Recap the call to be made. */
(void) fprintf(stderr,"\nsemop:Child Calling semop(%d, &sops, %d) with:", semid, nsops);
for (j = 0; j < nsops; j++) {
(void) fprintf(stderr, "\n\tsops[%d].sem_num = %d, ", j, sops[j].sem_num);
(void) fprintf(stderr, "sem_op = %d, ", sops[j].sem_op);
(void) fprintf(stderr, "sem_flg = %#o\n", sops[j].sem_flg);
}

/* Make the semop() call and report the results. */
if ((j = semop(semid, sops, nsops)) == -1) {
perror("semop: semop failed");
}
else {
(void) fprintf(stderr, "\tsemop: semop returned %d\n", j);
(void) fprintf(stderr, "\n\nChild Process Taking Control of Track: %d/3 times\n", i+1);

sleep(5); /* DO Nothing for 5 seconds */

nsops = 1;

/* wait for semaphore to reach zero */
sops[0].sem_num = 0;
sops[0].sem_op = -1; /* Give UP Control of track */
sops[0].sem_flg = SEM_UNDO | IPC_NOWAIT; /* take off semaphore, asynchronous */

if ((j = semop(semid, sops, nsops)) == -1) {
perror("semop: semop failed");
}
else
(void) fprintf(stderr, "Child Process Giving up Control of Track: %d/3 times\n", i+1);

sleep(5); /* halt process to allow parent to catch semaphor change first */

}/* end if Make semop()*/

++i;
}/*end while */
}/*end if child */

else {/* parent, pid hold id of child */

i = 0;

while (i < 3) { /* allow for 3 semaphore sets */

nsops = 2;

/* wait for semaphore to reach zero */
sops[0].sem_num = 0;
sops[0].sem_op = 0; /* wait for semaphore flag to become zero */
sops[0].sem_flg = SEM_UNDO; /* take off semaphore asynchronous */

sops[1].sem_num = 0;
sops[1].sem_op = 1; /* increment semaphore -- take control of track */
sops[1].sem_flg = SEM_UNDO | IPC_NOWAIT; /* take off semaphore */

/* Recap the call to be made. */
(void) fprintf(stderr,"\nsemop:Parent Calling semop(%d, &sops, %d) with:", semid, nsops);
for (j = 0; j < nsops; j++){
(void) fprintf(stderr, "\n\tsops[%d].sem_num = %d, ", j, sops[j].sem_num);
(void) fprintf(stderr, "sem_op = %d, ", sops[j].sem_op);
(void) fprintf(stderr, "sem_flg = %#o\n", sops[j].sem_flg);
}

/* Make the semop() call and report the results. */
if ((j = semop(semid, sops, nsops)) == -1) {
perror("semop: semop failed");
}
else {
(void) fprintf(stderr, "semop: semop returned %d\n", j);
(void) fprintf(stderr, "Parent Process Taking Control of Track: %d/3 times\n", i+1);

sleep(5); /* Do nothing for 5 seconds */

nsops = 1;

/* wait for semaphore to reach zero */
sops[0].sem_num = 0;
sops[0].sem_op = -1; /* Give UP Control of track */
sops[0].sem_flg = SEM_UNDO | IPC_NOWAIT; /* take off semaphore, asynchronous */

if ((j = semop(semid, sops, nsops)) == -1) {
perror("semop: semop failed");
}
else
(void) fprintf(stderr, "Parent Process Giving up Control of Track: %d/3 times\n", i+1);

sleep(5); /* halt process to allow child to catch semaphor change first */
}/* end if Make semop() */

++i;
} /* end while */
}/* end if parent */
}

Τα βασικά σημεία του προγράμματος είναι τα παρακάτω:

Άλλα παραδείγματα

Τα προγράμματα που ακολουθούν μπορούν να χρησιμοποιηθούν για τη διαλογική διερεύνηση των ιδιοτήτων των σηματοφορέων. Ο σηματοφορέας πρέπει να αρχικοποιηθεί με το πρόγραμμα semget.c. Ο έλεγχος γίνεται μέσω του προγράμματος semctl.c ενώ οι λειττουργίες εφαρμόζονται με το πρόγραμμα semop.c.

semget.cΕπίδειξη συνάρτησηςsemget()

/*
* semget.c: Illustrate the semget() function.
*
* This is a simple exerciser of the semget() function. It prompts
* for the arguments, makes the call, and reports the results.
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

extern void exit();
extern void perror();

main()
{
key_t key; /* key to pass to semget() */
int semflg; /* semflg to pass to semget() */
int nsems; /* nsems to pass to semget() */
int semid; /* return value from semget() */

(void) fprintf(stderr, "All numeric input must follow C conventions:\n");
(void) fprintf(stderr, "\t0x... is interpreted as hexadecimal,\n");
(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
(void) fprintf(stderr, "\totherwise, decimal.\n");
(void) fprintf(stderr, "IPC_PRIVATE == %#lx\n", IPC_PRIVATE);

(void) fprintf(stderr, "Enter key: ");
(void) scanf("%li", &key);

(void) fprintf(stderr, "Enter nsems value: ");
(void) scanf("%i", &nsems);

(void) fprintf(stderr, "\nExpected flags for semflg are:\n");
(void) fprintf(stderr, "\tIPC_EXCL = \t%#8.8o\n", IPC_EXCL);
(void) fprintf(stderr, "\tIPC_CREAT = \t%#8.8o\n",IPC_CREAT);
(void) fprintf(stderr, "\towner read = \t%#8.8o\n", 0400);
(void) fprintf(stderr, "\towner alter = \t%#8.8o\n", 0200);
(void) fprintf(stderr, "\tgroup read = \t%#8.8o\n", 040);
(void) fprintf(stderr, "\tgroup alter = \t%#8.8o\n", 020);
(void) fprintf(stderr, "\tother read = \t%#8.8o\n", 04);
(void) fprintf(stderr, "\tother alter = \t%#8.8o\n", 02);
(void) fprintf(stderr, "Enter semflg value: ");

(void) scanf("%i", &semflg);
(void) fprintf(stderr, "\nsemget: Calling semget(%#lx, %%#o)\n",key, nsems, semflg);
if ((semid = semget(key, nsems, semflg)) == -1) {
perror("semget: semget failed");
exit(1);
}
else {
(void) fprintf(stderr, "semget: semget succeeded: semid = %d\n", semid);
exit(0);
}
}

semctl.cΕπίδειξη συνάρτησηςsemctl() 

/*
* semctl.c: Illustrate the semctl() function.
*
* This is a simple exerciser of the semctl() function. It lets you
* perform one control operation on one semaphore set. It gives up
* immediately if any control operation fails, so be careful not to
* set permissions to preclude read permission; you won't be able to
* reset the permissions with this code if you do.
*/

#include <stdio.h>
#include <stdilb.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <time.h>

struct semid_ds semid_ds;

static void do_semctl();
static void do_stat();
extern char *malloc();
extern void exit();
extern void perror();

char warning_message[] = "If you remove read permission for yourself, this program will fail frequently!";

main()
{
union semun arg; /* union to pass to semctl() */
int cmd, /* command to give to semctl() */
i, /* work area */
semid, /* semid to pass to semctl() */
semnum; /* semnum to pass to semctl() */

(void) fprintf(stderr, "All numeric input must follow C conventions:\n");
(void) fprintf(stderr, "\t0x... is interpreted as hexadecimal,\n");
(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
(void) fprintf(stderr, "\totherwise, decimal.\n");

(void) fprintf(stderr, "Enter semid value: ");
(void) scanf("%i", &semid);

(void) fprintf(stderr, "Valid semctl cmd values are:\n");
(void) fprintf(stderr, "\tGETALL = %d\n", GETALL);
(void) fprintf(stderr, "\tGETNCNT = %d\n", GETNCNT);
(void) fprintf(stderr, "\tGETPID = %d\n", GETPID);
(void) fprintf(stderr, "\tGETVAL = %d\n", GETVAL);
(void) fprintf(stderr, "\tGETZCNT = %d\n", GETZCNT);
(void) fprintf(stderr, "\tIPC_RMID = %d\n", IPC_RMID);
(void) fprintf(stderr, "\tIPC_SET = %d\n", IPC_SET);
(void) fprintf(stderr, "\tIPC_STAT = %d\n", IPC_STAT);
(void) fprintf(stderr, "\tSETALL = %d\n", SETALL);
(void) fprintf(stderr, "\tSETVAL = %d\n", SETVAL);

(void) fprintf(stderr, "\nEnter cmd: ");
(void) scanf("%i", &cmd);

/* Do some setup operations needed by multiple commands. */
switch (cmd) {
case GETVAL:
case SETVAL:
case GETNCNT:
case GETZCNT:
/* Get the semaphore number for these commands. */
(void) fprintf(stderr, "\nEnter semnum value: ");
(void) scanf("%i", &semnum);
break;
case GETALL:
case SETALL:
/* Allocate a buffer for the semaphore values. */
(void) fprintf(stderr,
"Get number of semaphores in the set.\n");
arg.buf = &semid_ds;
do_semctl(semid, 0, IPC_STAT, arg);
if (arg.array =
(ushort *)malloc((unsigned)
(semid_ds.sem_nsems * sizeof(ushort)))) {
/* Break out if you got what you needed. */
break;
}

(void) fprintf(stderr, "semctl: unable to allocate space for %d values\n", semid_ds.sem_nsems);
exit(2);
}

/* Get the rest of the arguments needed for the specified command. */
switch (cmd) {
case SETVAL:
/* Set value of one semaphore. */
(void) fprintf(stderr, "\nEnter semaphore value: ");
(void) scanf("%i", &arg.val);
do_semctl(semid, semnum, SETVAL, arg);
/* Fall through to verify the result. */
(void) fprintf(stderr, "Do semctl GETVAL command to verify results.\n");
case GETVAL:
/* Get value of one semaphore. */
arg.val = 0;
do_semctl(semid, semnum, GETVAL, arg);
break;
case GETPID:
/* Get PID of last process to successfully complete a
semctl(SETVAL), semctl(SETALL), or semop() on the
semaphore. */
arg.val = 0;
do_semctl(semid, 0, GETPID, arg);
break;
case GETNCNT:
/* Get number of processes waiting for semaphore value to
increase. */
arg.val = 0;
do_semctl(semid, semnum, GETNCNT, arg);
break;
case GETZCNT:
/* Get number of processes waiting for semaphore value to
become zero. */
arg.val = 0;
do_semctl(semid, semnum, GETZCNT, arg);
break;
case SETALL:
/* Set the values of all semaphores in the set. */
(void) fprintf(stderr, "There are %d semaphores in the set.\n",semid_ds.sem_nsems);
(void) fprintf(stderr, "Enter semaphore values:\n");
for (i = 0; i < semid_ds.sem_nsems; i++) {
(void) fprintf(stderr, "Semaphore %d: ", i);
(void) scanf("%hi", &arg.array[i]);
}
do_semctl(semid, 0, SETALL, arg);
/* Fall through to verify the results. */
(void) fprintf(stderr,
"Do semctl GETALL command to verify results.\n");
case GETALL:
/* Get and print the values of all semaphores in the
set.*/
do_semctl(semid, 0, GETALL, arg);
(void) fprintf(stderr, "The values of the %d semaphores are:\n", semid_ds.sem_nsems);
for (i = 0; i < semid_ds.sem_nsems; i++)
(void) fprintf(stderr, "%d ", arg.array[i]);
(void) fprintf(stderr, "\n");
break;
case IPC_SET:
/* Modify mode and/or ownership. */
arg.buf = &semid_ds;
do_semctl(semid, 0, IPC_STAT, arg);
(void) fprintf(stderr, "Status before IPC_SET:\n");
do_stat();
(void) fprintf(stderr, "Enter sem_perm.uid value: ");
(void) scanf("%hi", &semid_ds.sem_perm.uid);
(void) fprintf(stderr, "Enter sem_perm.gid value: ");
(void) scanf("%hi", &semid_ds.sem_perm.gid);
(void) fprintf(stderr, "%s\n", warning_message);
(void) fprintf(stderr, "Enter sem_perm.mode value: ");
(void) scanf("%hi", &semid_ds.sem_perm.mode);
do_semctl(semid, 0, IPC_SET, arg);
/* Fall through to verify changes. */
(void) fprintf(stderr, "Status after IPC_SET:\n");
case IPC_STAT:
/* Get and print current status. */
arg.buf = &semid_ds;
do_semctl(semid, 0, IPC_STAT, arg);
do_stat();
break;
case IPC_RMID:
/* Remove the semaphore set. */
arg.val = 0;
do_semctl(semid, 0, IPC_RMID, arg);
break;
default:
/* Pass unknown command to semctl. */
arg.val = 0;
do_semctl(semid, 0, cmd, arg);
break;
}
exit(0);
}

/*
* Print indication of arguments being passed to semctl(), call
* semctl(), and report the results. If semctl() fails, do not
* return; this example doesn't deal with errors, it just reports
* them.
*/
static void
do_semctl(semid, semnum, cmd, arg)
union semun arg;
int cmd,
semid,
semnum;
{
register int i; /* work area */

void) fprintf(stderr, "\nsemctl: Calling semctl(%d, %d, %d, ",semid, semnum, cmd);
switch (cmd) {
case GETALL:
(void) fprintf(stderr, "arg.array = %#x)\n",
arg.array);
break;
case IPC_STAT:
case IPC_SET:
(void) fprintf(stderr, "arg.buf = %#x)\n", arg.buf);
break;
case SETALL:
(void) fprintf(stderr, "arg.array = [", arg.buf);
for (i = 0;i < semid_ds.sem_nsems;) {
(void) fprintf(stderr, "%d", arg.array[i++]);
if (i < semid_ds.sem_nsems)
(void) fprintf(stderr, ", ");
}
(void) fprintf(stderr, "])\n");
break;
case SETVAL:
default:
(void) fprintf(stderr, "arg.val = %d)\n", arg.val);
break;
}
i = semctl(semid, semnum, cmd, arg);
if (i == -1) {
perror("semctl: semctl failed");
exit(1);
}
(void) fprintf(stderr, "semctl: semctl returned %d\n", i);
return;
}

/*
* Display contents of commonly used pieces of the status structure.
*/
static void
do_stat()
{
(void) fprintf(stderr, "sem_perm.uid = %d\n", semid_ds.sem_perm.uid);
(void) fprintf(stderr, "sem_perm.gid = %d\n", semid_ds.sem_perm.gid);
(void) fprintf(stderr, "sem_perm.cuid = %d\n", semid_ds.sem_perm.cuid);
(void) fprintf(stderr, "sem_perm.cgid = %d\n", semid_ds.sem_perm.cgid);
(void) fprintf(stderr, "sem_perm.mode = %#o, ", semid_ds.sem_perm.mode);
(void) fprintf(stderr, "access permissions = %#o\n", semid_ds.sem_perm.mode & 0777);
(void) fprintf(stderr, "sem_nsems = %d\n", semid_ds.sem_nsems);
(void) fprintf(stderr, "sem_otime = %s", semid_ds.sem_otime ? ctime(&semid_ds.sem_otime) : "Not Set\n");
(void) fprintf(stderr, "sem_ctime = %s", ctime(&semid_ds.sem_ctime));
}

semop.c: Επίδειξη συνάρτησηςsemop()

/*
* semop.c: Illustrate the semop() function.
*
* This is a simple exerciser of the semop() function. It lets you
* to set up arguments for semop() and make the call. It then reports
* the results repeatedly on one semaphore set. You must have read
* permission on the semaphore set or this exerciser will fail.(It
* needs read permission to get the number of semaphores in the set
* and to report the values before and after calls to semop().)
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

static int ask();
extern void exit();
extern void free();
extern char *malloc();
extern void perror();

static struct semid_ds semid_ds; /* status of semaphore set */

static char error_mesg1[] = "semop: Can't allocate space for %d semaphore values. Giving up.\n";
static char error_mesg2[] = "semop: Can't allocate space for %d sembuf structures. Giving up.\n";

main()
{
register int i; /* work area */
int nsops; /* number of operations to do */
int semid; /* semid of semaphore set */
struct sembuf *sops; /* ptr to operations to perform */

(void) fprintf(stderr, "All numeric input must follow C conventions:\n");
(void) fprintf(stderr, "\t0x... is interpreted as hexadecimal,\n");
(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
(void) fprintf(stderr, "\totherwise, decimal.\n");
/* Loop until the invoker doesn't want to do anymore. */
while (nsops = ask(&semid, &sops)) {
/* Initialize the array of operations to be performed.*/
for (i = 0; i < nsops; i++) {
(void) fprintf(stderr, "\nEnter values for operation %d of %d.\n", i + 1, nsops);
(void) fprintf(stderr, "sem_num(valid values are 0 <= sem_num < %d): ", semid_ds.sem_nsems);
(void) scanf("%hi", &sops[i].sem_num);
(void) fprintf(stderr, "sem_op: ");
(void) scanf("%hi", &sops[i].sem_op);
(void) fprintf(stderr, "Expected flags in sem_flg are:\n");
(void) fprintf(stderr, "\tIPC_NOWAIT =\t%#6.6o\n", IPC_NOWAIT);
(void) fprintf(stderr, "\tSEM_UNDO =\t%#6.6o\n", SEM_UNDO);
(void) fprintf(stderr, "sem_flg: ");
(void) scanf("%hi", &sops[i].sem_flg);
}

/* Recap the call to be made. */
(void) fprintf(stderr, "\nsemop: Calling semop(%d, &sops, %d) with:", semid, nsops);
for (i = 0; i < nsops; i++)
{
(void) fprintf(stderr, "\nsops[%d].sem_num = %d, ", i, sops[i].sem_num);
(void) fprintf(stderr, "sem_op = %d, ", sops[i].sem_op);
(void) fprintf(stderr, "sem_flg = %#o\n", sops[i].sem_flg);
}

/* Make the semop() call and report the results. */
if ((i = semop(semid, sops, nsops)) == -1) {
perror("semop: semop failed");
}
else {
(void) fprintf(stderr, "semop: semop returned %d\n", i);
}
}
}

/*
* Ask if user wants to continue.
*
* On the first call:
* Get the semid to be processed and supply it to the caller.
* On each call:
* 1. Print current semaphore values.
* 2. Ask user how many operations are to be performed on the next
* call to semop. Allocate an array of sembuf structures
* sufficient for the job and set caller-supplied pointer to that
* array. (The array is reused on subsequent calls if it is big
* enough. If it isn't, it is freed and a larger array is
* allocated.)
*/
static
ask(semidp, sopsp)
int *semidp; /* pointer to semid (used only the first time) */
struct sembuf **sopsp;
{
static union semun arg; /* argument to semctl */
int i; /* work area */
static int nsops = 0; /* size of currently allocated sembuf array */
static int semid = -1; /* semid supplied by user */
static struct sembuf *sops; /* pointer to allocated array */

if (semid < 0) {
/* First call; get semid from user and the current state of the semaphore set. */
(void) fprintf(stderr, "Enter semid of the semaphore set you want to use: ");
(void) scanf("%i", &semid);
*semidp = semid;
arg.buf = &semid_ds;
if (semctl(semid, 0, IPC_STAT, arg) == -1) {
perror("semop: semctl(IPC_STAT) failed");
/* Note that if semctl fails, semid_ds remains filled
with zeros, so later test for number of semaphores will
be zero. */
(void) fprintf(stderr, "Before and after values are not printed.\n");
}
else {
if ((arg.array = (ushort *)malloc((unsigned)(sizeof(ushort) * semid_ds.sem_nsems))) == NULL) {
(void) fprintf(stderr, error_mesg1, semid_ds.sem_nsems);
exit(1);
}
}
}
/* Print current semaphore values. */
if (semid_ds.sem_nsems) {
(void) fprintf(stderr, "There are %d semaphores in the set.\n", semid_ds.sem_nsems);
if (semctl(semid, 0, GETALL, arg) == -1) {
perror("semop: semctl(GETALL) failed");
}
else {
(void) fprintf(stderr, "Current semaphore values are:");
for (i = 0; i < semid_ds.sem_nsems;
(void) fprintf(stderr, " %d", arg.array[i++]));
(void) fprintf(stderr, "\n");
}
}
/* Find out how many operations are going to be done in the next
call and allocate enough space to do it. */
(void) fprintf(stderr, "How many semaphore operations do you want %s\n", "on the next call to semop()?");
(void) fprintf(stderr, "Enter 0 or control-D to quit: ");
i = 0;
if (scanf("%i", &i) == EOF || i == 0)
exit(0);
if (i > nsops) {
if (nsops)
free((char *)sops);
nsops = i;
if ((sops = (struct sembuf *)malloc((unsigned)(nsops * sizeof(struct sembuf)))) == NULL) {(void) fprintf(stderr, error_mesg2, nsops);
exit(2);
}
}
*sopsp = sops;
return (i);
}

Exercises

Άσκηση 12763

Γράψτε 2 προγράμματα που όταν εκτελούνται 'ταυτόχρονα' να μπορούν να επικοινωνούν αμφίδρομα (μέσω ανάγνωσης και εγγραφής σε κοινή μνήμη) με τη χρήση σηματοφορέων. Μπορείτε να ξεκινήσετε από ένα πρόγραμμα όπως το semaphore.c αντικαθιστώντας τις συναρτήσεις sleep() με λειτουργίες ανάγνωσης ή εγγραφής. Στη συνέχεια μπορείτε να δημιουργήσετε δύο παρόμοια προγράμματα που 'γνωρίζουν' τον ίδιο σηματοφορέα.

Άσκηση 12764

Τροποποιείστε το πρόγραμμα semaphore.c ώστε να διαχειρίζεται σύγχρονη επικοινωνία σηματοφορέων.

Άσκηση 12765

Γράψτε 3 προγράμματα που να επικοινωνούν μέσω σηματοφορέων ως εξής: ο διακομιστής sem_server.c επικοινωνεί ανεξάρτητα (με διαφορετικούς σηματοφορείς, semaphore tracks) με δύο πελάτες, sem_client1.c. και sem_client2.c.

Άσκηση 12766

Μεταγλωττίστε τα προγράμματα semget.c, semctl.c και semop.c. Στη συνέχεια


Dave Marshall
1/5/1999
μετάφραση και προσαρμογή στα Ελληνικά Κ.Γ. Μαργαρίτης
5/4/2008