| Team-FLY |
15.2 POSIX:XSI Semaphore SetsA POSIX:XSI semaphore consists of an array of semaphore elements . The semaphore elements are similar, but not identical, to the classical integer semaphores proposed by Dijsktra, as described in Chapter 14. A process can perform operations on the entire set in a single call. Thus, POSIX:XSI semaphores are capable of AND synchronization, as described in Section 14.2. We refer to POSIX:XSI semaphores as semaphore sets to distinguish them from the POSIX:SEM semaphores described in Chapter 14. Each semaphore element includes at least the following information.
The major data structure for semaphores is
semid_ds
, which is defined in
sys/sem.h
and has the following
struct ipc_perm sem_perm; /* operation permission structure */ unsigned short sem_nsems; /* number of semaphores in the set */ time_t sem_otime; /* time of last semop */ time_t sem_ctime; /* time of last semctl */
Each semaphore element has two queues associated with it ”a queue of processes waiting for the value to equal 0 and a queue of processes waiting for the value to increase. The semaphore element operations allow a process to block until a semaphore element value is 0 or until it
15.2.1 Semaphore creation
The
semget
function returns the semaphore identifier associated with the
key
parameter. The
semget
function creates the identifier and its associated semaphore set if either the key is
IPC_PRIVATE
or
semflg & IPC_CREAT
is nonzero and no semaphore set or identifier is already associated with
key
. The
nsems
parameter specifies the number of semaphore elements in the set. The individual semaphore elements within a semaphore set are referenced by the integers
through
nsems - 1
. Semaphores have permissions specified by the
semflg
argument of
semget
. Set permission values in the same way as described in Section 4.3 for files, and change the permissions by calling
semctl
. Semaphore elements should be
SYNOPSIS #include <sys/sem.h> int semget(key_t key, int nsems, int semflg); POSIX:XSI
If successful,
semget
returns a nonnegative integer corresponding to the semaphore identifier. If
If a process attempts to create a semaphore that already exists, it receives a handle to the existing semaphore unless the semflg value includes both IPC_CREAT and IPC_EXCL . In the latter case, semget fails and sets errno equal to EEXIST . Example 15.3The following code segment creates a new semaphore set containing three semaphore elements.
#define PERMS (S_IRUSR S_IWUSR)
int semid;
if ((semid = semget(IPC_PRIVATE, 3, PERMS)) == -1)
perror("Failed to create new private semaphore");
This semaphore can only be read or written by the owner.
The
IPC_PRIVATE
key
Example 15.4
The following code segment
#define PERMS (S_IRUSR S_IWUSR S_IRGRP S_IWGRP S_IROTH S_IWOTH)
#define KEY ((key_t)99887)
int semid;
if ((semid = semget(KEY, 1, PERMS IPC_CREAT)) == -1)
perror ("Failed to access semaphore with key 99887");
The IPC_CREAT flag ensures that if the semaphore set doesn't exist, semget creates it. The permissions allow all users to access the semaphore set. Giving a specific key value allows cooperating processes to agree on a common semaphore set. If the semaphore already exists, semget returns a handle to the existing semaphore. If you replace the semflg argument of semget with PERMS IPC_CREAT IPC_EXCL, semget returns an error when the semaphore already exists.
Program 15.1
Program 15.1 semfrompath.cA program that creates a semaphore from a pathname key .
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sem.h>
#include <sys/stat.h>
#define PERMS (S_IRUSR S_IWUSR S_IRGRP S_IWGRP S_IROTH S_IWOTH)
#define SET_SIZE 2
int main(int argc, char *argv[]) {
key_t mykey;
int semid;
if (argc != 3) {
fprintf(stderr, "Usage: %s pathname id\n", argv[0]);
return 1;
}
if ((mykey = ftok(argv[1], atoi(argv[2]))) == (key_t)-1) {
fprintf(stderr, "Failed to derive key from filename %s:%s\n",
argv[1], strerror(errno));
return 1;
}
if ((semid = semget(mykey, SET_SIZE, PERMS IPC_CREAT)) == -1) {
fprintf(stderr, "Failed to create semaphore with key %d:%s\n",
(int)mykey, strerror(errno));
return 1;
}
printf("semid = %d\n", semid);
return 0;
}
15.2.2 Semaphore controlEach element of a semaphore set must be initialized with semctl before it is used. The semctl function provides control operations in element semnum for the semaphore set semid . The cmd parameter specifies the type of operation. The optional fourth parameter, arg , depends on the value of cmd . SYNOPSIS #include <sys/sem.h> int semctl(int semid, int semnum, int cmd, ...); POSIX:XSI If successful, semctl returns a nonnegative value whose interpretation depends on cmd . The GETVAL , GETPID , GETNCNT and GETZCNT values of cmd cause semctl to return the value associated with cmd . All other values of cmd cause semctl to return 0 if successful. If unsuccessful, semctl returns “1 and sets errno . The following table lists the mandatory errors for semctl .
Table 15.2 gives the POSIX:XSI values for the cmd parameter of semctl . Table 15.2. POSIX:XSI values for the cmd parameter of semctl .
Several of these commands, such as
GETALL
and
SETALL
, require an
arg
parameter to read or store results. The
arg
parameter is of type
union semun
, which must be defined in programs that use it, as
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
Example 15.5 initelement.cThe initelement function sets the value of the specified semaphore element to semvalue .
#include <sys/sem.h>
int initelement(int semid, int semnum, int semvalue) {
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
arg.val = semvalue;
return semctl(semid, semnum, SETVAL, arg);
}
The semid and semnum parameters identify the semaphore set and the element within the set whose value is to be set to semvalue . If successful, initelement returns 0. If unsuccessful, initelement returns “1 with errno set (since semctl sets errno ). Example 15.6 removesem.cThe removesem function deletes the semaphore specified by semid .
#include <sys/sem.h>
int removesem(int semid) {
return semctl(semid, 0, IPC_RMID);
}
If successful, removesem returns 0. If unsuccessful, removesem returns “1 with errno set (since semctl sets errno ). 15.2.3 POSIX semaphore set operations
The
semop
function atomically
SYNOPSIS #include <sys/sem.h> int semop(int semid, struct sembuf *sops, size_t nsops); POSIX:XSI If successful, semop returns 0. If unsuccessful, semop returns “1 and sets errno . The following table lists the mandatory errors for semop .
The semop function performs all the operations specified in sops array atomically on a single semaphore set. If any of the individual element operations would cause the process to block, the process blocks and none of the operations are performed. The struct sembuf structure, which specifies a semaphore element operation, includes the following members.
The sem_op element operations are values specifying the amount by which the semaphore value is to be changed.
The description of
semop
assumes that
sem_flg
is 0 for all the element operations. If
sem_flg & IPC_NOWAIT
is true, the element operation never causes the
semop
call to block. If a
semop
returns because it would have blocked on that element operation, it returns “1 with
errno
set to
EAGAIN
. If
sem_flg & SEM_UNDO
is true, the function also modifies the semaphore adjustment value for the process. This adjustment value allows the process to
undo
its effect on the semaphore when it exits. You should read the man page
Example 15.7What is wrong with the following code to declare myopbuf and initialize it so that sem_num is 1, sem_op is 1, and sem_flg is 0?
struct sembuf myopbuf = {1, -1, 0};
Answer: The direct assignment assumes that the members of struct sembuf appear in the order sem_num, sem_op and sem_flg . You may see this type of initialization in legacy code and it may work on your system, but try to avoid it. Although the POSIX:XSI Extension specifies that the struct sembuf structure has sem_num, sem_op and sem_flg members, the standard does not specify the order in which these members appear in the definition nor does the standard restrict struct sembuf to contain only these members. Example 15.8 setsembuf.c
The function
setsembuf
initializes the
struct sembuf
structure members
sem_num, sem_op
and
sem_flg
in an
#include <sys/sem.h>
void setsembuf(struct sembuf *s, int num, int op, int flg) {
s->sem_num = (short)num;
s->sem_op = (short)op;
s->sem_flg = (short)flg;
return;
}
Example 15.9The following code segment atomically increments element zero of semid by 1 and element one of semid by 2, using setsembuf of Example 15.8.
struct sembuf myop[2];
setsembuf(myop, 0, 1, 0);
setsembuf(myop + 1, 1, 2, 0);
if (semop(semid, myop, 2) == -1)
perror("Failed to perform semaphore operation");
Example 15.10
Suppose a two-element semaphore set,
S
, represents a tape drive system in which Process 1 uses Tape A, Process 2 uses Tape A and B, and Process 3 uses Tape B. The following pseudocode segment defines semaphore operations that allow the processes to access one or both tape
struct sembuf get_tapes[2];
struct sembuf release_tapes[2];
setsembuf(&(get_tapes[0]), 0, -1, 0);
setsembuf(&(get_tapes[1]), 1, -1, 0);
setsembuf(&(release_tapes[0]), 0, 1, 0);
setsembuf(&(release_tapes[1]), 1, 1, 0);
Process 1: semop(S, get_tapes, 1);
<use tape A>
semop(S, release_tapes, 1);
Process 2: semop(S, get_tapes, 2);
<use tapes A and B>
semop(S, release_tapes, 2);
Process 3: semop(S, get_tapes + 1, 1);
<use tape B>
semop(S, release_tapes + 1, 1);
S[0] represents tape A, and S[1] represents tape B. We assume that both elements of S have been initialized to 1. If semop is interrupted by a signal, it returns “1 and sets errno to EINTR . Program 15.2 shows a function that restarts semop if it is interrupted by a signal. Program 15.2 r_semop.cA function that restarts semop after a signal .
#include <errno.h>
#include <sys/sem.h>
int r_semop(int semid, struct sembuf *sops, int nsops) {
while (semop(semid, sops, nsops) == -1)
if (errno != EINTR)
return -1;
return 0;
}
Program 15.3 modifies Program 14.1 to use POSIX:XSI semaphore sets to protect a critical section. Program 15.3 calls setsembuf (Example 15.8) and removesem (Example 15.6). It restarts semop operations if interrupted by a signal, even though the program does not catch any signals. You should get into the habit of restarting functions that can set errno equal to EINTR .
Once the semaphore of Program 15.3 is created, it persists until it is removed. If a child process generates an error, it just exits. If the parent generates an error, it
Program 15.3 chainsemset.cA modification of Program 14.1 that uses semaphore sets to protect the critical section .
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "restart.h"
#define BUFSIZE 1024
#define PERMS (S_IRUSR S_IWUSR)
int initelement(int semid, int semnum, int semvalue);
int r_semop(int semid, struct sembuf *sops, int nsops);
int removesem(int semid);
void setsembuf(struct sembuf *s, int num, int op, int flg);
void printerror(char *msg, int error) {
fprintf(stderr, "[%ld] %s: %s\n", (long)getpid(), msg, strerror(error));
}
int main (int argc, char *argv[]) {
char buffer[MAX_CANON];
char *c;
pid_t childpid;
int delay;
int error;
int i, j, n;
int semid;
struct sembuf semsignal[1];
struct sembuf semwait[1];
if ((argc != 3) ((n = atoi(argv[1])) <= 0)
((delay = atoi(argv[2])) < 0)) {
fprintf (stderr, "Usage: %s processes delay\n", argv[0]);
return 1;
}
/* create a semaphore containing a single element */
if ((semid = semget(IPC_PRIVATE, 1, PERMS)) == -1) {
perror("Failed to create a private semaphore");
return 1;
}
setsembuf(semwait, 0, -1, 0); /* decrement element 0 */
setsembuf(semsignal, 0, 1, 0); /* increment element 0 */
if (initelement(semid, 0, 1) == -1) {
perror("Failed to initialize semaphore element to 1");
if (removesem(semid) == -1)
perror("Failed to remove failed semaphore");
return 1;
}
for (i = 1; i < n; i++)
if (childpid = fork())
break;
snprintf(buffer, BUFSIZE, "i:%d PID:%ld parent PID:%ld child PID:%ld\n",
i, (long)getpid(), (long)getppid(), (long)childpid);
c = buffer;
/******************** entry section ************************************/
if (((error = r_semop(semid, semwait, 1)) == -1) && (i > 1)) {
printerror("Child failed to lock semid", error);
return 1;
}
else if (!error) {
/***************** start of critical section ************************/
while (*c != '
A program calls
semget
to create or access a semaphore set and calls
semctl
to initialize it. If one process creates and initializes a semaphore and another process calls
semop
between the creation and initialization, the results of the execution are unpredictable. This
Program 15.4 can be used to create or access a semaphore set containing a single semaphore element. It takes three parameters, a semaphore key, an initial value and a pointer to a variable of type sig_atomic_t that is initialized to 0 and shared among all processes and threads that call this function. If this function is used among threads of a single process, the sig_atomic_t variable could be defined outside a block and statically initialized. Using initsemset among processes requires shared memory. We use Program 15.4 later in the chapter to protect a shared memory segment. The busy-waiting used in initsemset is not as inefficient as it may seem, since it is only used when the thread that creates the semaphore set loses the CPU before it can initialize it. Program 15.4 initsemset.cA function that creates and initializes a semaphore set containing a single semaphore .
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <sys/sem.h>
#include <sys/stat.h>
#define PERMS (S_IRUSR S_IWUSR)
#define TEN_MILLION 10000000L
int initelement(int semid, int semnum, int semvalue);
int initsemset(key_t mykey, int value, sig_atomic_t *readyp) {
int semid;
struct timespec sleeptime;
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = TEN_MILLION;
semid = semget(mykey, 2, PERMS IPC_CREAT IPC_EXCL);
if ((semid == -1) && (errno != EEXIST)) /* real error, so return */
return -1;
if (semid >= 0) { /* we created the semaphore, so initialize it */
if (initelement(semid, 0, value) == -1)
return -1;
*readyp = 1;
return semid;
}
if ((semid = semget(mykey, 2, PERMS)) == -1) /* just access it */
return -1;
while (*readyp == 0) /* wait for initialization */
nanosleep(&sleeptime, NULL);
return semid;
}
|
| Team-FLY |